Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

Building WPF Applications with Self-Tracking Entity Generator - Project Setup

, 20 Feb 2012 CPOL
This article describes the project setup of building a WPF sample application with Self-Tracking Entity Generator for WPF/Silverlight.
SchoolSample_20111201.zip
SchoolSample
Local.testsettings
SchoolSample.vsmdi
TraceAndTestImpact.testsettings
Assemblies
GalaSoft.MvvmLight.Extras.WPF4.dll
GalaSoft.MvvmLight.WPF4.dll
Moq.dll
System.Windows.Interactivity.dll
WPFToolkit.Extended.dll
ComponentModel.Composition.Initialization.Desktop
Microsoft
ComponentModel
Composition
Hosting
Internal
Properties
System
ComponentModel
Composition
Hosting
System.Windows.Controls.Data.Input
Common
Properties
Settings.settings
themes
Validation
Database
SchoolSample
SchoolSample.csproj.user
Asset
Control
Properties
Settings.settings
View
SchoolSample.Common
Model
Properties
Resource
SchoolSample.Data
EntityModel
Properties
Validation
SchoolSample.Data.Wcf
EntityModel
SchoolModel.edmx
Properties
Validation
SchoolSample.Model
Properties
SchoolSample.ViewModel
Properties
SchoolSample.Wcf
SchoolSample.Wcf.csproj.user
Properties
Service
SchoolService.svc
SchoolSample.WCFService
SchoolSample.WCFService.csproj.user
Properties
Service References
SchoolService
configuration.svcinfo
configuration91.svcinfo
Reference.svcmap
SchoolService.disco
SchoolService.wsdl
Test.SchoolSample.Model
Properties
Test References
SchoolSample.Model.accessor
Test.SchoolSample.ViewModel
Properties
Test References
SchoolSample.ViewModel.accessor
SchoolSample_20120117.zip
Local.testsettings
SchoolSample.vsmdi
TraceAndTestImpact.testsettings
GalaSoft.MvvmLight.Extras.WPF4.dll
GalaSoft.MvvmLight.WPF4.dll
Moq.dll
System.Windows.Interactivity.dll
WPFToolkit.Extended.dll
Settings.settings
SchoolSample.csproj.user
Settings.settings
SchoolModel.edmx
SchoolSample.Wcf.csproj.user
SchoolService.svc
SchoolSample.WCFService.csproj.user
configuration.svcinfo
configuration91.svcinfo
Reference.svcmap
SchoolService.disco
SchoolService.wsdl
SchoolSample.Model.accessor
SchoolSample.ViewModel.accessor
SchoolSample_20120216.zip
Local.testsettings
SchoolSample.vsmdi
TraceAndTestImpact.testsettings
GalaSoft.MvvmLight.Extras.WPF4.dll
GalaSoft.MvvmLight.WPF4.dll
Moq.dll
System.Windows.Interactivity.dll
WPFToolkit.Extended.dll
Settings.settings
SchoolSample.csproj.user
Settings.settings
SchoolModel.edmx
SchoolSample.Wcf.csproj.user
SchoolService.svc
SchoolSample.WCFService.csproj.user
configuration.svcinfo
configuration91.svcinfo
Reference.svcmap
SchoolService.disco
SchoolService.wsdl
SchoolSample.Model.accessor
SchoolSample.ViewModel.accessor
using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Linq;
using SchoolSample.Common;
using SchoolSample.EntityModel;
using SchoolSample.WCFService.SchoolService;

namespace SchoolSample.Model
{
    [Export(typeof(ISchoolModel))]
    [PartCreationPolicy(CreationPolicy.Shared)]
    public class SchoolModel : ISchoolModel
    {
        #region "Private Data"
        private ISchoolServiceClient _proxy;
        private ActionQueue _actionQueue;
        private Exception _lastError;
        #endregion "Private Data"

        #region "Constructor"
        public SchoolModel()
            : this(SchoolServiceClient.Instance)
        {
        }

        public SchoolModel(ISchoolServiceClient schoolServiceClient)
        {
            _proxy = schoolServiceClient;
            // Set up event handling
            _proxy.PropertyChanged += _proxy_PropertyChanged;

            _actionQueue = new ActionQueue();
        }
        #endregion "Constructor"

        #region "ISchoolModel Interface (Data Retrieval) implementation"

        public void GetStudentsAsync(string includeOption, string screenName)
        {
            _proxy.BeginGetStudents(includeOption, BeginGetStudentsComplete, screenName);
            _proxy.IncrementCallCount();
        }

        public void GetStudentByIdAsync(int studentId)
        {
            _proxy.BeginGetStudentById(studentId, BeginGetStudentByIdComplete, null);
            _proxy.IncrementCallCount();
        }

        public void GetInstructorsAsync(string includeOption, string screenName)
        {
            _proxy.BeginGetInstructors(includeOption, BeginGetInstructorsComplete, screenName);
            _proxy.IncrementCallCount();
        }

        public void GetInstructorByIdAsync(int instructorId)
        {
            _proxy.BeginGetInstructorById(instructorId, BeginGetInstructorByIdComplete, null);
            _proxy.IncrementCallCount();
        }

        public void GetCoursesAsync(string screenName)
        {
            _proxy.BeginGetCourses(BeginGetCoursesComplete, screenName);
            _proxy.IncrementCallCount();
        }

        public void GetCourseByIdAsync(int courseId)
        {
            _proxy.BeginGetCourseById(courseId, BeginGetCourseByIdComplete, null);
            _proxy.IncrementCallCount();
        }

        public event EventHandler<ResultsArgs<Student>> GetStudentsCompleted;
        public event EventHandler<ResultArgs<Student>> GetStudentByIdCompleted;
        public event EventHandler<ResultsArgs<Instructor>> GetInstructorsCompleted;
        public event EventHandler<ResultArgs<Instructor>> GetInstructorByIdCompleted;
        public event EventHandler<ResultsArgs<Course>> GetCoursesCompleted;
        public event EventHandler<ResultArgs<Course>> GetCourseByIdCompleted;

        #endregion "ISchoolModel Interface (Data Retrieval) implementation"

        #region "ISchoolModel Interface (Data Cache) implementation"

        public ObservableCollection<Student> StudentsList
        {
            get { return _studentsList; }
            set
            {
                if (!ReferenceEquals(_studentsList, value))
                {
                    if (_studentsList != null)
                    {
                        _studentsList.CollectionChanged -= _studentsList_CollectionChanged;
                        foreach (var student in _studentsList)
                        {
                            ((INotifyPropertyChanged)student).PropertyChanged -= EntityModel_PropertyChanged;
                        }
                    }
                    _studentsList = value;
                    if (_studentsList != null)
                    {
                        _studentsList.CollectionChanged += _studentsList_CollectionChanged;
                        foreach (var student in _studentsList)
                        {
                            ((INotifyPropertyChanged)student).PropertyChanged += EntityModel_PropertyChanged;
                        }
                    }
                    ReCalculateStudentsListHasChanges();
                }
            }
        }

        private ObservableCollection<Student> _studentsList;

        public Student CurrentStudent
        {
            get { return _currentStudent; }
            set
            {
                if (!ReferenceEquals(_currentStudent, value))
                {
                    if (_currentStudent != null)
                    {
                        ((INotifyPropertyChanged)_currentStudent).PropertyChanged -= EntityModel_PropertyChanged;
                    }
                    _currentStudent = value;
                    if (_currentStudent != null)
                    {
                        ((INotifyPropertyChanged)_currentStudent).PropertyChanged += EntityModel_PropertyChanged;
                    }
                    ReCalculateCurrentStudentHasChanges();
                }
            }
        }

        private Student _currentStudent;

        public ObservableCollection<Instructor> InstructorsList
        {
            get { return _instructorsList; }
            set
            {
                if (!ReferenceEquals(_instructorsList, value))
                {
                    if (_instructorsList != null)
                    {
                        _instructorsList.CollectionChanged -= _instructorsList_CollectionChanged;
                        foreach (var instructor in _instructorsList)
                        {
                            ((INotifyPropertyChanged)instructor).PropertyChanged -= EntityModel_PropertyChanged;
                        }
                    }
                    _instructorsList = value;
                    if (_instructorsList != null)
                    {
                        _instructorsList.CollectionChanged += _instructorsList_CollectionChanged;
                        foreach (var instructor in _instructorsList)
                        {
                            ((INotifyPropertyChanged)instructor).PropertyChanged += EntityModel_PropertyChanged;
                        }
                    }
                    ReCalculateInstructorsListHasChanges();
                }
            }
        }

        private ObservableCollection<Instructor> _instructorsList;

        public Instructor CurrentInstructor
        {
            get { return _currentInstructor; }
            set
            {
                if (!ReferenceEquals(_currentInstructor, value))
                {
                    if (_currentInstructor != null)
                    {
                        ((INotifyPropertyChanged)_currentInstructor).PropertyChanged -= EntityModel_PropertyChanged;
                    }
                    _currentInstructor = value;
                    if (_currentInstructor != null)
                    {
                        ((INotifyPropertyChanged)_currentInstructor).PropertyChanged += EntityModel_PropertyChanged;
                    }
                    ReCalculateCurrentInstructorHasChanges();
                }
            }
        }

        private Instructor _currentInstructor;

        public ObservableCollection<Course> CoursesList
        {
            get { return _coursesList; }
            set
            {
                if (!ReferenceEquals(_coursesList, value))
                {
                    if (_coursesList != null)
                    {
                        _coursesList.CollectionChanged -= _coursesList_CollectionChanged;
                        foreach (var course in _coursesList)
                        {
                            ((INotifyPropertyChanged)course).PropertyChanged -= EntityModel_PropertyChanged;
                            if (course.Enrollments != null)
                            {
                                course.Enrollments.CollectionChanged -= Enrollments_CollectionChanged;
                                foreach (var enrollment in course.Enrollments)
                                {
                                    ((INotifyPropertyChanged)enrollment).PropertyChanged -= EntityModel_PropertyChanged;
                                }
                            }
                        }
                    }
                    _coursesList = value;
                    if (_coursesList != null)
                    {
                        _coursesList.CollectionChanged += _coursesList_CollectionChanged;
                        foreach (var course in _coursesList)
                        {
                            ((INotifyPropertyChanged)course).PropertyChanged += EntityModel_PropertyChanged;
                            if (course.Enrollments != null)
                            {
                                course.Enrollments.CollectionChanged += Enrollments_CollectionChanged;
                                foreach (var enrollment in course.Enrollments)
                                {
                                    ((INotifyPropertyChanged)enrollment).PropertyChanged += EntityModel_PropertyChanged;
                                }
                            }
                        }
                    }
                    ReCalculateCoursesListHasChanges();
                }
            }
        }

        private ObservableCollection<Course> _coursesList;

        public Course CurrentCourse
        {
            get { return _currentCourse; }
            set
            {
                if (!ReferenceEquals(_currentCourse, value))
                {
                    if (_currentCourse != null)
                    {
                        ((INotifyPropertyChanged)_currentCourse).PropertyChanged -= EntityModel_PropertyChanged;
                        if (_currentCourse.Enrollments != null)
                        {
                            _currentCourse.Enrollments.CollectionChanged -= Enrollments_CollectionChanged;
                            foreach (var enrollment in _currentCourse.Enrollments)
                            {
                                ((INotifyPropertyChanged)enrollment).PropertyChanged -= EntityModel_PropertyChanged;
                            }
                        }
                    }
                    _currentCourse = value;
                    if (_currentCourse != null)
                    {
                        ((INotifyPropertyChanged)_currentCourse).PropertyChanged += EntityModel_PropertyChanged;
                        if (_currentCourse.Enrollments != null)
                        {
                            _currentCourse.Enrollments.CollectionChanged += Enrollments_CollectionChanged;
                            foreach (var enrollment in _currentCourse.Enrollments)
                            {
                                ((INotifyPropertyChanged)enrollment).PropertyChanged += EntityModel_PropertyChanged;
                            }
                        }
                    }
                    ReCalculateCurrentCourseHasChanges();
                }
            }
        }

        private Course _currentCourse;

        #endregion "ISchoolModel Interface (Data Cache) implementation"

        #region "ISchoolModel Interface (Data Status) implementation"

        public bool StudentsListHasChanges
        {
            get { return _studentsListHasChanges; }
            private set
            {
                if (_studentsListHasChanges != value)
                {
                    _studentsListHasChanges = value;
                    OnPropertyChanged("StudentsListHasChanges");
                }
            }
        }

        private bool _studentsListHasChanges;

        public bool CurrentStudentHasChanges
        {
            get { return _currentStudentHasChanges; }
            private set
            {
                if (_currentStudentHasChanges != value)
                {
                    _currentStudentHasChanges = value;
                    OnPropertyChanged("CurrentStudentHasChanges");
                }
            }
        }

        private bool _currentStudentHasChanges;

        public bool InstructorsListHasChanges
        {
            get { return _instructorsListHasChanges; }
            private set
            {
                if (_instructorsListHasChanges != value)
                {
                    _instructorsListHasChanges = value;
                    OnPropertyChanged("InstructorsListHasChanges");
                }
            }
        }

        private bool _instructorsListHasChanges;

        public bool CurrentInstructorHasChanges
        {
            get { return _currentInstructorHasChanges; }
            private set
            {
                if (_currentInstructorHasChanges != value)
                {
                    _currentInstructorHasChanges = value;
                    OnPropertyChanged("CurrentInstructorHasChanges");
                }
            }
        }

        private bool _currentInstructorHasChanges;

        public bool CoursesListHasChanges
        {
            get { return _coursesListHasChanges; }
            private set
            {
                if (_coursesListHasChanges != value)
                {
                    _coursesListHasChanges = value;
                    OnPropertyChanged("CoursesListHasChanges");
                }
            }
        }

        private bool _coursesListHasChanges;

        public bool CurrentCourseHasChanges
        {
            get { return _currentCourseHasChanges; }
            private set
            {
                if (_currentCourseHasChanges != value)
                {
                    _currentCourseHasChanges = value;
                    OnPropertyChanged("CurrentCourseHasChanges");
                }
            }
        }

        private bool _currentCourseHasChanges;

        /// <summary>
        /// True if at least one call is
        /// in progress; otherwise, false
        /// </summary>
        public Boolean IsBusy
        {
            get { return _isBusy; }
            private set
            {
                if (_isBusy != value)
                {
                    _isBusy = value;
                    OnPropertyChanged("IsBusy");
                }
            }
        }

        private Boolean _isBusy;

        #endregion "ISchoolModel Interface (Data Status) implementation"

        #region "ISchoolModel Interface (Update and Rollback of Entity List) implementation"

        /// <summary>
        /// If allItems is true, all items from the StudentsList have
        /// their changes saved; otherwise, only CurrentStudent from
        /// the StudentsList has its changes saved.
        /// </summary>
        /// <param name="allItems"></param>
        public void SaveStudentChangesAsync(bool allItems = true)
        {
            if (allItems)
            {
                if (StudentsList != null && StudentsListHasChanges)
                {
                    // save changes for all items from the StudentsList
                    foreach (var student in StudentsList.Where(n => n.ObjectGraphHasChanges()))
                    {
                        var totalSize = student.EstimateObjectGraphSize();
                        var changeSize = student.EstimateObjectGraphChangeSize();
                        // if the optimized entity object graph is less than 70%
                        // of the original, call GetObjectGraphChanges()
                        var currentStudent = changeSize < (totalSize*0.7)
                                                 ? (Student) student.GetObjectGraphChanges()
                                                 : student;

                        _actionQueue.Add(
                            n => _proxy.BeginUpdateStudent(
                                currentStudent,
                                BeginUpdateStudentComplete,
                                currentStudent.PersonId));
                    }
                    // start save changes for the first student
                    if (_actionQueue.BeginOneAction()) _proxy.IncrementCallCount();
                }
            }
            else
            {
                if (CurrentStudent != null && StudentsList != null && CurrentStudentHasChanges)
                {
                    // save changes for only CurrentStudent from the StudentsList
                    var currentStudent = StudentsList
                        .FirstOrDefault(n => n.PersonId == CurrentStudent.PersonId);
                    if (currentStudent != null)
                    {
                        var totalSize = currentStudent.EstimateObjectGraphSize();
                        var changeSize = currentStudent.EstimateObjectGraphChangeSize();
                        // if the optimized entity object graph is less than 70%
                        // of the original, call GetObjectGraphChanges()
                        currentStudent = changeSize < (totalSize*0.7)
                                             ? (Student) currentStudent.GetObjectGraphChanges()
                                             : currentStudent;

                        _actionQueue.Add(
                            n => _proxy.BeginUpdateStudent(
                                currentStudent,
                                BeginUpdateStudentComplete,
                                currentStudent.PersonId));
                        // start save changes for the current student
                        if (_actionQueue.BeginOneAction()) _proxy.IncrementCallCount();
                    }
                }
            }
        }

        /// <summary>
        /// If allItems is true, all items from the StudentsList have
        /// their changes rejected; otherwise, only CurrentStudent from
        /// the StudentsList has its changes rejected.
        /// </summary>
        /// <param name="allItems"></param>
        public void RejectStudentChanges(bool allItems = true)
        {
            if (allItems)
            {
                if (StudentsList != null && StudentsListHasChanges)
                {
                    // reject changes for all items from the StudentsList
                    foreach (var student in StudentsList.Where(n => n.ObjectGraphHasChanges()).ToList())
                    {
                        var isAdded = student.ChangeTracker.State == ObjectState.Added;
                        student.RejectObjectGraphChanges();
                        // if the State is Added, simply remove it from the StudentsList
                        if (isAdded) StudentsList.Remove(student);
                    }
                }
            }
            else
            {
                if (CurrentStudent != null && StudentsList != null && CurrentStudentHasChanges)
                {
                    // reject changes for only CurrentStudent from the StudentsList
                    var currentStudent = StudentsList
                        .FirstOrDefault(n => n.PersonId == CurrentStudent.PersonId);
                    if (currentStudent != null)
                    {
                        var isAdded = currentStudent.ChangeTracker.State == ObjectState.Added;
                        currentStudent.RejectObjectGraphChanges();
                        // if the State is Added, simply remove it from the StudentsList
                        if (isAdded) StudentsList.Remove(currentStudent);
                    }
                }
            }
        }

        /// <summary>
        /// If allItems is true, all items from the InstructorsList have
        /// their changes saved; otherwise, only CurrentInstructor from
        /// the InstructorsList has its changes saved.
        /// </summary>
        /// <param name="allItems"></param>
        public void SaveInstructorChangesAsync(bool allItems = true)
        {
            if (allItems)
            {
                if (InstructorsList != null && InstructorsListHasChanges)
                {
                    // save changes for all items from the InstructorsList
                    foreach (var instructor in InstructorsList.Where(n => n.ObjectGraphHasChanges()))
                    {
                        var totalSize = instructor.EstimateObjectGraphSize();
                        var changeSize = instructor.EstimateObjectGraphChangeSize();
                        // if the optimized entity object graph is less than 70%
                        // of the original, call GetObjectGraphChanges()
                        var currentInstructor = changeSize < (totalSize*0.7)
                                                    ? (Instructor) instructor.GetObjectGraphChanges()
                                                    : instructor;

                        _actionQueue.Add(
                            n => _proxy.BeginUpdateInstructor(
                                currentInstructor,
                                BeginUpdateInstructorComplete,
                                currentInstructor.PersonId));
                    }
                    // start save changes for the first instructor
                    if (_actionQueue.BeginOneAction()) _proxy.IncrementCallCount();
                }
            }
            else
            {
                if (CurrentInstructor != null && InstructorsList != null && CurrentInstructorHasChanges)
                {
                    // save changes for only CurrentInstructor from the InstructorsList
                    var currentInstructor = InstructorsList
                        .FirstOrDefault(n => n.PersonId == CurrentInstructor.PersonId);
                    if (currentInstructor != null)
                    {
                        var totalSize = currentInstructor.EstimateObjectGraphSize();
                        var changeSize = currentInstructor.EstimateObjectGraphChangeSize();
                        // if the optimized entity object graph is less than 70%
                        // of the original, call GetObjectGraphChanges()
                        currentInstructor = changeSize < (totalSize*0.7)
                                                ? (Instructor) currentInstructor.GetObjectGraphChanges()
                                                : currentInstructor;

                        _actionQueue.Add(
                            n => _proxy.BeginUpdateInstructor(
                                currentInstructor,
                                BeginUpdateInstructorComplete,
                                currentInstructor.PersonId));
                        // start save changes for the current instructor
                        if (_actionQueue.BeginOneAction()) _proxy.IncrementCallCount();
                    }
                }
            }
        }

        /// <summary>
        /// If allItems is true, all items from the InstructorsList have
        /// their changes rejected; otherwise, only CurrentInstructor from
        /// the InstructorsList has its changes rejected.
        /// </summary>
        /// <param name="allItems"></param>
        public void RejectInstructorChanges(bool allItems = true)
        {
            if (allItems)
            {
                if (InstructorsList != null && InstructorsListHasChanges)
                {
                    // reject changes for all items from the InstructorsList
                    foreach (var instructor in InstructorsList.Where(n => n.ObjectGraphHasChanges()).ToList())
                    {
                        var isAdded = instructor.ChangeTracker.State == ObjectState.Added;
                        instructor.RejectObjectGraphChanges();
                        // if the State is Added, simply remove it from the InstructorsList
                        if (isAdded) InstructorsList.Remove(instructor);
                    }
                }
            }
            else
            {
                if (CurrentInstructor != null && InstructorsList != null && CurrentInstructorHasChanges)
                {
                    // reject changes for only CurrentInstructor from the InstructorsList
                    var currentInstructor = InstructorsList
                        .FirstOrDefault(n => n.PersonId == CurrentInstructor.PersonId);
                    if (currentInstructor != null)
                    {
                        var isAdded = currentInstructor.ChangeTracker.State == ObjectState.Added;
                        currentInstructor.RejectObjectGraphChanges();
                        // if the State is Added, simply remove it from the InstructorsList
                        if (isAdded) InstructorsList.Remove(currentInstructor);
                    }
                }
            }
        }

        /// <summary>
        /// If allItems is true, all items from the CoursesList have
        /// their changes saved; otherwise, only CurrentCourse from
        /// the CoursesList has its changes saved.
        /// </summary>
        /// <param name="allItems"></param>
        public void SaveCourseChangesAsync(bool allItems = true)
        {
            if (allItems)
            {
                if (CoursesList != null && CoursesListHasChanges)
                {
                    // save changes for all items from the CoursesList
                    foreach (var course in CoursesList.Where(n => n.ObjectGraphHasChanges()))
                    {
                        var totalSize = course.EstimateObjectGraphSize();
                        var changeSize = course.EstimateObjectGraphChangeSize();
                        // if the optimized entity object graph is less than 70%
                        // of the original, call GetObjectGraphChanges()
                        var currentCourse = changeSize < (totalSize*0.7)
                                                ? (Course) course.GetObjectGraphChanges()
                                                : course;

                        _actionQueue.Add(
                            n => _proxy.BeginUpdateCourse(
                                currentCourse,
                                BeginUpdateCourseComplete,
                                currentCourse.CourseId));
                    }
                    // start save changes for the first course in queue
                    if (_actionQueue.BeginOneAction()) _proxy.IncrementCallCount();
                }
            }
            else
            {
                if (CurrentCourse != null && CoursesList != null && CurrentCourseHasChanges)
                {
                    // save changes for only CurrentCourse from the CoursesList
                    var currentCourse = CoursesList.FirstOrDefault(n => n.CourseId == CurrentCourse.CourseId);
                    if (currentCourse != null)
                    {
                        var totalSize = currentCourse.EstimateObjectGraphSize();
                        var changeSize = currentCourse.EstimateObjectGraphChangeSize();
                        // if the optimized entity object graph is less than 70%
                        // of the original, call GetObjectGraphChanges()
                        currentCourse = changeSize < (totalSize*0.7)
                                            ? (Course) currentCourse.GetObjectGraphChanges()
                                            : currentCourse;

                        _actionQueue.Add(
                            n => _proxy.BeginUpdateCourse(
                                currentCourse,
                                BeginUpdateCourseComplete,
                                currentCourse.CourseId));
                        // start save changes for the current course
                        if (_actionQueue.BeginOneAction()) _proxy.IncrementCallCount();
                    }
                }
            }
        }

        /// <summary>
        /// If allItems is true, all items from the CoursesList have
        /// their changes rejected; otherwise, only CurrentCourse from
        /// the CoursesList has its changes rejected.
        /// </summary>
        /// <param name="allItems"></param>
        public void RejectCourseChanges(bool allItems = true)
        {
            if (allItems)
            {
                if (CoursesList != null && CoursesListHasChanges)
                {
                    // reject changes for all items from the CoursesList
                    foreach (var course in CoursesList.Where(n => n.ObjectGraphHasChanges()).ToList())
                    {
                        var isAdded = course.ChangeTracker.State == ObjectState.Added;
                        course.RejectObjectGraphChanges();
                        // if the State is Added, simply remove it from the CoursesList
                        if (isAdded) CoursesList.Remove(course);
                    }
                }
            }
            else
            {
                if (CurrentCourse != null && CoursesList != null && CurrentCourseHasChanges)
                {
                    // reject changes for only CurrentCourse from the CoursesList
                    var currentCourse = CoursesList.FirstOrDefault(n => n.CourseId == CurrentCourse.CourseId);
                    if (currentCourse != null)
                    {
                        var isAdded = currentCourse.ChangeTracker.State == ObjectState.Added;
                        currentCourse.RejectObjectGraphChanges();
                        // if the State is Added, simply remove it from the CoursesList
                        if (isAdded) CoursesList.Remove(currentCourse);
                    }
                }
            }
        }

        public event EventHandler<ResultArgs<string>> SaveStudentChangesCompleted;
        public event EventHandler<ResultArgs<string>> SaveInstructorChangesCompleted;
        public event EventHandler<ResultArgs<string>> SaveCourseChangesCompleted;

        #endregion "ISchoolModel Interface (Update and Rollback of Entity List) implementation"

        #region "ISchoolModel Interface (Error Handling) implementation"

        public Exception GetLastError()
        {
            return _lastError;
        }

        public void ClearLastError()
        {
            _lastError = null;
        }

        public bool AllowMultipleErrors { get; set; }

        #endregion "ISchoolModel Interface (Error Handling) implementation"

        #region "INotifyPropertyChanged Interface implementation"
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion "INotifyPropertyChanged Interface implementation"

        #region "Private Methods"

        /// <summary>
        /// CollectionChanged Event handler for _studentsList
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _studentsList_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null)
            {
                foreach (Student newItem in e.NewItems)
                    ((INotifyPropertyChanged)newItem).PropertyChanged += EntityModel_PropertyChanged;
            }
            if (e.OldItems != null)
            {
                foreach (Student oldItem in e.OldItems)
                    ((INotifyPropertyChanged)oldItem).PropertyChanged -= EntityModel_PropertyChanged;
            }
            ReCalculateStudentsListHasChanges();
        }

        /// <summary>
        /// CollectionChanged Event handler for _instructorsList
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _instructorsList_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null)
            {
                foreach (Instructor newItem in e.NewItems)
                    ((INotifyPropertyChanged)newItem).PropertyChanged += EntityModel_PropertyChanged;
            }
            if (e.OldItems != null)
            {
                foreach (Instructor oldItem in e.OldItems)
                    ((INotifyPropertyChanged)oldItem).PropertyChanged -= EntityModel_PropertyChanged;
            }
            ReCalculateInstructorsListHasChanges();
        }

        /// <summary>
        /// CollectionChanged Event handler for _coursesList
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _coursesList_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null)
            {
                foreach (Course newItem in e.NewItems)
                {
                    ((INotifyPropertyChanged)newItem).PropertyChanged += EntityModel_PropertyChanged;
                    if (newItem.Enrollments != null)
                    {
                        newItem.Enrollments.CollectionChanged += Enrollments_CollectionChanged;
                        foreach (Enrollment item in newItem.Enrollments)
                            ((INotifyPropertyChanged)item).PropertyChanged += EntityModel_PropertyChanged;
                    }
                }
            }
            if (e.OldItems != null)
            {
                foreach (Course oldItem in e.OldItems)
                {
                    ((INotifyPropertyChanged)oldItem).PropertyChanged -= EntityModel_PropertyChanged;
                    if (oldItem.Enrollments != null)
                    {
                        oldItem.Enrollments.CollectionChanged -= Enrollments_CollectionChanged;
                        foreach (Enrollment item in oldItem.Enrollments)
                            ((INotifyPropertyChanged)item).PropertyChanged -= EntityModel_PropertyChanged;
                    }
                }
            }
            ReCalculateCoursesListHasChanges();
        }

        /// <summary>
        /// CollectionChanged Event handler for Enrollments
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Enrollments_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null)
            {
                foreach (Enrollment item in e.NewItems)
                    ((INotifyPropertyChanged)item).PropertyChanged += EntityModel_PropertyChanged;
            }
            if (e.OldItems != null)
            {
                foreach (Enrollment item in e.OldItems)
                    ((INotifyPropertyChanged)item).PropertyChanged -= EntityModel_PropertyChanged;
            }
        }

        /// <summary>
        /// Event handler for PropertyChanged
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void EntityModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName.Equals("HasChanges"))
            {
                if (sender is Student)
                {
                    ReCalculateStudentsListHasChanges();
                    ReCalculateCurrentStudentHasChanges();
                }
                else if (sender is Instructor)
                {
                    ReCalculateInstructorsListHasChanges();
                    ReCalculateCurrentInstructorHasChanges();
                }
                else if (sender is Course || sender is Enrollment)
                {
                    ReCalculateCoursesListHasChanges();
                    ReCalculateCurrentCourseHasChanges();
                }
                else
                {
                    throw new NotImplementedException();
                }
            }
        }

        /// <summary>
        /// Re-calculate whether StudentsList has any changes
        /// </summary>
        private void ReCalculateStudentsListHasChanges()
        {
            // re-calculate StudentsListHasChanges
            StudentsListHasChanges = StudentsList != null
                && StudentsList.Any(n => n.ObjectGraphHasChanges());
        }

        /// <summary>
        /// Re-calculate whether CurrentStudent has changes
        /// </summary>
        private void ReCalculateCurrentStudentHasChanges()
        {
            // re-calculate CurrentStudentHasChanges
            CurrentStudentHasChanges = CurrentStudent != null
                && CurrentStudent.ObjectGraphHasChanges();
        }

        /// <summary>
        /// Re-calculate whether InstructorsList has any changes
        /// </summary>
        private void ReCalculateInstructorsListHasChanges()
        {
            // re-calculate InstructorsListHasChanges
            InstructorsListHasChanges = InstructorsList != null
                && InstructorsList.Any(n => n.ObjectGraphHasChanges());
        }

        /// <summary>
        /// Re-calculate whether CurrentInstructor has changes
        /// </summary>
        private void ReCalculateCurrentInstructorHasChanges()
        {
            // re-calculate CurrentInstructorHasChanges
            CurrentInstructorHasChanges = CurrentInstructor != null
                && CurrentInstructor.ObjectGraphHasChanges();
        }

        /// <summary>
        /// Re-calculate whether CoursesList has any changes
        /// </summary>
        private void ReCalculateCoursesListHasChanges()
        {
            // re-calculate CoursesListHasChanges
            CoursesListHasChanges = CoursesList != null
                && CoursesList.Any(n => n.ObjectGraphHasChanges());
        }

        /// <summary>
        /// Re-calculate whether CurrentCourse has changes
        /// </summary>
        private void ReCalculateCurrentCourseHasChanges()
        {
            // re-calculate CurrentCourseHasChanges
            CurrentCourseHasChanges = CurrentCourse != null
                && CurrentCourse.ObjectGraphHasChanges();
        }

        /// <summary>
        /// Event handler for PropertyChanged
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _proxy_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName.Equals("ActiveCallCount"))
            {
                // re-calculate IsBusy
                IsBusy = (_proxy.ActiveCallCount != 0);
            }
        }

        /// <summary>
        /// AsyncCallback for BeginGetStudents
        /// </summary>
        /// <param name="result"></param>
        private void BeginGetStudentsComplete(IAsyncResult result)
        {
            ThreadHelper.BeginInvokeOnUIThread(
                delegate
                {
                    _proxy.DecrementCallCount();
                    try
                    {
                        // get the return values
                        var students = _proxy.EndGetStudents(result);
                        if (GetStudentsCompleted != null)
                        {
                            GetStudentsCompleted(this, new ResultsArgs<Student>(students, null, false, result.AsyncState));
                        }
                    }
                    catch (Exception ex)
                    {
                        if (GetStudentsCompleted != null && (_lastError == null || AllowMultipleErrors))
                        {
                            GetStudentsCompleted(this, new ResultsArgs<Student>(null, ex, true, result.AsyncState));
                        }
                        _lastError = ex;
                    }
                });
        }

        /// <summary>
        /// AsyncCallback for BeginGetStudentById
        /// </summary>
        /// <param name="result"></param>
        private void BeginGetStudentByIdComplete(IAsyncResult result)
        {
            ThreadHelper.BeginInvokeOnUIThread(
                delegate
                {
                    _proxy.DecrementCallCount();
                    try
                    {
                        // get the return values
                        var student = _proxy.EndGetStudentById(result);
                        if (GetStudentByIdCompleted != null)
                        {
                            GetStudentByIdCompleted(this, new ResultArgs<Student>(student, null, false, result.AsyncState));
                        }
                    }
                    catch (Exception ex)
                    {
                        if (GetStudentByIdCompleted != null && (_lastError == null || AllowMultipleErrors))
                        {
                            GetStudentByIdCompleted(this, new ResultArgs<Student>(null, ex, true, result.AsyncState));
                        }
                        _lastError = ex;
                    }
                });
        }

        /// <summary>
        /// AsyncCallback for BeginGetInstructors
        /// </summary>
        /// <param name="result"></param>
        private void BeginGetInstructorsComplete(IAsyncResult result)
        {
            ThreadHelper.BeginInvokeOnUIThread(
                delegate
                {
                    _proxy.DecrementCallCount();
                    try
                    {
                        // get the return values
                        var instructors = _proxy.EndGetInstructors(result);
                        if (GetInstructorsCompleted != null)
                        {
                            GetInstructorsCompleted(this, new ResultsArgs<Instructor>(instructors, null, false, result.AsyncState));
                        }
                    }
                    catch (Exception ex)
                    {
                        if (GetInstructorsCompleted != null && (_lastError == null || AllowMultipleErrors))
                        {
                            GetInstructorsCompleted(this, new ResultsArgs<Instructor>(null, ex, true, result.AsyncState));
                        }
                        _lastError = ex;
                    }
                });
        }

        /// <summary>
        /// AsyncCallback for BeginGetInstructorById
        /// </summary>
        /// <param name="result"></param>
        private void BeginGetInstructorByIdComplete(IAsyncResult result)
        {
            ThreadHelper.BeginInvokeOnUIThread(
                delegate
                {
                    _proxy.DecrementCallCount();
                    try
                    {
                        // get the return values
                        var instructor = _proxy.EndGetInstructorById(result);
                        if (GetInstructorByIdCompleted != null)
                        {
                            GetInstructorByIdCompleted(this, new ResultArgs<Instructor>(instructor, null, false, result.AsyncState));
                        }
                    }
                    catch (Exception ex)
                    {
                        if (GetInstructorByIdCompleted != null && (_lastError == null || AllowMultipleErrors))
                        {
                            GetInstructorByIdCompleted(this, new ResultArgs<Instructor>(null, ex, true, result.AsyncState));
                        }
                        _lastError = ex;
                    }
                });
        }

        /// <summary>
        /// AsyncCallback for BeginGetCourses
        /// </summary>
        /// <param name="result"></param>
        private void BeginGetCoursesComplete(IAsyncResult result)
        {
            ThreadHelper.BeginInvokeOnUIThread(
                delegate
                {
                    _proxy.DecrementCallCount();
                    try
                    {
                        // get the return values
                        var courses = _proxy.EndGetCourses(result);
                        if (GetCoursesCompleted != null)
                        {
                            GetCoursesCompleted(this, new ResultsArgs<Course>(courses, null, false, result.AsyncState));
                        }
                    }
                    catch (Exception ex)
                    {
                        if (GetCoursesCompleted != null && (_lastError == null || AllowMultipleErrors))
                        {
                            GetCoursesCompleted(this, new ResultsArgs<Course>(null, ex, true, result.AsyncState));
                        }
                        _lastError = ex;
                    }
                });
        }

        /// <summary>
        /// AsyncCallback for BeginGetCourseById
        /// </summary>
        /// <param name="result"></param>
        private void BeginGetCourseByIdComplete(IAsyncResult result)
        {
            ThreadHelper.BeginInvokeOnUIThread(
                delegate
                {
                    _proxy.DecrementCallCount();
                    try
                    {
                        // get the return values
                        var course = _proxy.EndGetCourseById(result);
                        if (GetCourseByIdCompleted != null)
                        {
                            GetCourseByIdCompleted(this, new ResultArgs<Course>(course, null, false, result.AsyncState));
                        }
                    }
                    catch (Exception ex)
                    {
                        if (GetCourseByIdCompleted != null && (_lastError == null || AllowMultipleErrors))
                        {
                            GetCourseByIdCompleted(this, new ResultArgs<Course>(null, ex, true, result.AsyncState));
                        }
                        _lastError = ex;
                    }
                });
        }

        /// <summary>
        /// AsyncCallback for BeginUpdateStudent
        /// </summary>
        /// <param name="result"></param>
        private void BeginUpdateStudentComplete(IAsyncResult result)
        {
            ThreadHelper.BeginInvokeOnUIThread(
                delegate
                {
                    try
                    {
                        // get the return values
                        var returnList = _proxy.EndUpdateStudent(result);
                        // returnList[0] could be a warning message
                        var warningMessage = returnList[0] as string;
                        // returnList[1] is the updated student Id
                        var updatedStudentId = Convert.ToInt32(returnList[1]);
                        // get the studentId for the student that finished saving changes
                        var studentId = Convert.ToInt32(result.AsyncState);
                        var student = StudentsList.Single(n => n.PersonId == studentId);
                        // update the student Id if the student State is Added
                        if (student.ChangeTracker.State == ObjectState.Added)
                            student.PersonId = updatedStudentId;

                        if (string.IsNullOrEmpty(warningMessage))
                        {
                            var isDeleted = student.ChangeTracker.State == ObjectState.Deleted;
                            // if there is no error or warning message, call AcceptObjectGraphChanges() first
                            student.AcceptObjectGraphChanges();
                            // if State is Deleted, remove the student from the StudentsList
                            if (isDeleted) StudentsList.Remove(student);
                            // then, continue to save changes for the next student in queue
                            if (_actionQueue.BeginOneAction() == false)
                            {
                                // all changes are saved, we need to send notification
                                _proxy.DecrementCallCount();
                                if (SaveStudentChangesCompleted != null &&
                                    _lastError == null && string.IsNullOrEmpty(warningMessage))
                                {
                                    SaveStudentChangesCompleted(this,
                                        new ResultArgs<string>(string.Empty, null, false, null));
                                }
                            }
                        }
                        else
                        {
                            // if there is a warning message, we need to stop and send notification
                            // on first occurrence, in other words, if _lastError is still null
                            _actionQueue.Clear();
                            _proxy.DecrementCallCount();
                            if (SaveStudentChangesCompleted != null &&
                                (_lastError == null || AllowMultipleErrors))
                            {
                                SaveStudentChangesCompleted(this,
                                    new ResultArgs<string>(warningMessage, null, true, null));
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        // if there is an error, we need to stop and send notification
                        // on first occurrence, in other words, if _lastError is still null
                        _actionQueue.Clear();
                        _proxy.DecrementCallCount();
                        if (SaveStudentChangesCompleted != null &&
                            (_lastError == null || AllowMultipleErrors))
                        {
                            SaveStudentChangesCompleted(this,
                                new ResultArgs<string>(string.Empty, ex, true, null));
                        }
                        _lastError = ex;
                    }
                });
        }

        /// <summary>
        /// AsyncCallback for BeginUpdateInstructor
        /// </summary>
        /// <param name="result"></param>
        private void BeginUpdateInstructorComplete(IAsyncResult result)
        {
            ThreadHelper.BeginInvokeOnUIThread(
                delegate
                {
                    try
                    {
                        // get the return values
                        var returnList = _proxy.EndUpdateInstructor(result);
                        // returnList[0] could be a warning message
                        var warningMessage = returnList[0] as string;
                        // returnList[1] is the updated instructor Id
                        var updatedInstructorId = Convert.ToInt32(returnList[1]);
                        // get the instructorId for the instructor that finished saving changes
                        var instructorId = Convert.ToInt32(result.AsyncState);
                        var instructor = InstructorsList.Single(n => n.PersonId == instructorId);
                        // update the instructor Id if the instructor State is Added
                        if (instructor.ChangeTracker.State == ObjectState.Added)
                            instructor.PersonId = updatedInstructorId;

                        if (string.IsNullOrEmpty(warningMessage))
                        {
                            var isDeleted = instructor.ChangeTracker.State == ObjectState.Deleted;
                            // if there is no error or warning message, call AcceptObjectGraphChanges() first
                            instructor.AcceptObjectGraphChanges();
                            // if State is Deleted, remove the instructor from the InstructorsList
                            if (isDeleted) InstructorsList.Remove(instructor);
                            // then, continue to save changes for the next instructor in queue
                            if (_actionQueue.BeginOneAction() == false)
                            {
                                // all changes are saved, we need to send notification
                                _proxy.DecrementCallCount();
                                if (SaveInstructorChangesCompleted != null &&
                                    _lastError == null && string.IsNullOrEmpty(warningMessage))
                                {
                                    SaveInstructorChangesCompleted(this,
                                        new ResultArgs<string>(string.Empty, null, false, null));
                                }
                            }
                        }
                        else
                        {
                            // if there is a warning message, we need to stop and send notification
                            // on first occurrence, in other words, if _lastError is still null
                            _actionQueue.Clear();
                            _proxy.DecrementCallCount();
                            if (SaveInstructorChangesCompleted != null &&
                                (_lastError == null || AllowMultipleErrors))
                            {
                                SaveInstructorChangesCompleted(this,
                                    new ResultArgs<string>(warningMessage, null, true, null));
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        // if there is an error, we need to stop and send notification
                        // on first occurrence, in other words, if _lastError is still null
                        _actionQueue.Clear();
                        _proxy.DecrementCallCount();
                        if (SaveInstructorChangesCompleted != null &&
                            (_lastError == null || AllowMultipleErrors))
                        {
                            SaveInstructorChangesCompleted(this,
                                new ResultArgs<string>(string.Empty, ex, true, null));
                        }
                        _lastError = ex;
                    }
                });
        }

        /// <summary>
        /// AsyncCallback for BeginUpdateCourse
        /// </summary>
        /// <param name="result"></param>
        private void BeginUpdateCourseComplete(IAsyncResult result)
        {
            ThreadHelper.BeginInvokeOnUIThread(
                delegate
                {
                    try
                    {
                        // get the return values
                        var returnList = _proxy.EndUpdateCourse(result);
                        // returnList[0] could be a warning message
                        var warningMessage = returnList[0] as string;
                        // returnList[1] is the updated course Id
                        var updatedCourseId = Convert.ToInt32(returnList[1]);
                        // get the courseId for the course that finished saving changes
                        var courseId = Convert.ToInt32(result.AsyncState);
                        var course = CoursesList.Single(n => n.CourseId == courseId);
                        // update the course Id if the course State is Added
                        if (course.ChangeTracker.State == ObjectState.Added)
                            course.CourseId = updatedCourseId;

                        if (string.IsNullOrEmpty(warningMessage))
                        {
                            var isDeleted = course.ChangeTracker.State == ObjectState.Deleted;
                            // if there is no error or warning message, call AcceptObjectGraphChanges() first
                            course.AcceptObjectGraphChanges();
                            // if State is Deleted, remove the course from the CoursesList
                            if (isDeleted) CoursesList.Remove(course);
                            // then, continue to save changes for the next course in queue
                            if (_actionQueue.BeginOneAction() == false)
                            {
                                // all changes are saved, we need to send notification
                                _proxy.DecrementCallCount();
                                if (SaveCourseChangesCompleted != null &&
                                    _lastError == null &&
                                    string.IsNullOrEmpty(warningMessage))
                                {
                                    SaveCourseChangesCompleted(this,
                                        new ResultArgs<string>(
                                            string.Empty, null,
                                            false, null));
                                }
                            }
                        }
                        else
                        {
                            // if there is a warning message, we need to stop and send notification
                            // on first occurrence, in other words, if _lastError is still null
                            _actionQueue.Clear();
                            _proxy.DecrementCallCount();
                            if (SaveCourseChangesCompleted != null &&
                                (_lastError == null || AllowMultipleErrors))
                            {
                                SaveCourseChangesCompleted(this,
                                    new ResultArgs<string>(
                                        warningMessage, null,
                                        true, null));
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        // if there is an error, we need to stop and send notification
                        // on first occurrence, in other words, if _lastError is still null
                        _actionQueue.Clear();
                        _proxy.DecrementCallCount();
                        if (SaveCourseChangesCompleted != null &&
                            (_lastError == null || AllowMultipleErrors))
                        {
                            SaveCourseChangesCompleted(this,
                                new ResultArgs<string>(
                                    string.Empty, ex, true,
                                    null));
                        }
                        _lastError = ex;
                    }
                });
        }

        #endregion "Private Methods"
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Weidong Shen
Software Developer (Senior)
United States United States
Weidong has been an information system professional since 1990. He has a Master's degree in Computer Science, and is currently a MCSD .NET

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.1411023.1 | Last Updated 20 Feb 2012
Article Copyright 2011 by Weidong Shen
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid