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
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated from a template.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.ServiceModel;

namespace SchoolSample.EntityModel
{
    // Helper class that captures most of the change tracking work that needs to be done
    // for self tracking entities.
    [DataContract(IsReference = true)]
    public class ObjectChangeTracker
    {
        #region  Fields
    
        private object _parentObject;
        private bool _isDeserializing;
        private ObjectState _objectState = ObjectState.Added;
        private bool _changeTrackingEnabled;
        private OriginalValuesDictionary _originalValues;
        private ComplexTypeOriginalValuesDictionary _complexTypeOriginalValues;
        private ExtendedPropertiesDictionary _extendedProperties;
        private ObjectsAddedToCollectionProperties _objectsAddedToCollections = new ObjectsAddedToCollectionProperties();
        private ObjectsRemovedFromCollectionProperties _objectsRemovedFromCollections = new ObjectsRemovedFromCollectionProperties();
    
        #endregion
    
        #region Events
    
        public event EventHandler<ObjectStateChangingEventArgs> ObjectStateChanging;
        public event EventHandler<ObjectStateChangedEventArgs> ObjectStateChanged;
        public event EventHandler UpdateHasChanges;
    
        protected virtual void OnObjectStateChanging(ObjectState newState)
        {
            if (ObjectStateChanging != null)
            {
                ObjectStateChanging(this, new ObjectStateChangingEventArgs { NewState = newState });
            }
        }
    
        protected virtual void OnObjectStateChanged(ObjectState newState)
        {
            if (ObjectStateChanged != null)
            {
                ObjectStateChanged(this, new ObjectStateChangedEventArgs { NewState = newState });
            }
        }
    
        protected virtual void OnUpdateHasChanges()
        {
            if (UpdateHasChanges != null)
            {
                UpdateHasChanges(this, new EventArgs());
            }
        }
    
        #endregion
    
        #region Public Properties
    
        [DataMember]
        public ObjectState State
        {
            get { return _objectState; }
            set
            {
                if (_objectState != value)
                {
                    if (_isDeserializing || _changeTrackingEnabled)
                    {
                        OnObjectStateChanging(value);
                        _objectState = value;
                        OnObjectStateChanged(value);
                    }
                }
            }
        }
    
        public bool ChangeTrackingEnabled
        {
            get { return _changeTrackingEnabled; }
            set
            {
                if (_changeTrackingEnabled != value)
                {
                    _changeTrackingEnabled = value;
                    OnUpdateHasChanges();
                }
            }
        }
    
        // Returns the removed objects to collection valued properties that were changed.
        [DataMember]
        public ObjectsRemovedFromCollectionProperties ObjectsRemovedFromCollectionProperties
        {
            get
            {
                if (_objectsRemovedFromCollections == null)
                {
                    _objectsRemovedFromCollections = new ObjectsRemovedFromCollectionProperties();
                }
                return _objectsRemovedFromCollections;
            }
        }
    
        // Returns the original values for properties that were changed.
        [DataMember]
        public OriginalValuesDictionary OriginalValues
        {
            get
            {
                if (_originalValues == null)
                {
                    _originalValues = new OriginalValuesDictionary();
                }
                return _originalValues;
            }
        }
    
        // Returns the original values for Complex properties that were changed.
        [DataMember]
        public ComplexTypeOriginalValuesDictionary ComplexTypeOriginalValues
        {
            get
            {
                if (_complexTypeOriginalValues == null)
                {
                    _complexTypeOriginalValues = new ComplexTypeOriginalValuesDictionary();
                }
                return _complexTypeOriginalValues;
            }
        }
    
        // Returns the extended property values.
        // This includes key values for independent associations that are needed for the
        // concurrency model in the Entity Framework
        [DataMember]
        public ExtendedPropertiesDictionary ExtendedProperties
        {
            get
            {
                if (_extendedProperties == null)
                {
                    _extendedProperties = new ExtendedPropertiesDictionary();
                }
                return _extendedProperties;
            }
        }
    
        // Returns the added objects to collection valued properties that were changed.
        [DataMember]
        public ObjectsAddedToCollectionProperties ObjectsAddedToCollectionProperties
        {
            get
            {
                if (_objectsAddedToCollections == null)
                {
                    _objectsAddedToCollections = new ObjectsAddedToCollectionProperties();
                }
                return _objectsAddedToCollections;
            }
        }
    
        #endregion
    
        #region MethodsForChangeTrackingOnClient
    
        [OnDeserializing]
        public void OnDeserializingMethod(StreamingContext context)
        {
            _isDeserializing = true;
        }
    
        [OnDeserialized]
        public void OnDeserializedMethod(StreamingContext context)
        {
            _isDeserializing = false;
        }
    
        // Resets the ObjectChangeTracker to the Unchanged state and
        // clears the original values as well as the record of changes
        // to collection properties
        public void AcceptChanges()
        {
            OnObjectStateChanging(ObjectState.Unchanged);
            OriginalValues.Clear();
            ComplexTypeOriginalValues.Clear();
            ObjectsAddedToCollectionProperties.Clear();
            ObjectsRemovedFromCollectionProperties.Clear();
            ChangeTrackingEnabled = true;
            _objectState = ObjectState.Unchanged;
            OnObjectStateChanged(ObjectState.Unchanged);
        }
    
        // Resets the ObjectChangeTracker to the Unchanged state and
        // rollback the original values as well as the record of changes
        // to collection properties
        public void RejectChanges()
        {
            OnObjectStateChanging(ObjectState.Unchanged);
            Type type = _parentObject.GetType();
            // rollback original values for Primitive Properties
            foreach (var originalValue in OriginalValues.ToList())
            {
                PropertyInfo primitiveProperty = type.GetProperty(originalValue.Key);
                if (primitiveProperty != null)
                {
                    primitiveProperty.SetValue(_parentObject, originalValue.Value, null);
                }
            }
            // rollback original values for Complex Properties
            foreach (var originalValue in ComplexTypeOriginalValues.ToList())
            {
                PropertyInfo complexProperty = type.GetProperty(originalValue.Key);
                if (complexProperty != null)
                {
                    complexProperty.SetValue(_parentObject, originalValue.Value, null);
                }
            }
            // create copy of ObjectsAddedToCollectionProperties and ObjectsRemovedFromCollectionProperties
            Dictionary<string, ObjectList> removeCollection =
                ObjectsAddedToCollectionProperties.ToDictionary(n => n.Key, n => n.Value);
            Dictionary<string, ObjectList> addCollection =
                ObjectsRemovedFromCollectionProperties.ToDictionary(n => n.Key, n => n.Value);
            // rollback ObjectsAddedToCollectionProperties
            foreach (var entry in removeCollection)
            {
                PropertyInfo collectionProperty = type.GetProperty(entry.Key);
                IList collectionObject = (IList) collectionProperty.GetValue(_parentObject, null);
                foreach (object obj in entry.Value.ToList())
                    collectionObject.Remove(obj);
            }
            // rollback ObjectsRemovedFromCollectionProperties
            foreach (var entry in addCollection)
            {
                PropertyInfo collectionProperty = type.GetProperty(entry.Key);
                IList collectionObject = (IList) collectionProperty.GetValue(_parentObject, null);
                foreach (var obj in entry.Value.ToList())
                    collectionObject.Add(obj);
            }
            OriginalValues.Clear();
            ComplexTypeOriginalValues.Clear();
            ObjectsAddedToCollectionProperties.Clear();
            ObjectsRemovedFromCollectionProperties.Clear();
            _objectState = ObjectState.Unchanged;
            OnObjectStateChanged(ObjectState.Unchanged);
        }
    
        internal void SetParentObject(object parent)
        {
            _parentObject = parent;
        }
    
        // Clone the ObjectChangeTracker object
        internal ObjectChangeTracker Clone()
        {
            var objectChangeTracker = new ObjectChangeTracker {_objectState = State};
            if (OriginalValues != null)
            {
                objectChangeTracker._originalValues = new OriginalValuesDictionary();
                foreach (var originalValue in OriginalValues)
                {
                    (objectChangeTracker._originalValues).Add(originalValue.Key, originalValue.Value);
                }
            }
            if (ComplexTypeOriginalValues != null)
            {
                objectChangeTracker._complexTypeOriginalValues = new ComplexTypeOriginalValuesDictionary();
                foreach (var complexTypeOriginalValue in ComplexTypeOriginalValues)
                {
                    objectChangeTracker._complexTypeOriginalValues.Add(complexTypeOriginalValue.Key,
                                                                       complexTypeOriginalValue.Value);
                }
            }
            if (ExtendedProperties != null)
            {
                objectChangeTracker._extendedProperties = new ExtendedPropertiesDictionary();
                foreach (var extendedProperty in ExtendedProperties)
                {
                    objectChangeTracker._extendedProperties.Add(extendedProperty.Key, extendedProperty.Value);
                }
            }
            if (ObjectsAddedToCollectionProperties != null)
            {
                objectChangeTracker._objectsAddedToCollections = new ObjectsAddedToCollectionProperties();
                foreach (var objectsAddedToCollectionProperty in ObjectsAddedToCollectionProperties)
                {
                    objectChangeTracker._objectsAddedToCollections.Add(objectsAddedToCollectionProperty.Key,
                                                                       objectsAddedToCollectionProperty.Value);
                }
            }
            if (ObjectsRemovedFromCollectionProperties != null)
            {
                objectChangeTracker._objectsRemovedFromCollections = new ObjectsRemovedFromCollectionProperties();
                foreach (var objectsRemovedFromCollectionProperty in ObjectsRemovedFromCollectionProperties)
                {
                    objectChangeTracker._objectsRemovedFromCollections.Add(
                        objectsRemovedFromCollectionProperty.Key, objectsRemovedFromCollectionProperty.Value);
                }
            }
            return objectChangeTracker;
        }
    
        // Captures the original value for a property that is changing.
        internal void RecordOriginalValue(string propertyName, object value)
        {
            if (_changeTrackingEnabled && _objectState != ObjectState.Added)
            {
                if (!OriginalValues.ContainsKey(propertyName))
                {
                    OriginalValues[propertyName] = value;
                }
            }
        }
    
        // Captures the original value for a Complex property that is changing.
        internal void RecordComplexTypeOriginalValue(string propertyName, object value)
        {
            if (_changeTrackingEnabled && _objectState != ObjectState.Added)
            {
                if (!ComplexTypeOriginalValues.ContainsKey(propertyName))
                {
                    if (!ReferenceEquals(value, null))
                    {
                        ComplexTypeOriginalValues[propertyName] = value.Clone();
                    }
                    else
                    {
                        ComplexTypeOriginalValues[propertyName] = null;
                    }
                }
            }
        }
    
        // Records an addition to collection valued properties on SelfTracking Entities.
        internal void RecordAdditionToCollectionProperties(string propertyName, object value)
        {
            if (_changeTrackingEnabled)
            {
                // Add the entity back after deleting it, we should do nothing here then
                if (ObjectsRemovedFromCollectionProperties.ContainsKey(propertyName)
                    && ObjectsRemovedFromCollectionProperties[propertyName].Contains(value))
                {
                    ObjectsRemovedFromCollectionProperties[propertyName].Remove(value);
                    if (ObjectsRemovedFromCollectionProperties[propertyName].Count == 0)
                    {
                        ObjectsRemovedFromCollectionProperties.Remove(propertyName);
                    }
                    OnUpdateHasChanges();
                    return;
                }
    
                if (!ObjectsAddedToCollectionProperties.ContainsKey(propertyName))
                {
                    ObjectsAddedToCollectionProperties[propertyName] = new ObjectList();
                    ObjectsAddedToCollectionProperties[propertyName].Add(value);
                }
                else
                {
                    ObjectsAddedToCollectionProperties[propertyName].Add(value);
                }
                OnUpdateHasChanges();
            }
        }
    
        // Records a removal to collection valued properties on SelfTracking Entities.
        internal void RecordRemovalFromCollectionProperties(string propertyName, object value)
        {
            if (_changeTrackingEnabled)
            {
                // Delete the entity back after adding it, we should do nothing here then
                if (ObjectsAddedToCollectionProperties.ContainsKey(propertyName)
                    && ObjectsAddedToCollectionProperties[propertyName].Contains(value))
                {
                    ObjectsAddedToCollectionProperties[propertyName].Remove(value);
                    if (ObjectsAddedToCollectionProperties[propertyName].Count == 0)
                    {
                        ObjectsAddedToCollectionProperties.Remove(propertyName);
                    }
                    OnUpdateHasChanges();
                    return;
                }
    
                if (!ObjectsRemovedFromCollectionProperties.ContainsKey(propertyName))
                {
                    ObjectsRemovedFromCollectionProperties[propertyName] = new ObjectList();
                    ObjectsRemovedFromCollectionProperties[propertyName].Add(value);
                }
                else
                {
                    if (!ObjectsRemovedFromCollectionProperties[propertyName].Contains(value))
                    {
                        ObjectsRemovedFromCollectionProperties[propertyName].Add(value);
                    }
                }
                OnUpdateHasChanges();
            }
        }
        #endregion
    }
    
    #region EnumForObjectState
    [Flags]
    public enum ObjectState
    {
        Unchanged = 0x1,
        Added = 0x2,
        Modified = 0x4,
        Deleted = 0x8
    }
    #endregion
    
    [CollectionDataContract (Name = "ObjectsAddedToCollectionProperties",
        ItemName = "AddedObjectsForProperty", KeyName = "CollectionPropertyName", ValueName = "AddedObjects")]
    public class ObjectsAddedToCollectionProperties : Dictionary<string, ObjectList> { }
    
    [CollectionDataContract (Name = "ObjectsRemovedFromCollectionProperties",
        ItemName = "DeletedObjectsForProperty", KeyName = "CollectionPropertyName",ValueName = "DeletedObjects")]
    public class ObjectsRemovedFromCollectionProperties : Dictionary<string, ObjectList> { }
    
    [CollectionDataContract(Name = "OriginalValuesDictionary",
        ItemName = "OriginalValues", KeyName = "Name", ValueName = "OriginalValue")]
    public class OriginalValuesDictionary : Dictionary<string, Object> { }
    
    [CollectionDataContract(Name = "ComplexTypeOriginalValuesDictionary",
        ItemName = "ComplexTypeOriginalValues", KeyName = "Name", ValueName = "OriginalValue")]
    public class ComplexTypeOriginalValuesDictionary : Dictionary<string, Object> { }
    
    [CollectionDataContract(Name = "ExtendedPropertiesDictionary",
        ItemName = "ExtendedProperties", KeyName = "Name", ValueName = "ExtendedProperty")]
    public class ExtendedPropertiesDictionary : Dictionary<string, Object> { }
    
    [CollectionDataContract(ItemName = "ObjectValue")]
    public class ObjectList : List<object> { }
    // The interface is implemented by the self tracking entities that EF will generate.
    // We will have an Adapter that converts this interface to the interface that the EF expects.
    // The Adapter will live on the server side.
    public interface IObjectWithChangeTracker
    {
        // Has all the change tracking information for the subgraph of a given object.
        ObjectChangeTracker ChangeTracker { get; }
    }
    
    public class ObjectStateChangingEventArgs : EventArgs
    {
        public ObjectState NewState { get; set; }
    }
    
    public class ObjectStateChangedEventArgs : EventArgs
    {
        public ObjectState NewState { get; set; }
    }
    
    public static class ObjectWithChangeTrackerExtensions
    {
        public static T MarkAsDeleted<T>(this T trackingItem) where T : class, IObjectWithChangeTracker
        {
            if (trackingItem == null)
            {
                throw new ArgumentNullException("trackingItem");
            }
    
            trackingItem.ChangeTracker.ChangeTrackingEnabled = true;
            trackingItem.ChangeTracker.State = ObjectState.Deleted;
            return trackingItem;
        }
    
        public static T MarkAsAdded<T>(this T trackingItem) where T : class, IObjectWithChangeTracker
        {
            if (trackingItem == null)
            {
                throw new ArgumentNullException("trackingItem");
            }
    
            trackingItem.ChangeTracker.ChangeTrackingEnabled = true;
            trackingItem.ChangeTracker.State = ObjectState.Added;
            return trackingItem;
        }
    
        public static T MarkAsModified<T>(this T trackingItem) where T : class, IObjectWithChangeTracker
        {
            if (trackingItem == null)
            {
                throw new ArgumentNullException("trackingItem");
            }
    
            trackingItem.ChangeTracker.ChangeTrackingEnabled = true;
            trackingItem.ChangeTracker.State = ObjectState.Modified;
            return trackingItem;
        }
    
        public static T MarkAsUnchanged<T>(this T trackingItem) where T : class, IObjectWithChangeTracker
        {
            if (trackingItem == null)
            {
                throw new ArgumentNullException("trackingItem");
            }
    
            trackingItem.ChangeTracker.ChangeTrackingEnabled = true;
            trackingItem.ChangeTracker.State = ObjectState.Unchanged;
            return trackingItem;
        }
    
        public static void StartTracking(this IObjectWithChangeTracker trackingItem)
        {
            if (trackingItem == null)
            {
                throw new ArgumentNullException("trackingItem");
            }
    
            trackingItem.ChangeTracker.ChangeTrackingEnabled = true;
        }
    
        public static void StopTracking(this IObjectWithChangeTracker trackingItem)
        {
            if (trackingItem == null)
            {
                throw new ArgumentNullException("trackingItem");
            }
    
            trackingItem.ChangeTracker.ChangeTrackingEnabled = false;
        }
    
        public static void AcceptChanges(this IObjectWithChangeTracker trackingItem)
        {
            if (trackingItem == null)
            {
                throw new ArgumentNullException("trackingItem");
            }
    
            var editableObject = trackingItem as IEditableObject;
            if (editableObject != null) editableObject.EndEdit();
            trackingItem.ChangeTracker.AcceptChanges();
        }
    
        public static void RejectChanges(this IObjectWithChangeTracker trackingItem)
        {
            if (trackingItem == null)
            {
                throw new ArgumentNullException("trackingItem");
            }
    
            var editableObject = trackingItem as IEditableObject;
            if (editableObject != null) editableObject.EndEdit();
            trackingItem.ChangeTracker.RejectChanges();
        }
    
        public static T Clone<T>(this T source)
        {
            Type type = source.GetType();
      
            DataContractSerializer serializer = new DataContractSerializer(type);
            using (Stream stream = new MemoryStream())
            {
                serializer.WriteObject(stream, source);
                stream.Seek(0, SeekOrigin.Begin);
                return (T)serializer.ReadObject(stream);
            }
        }
    }
    
    // An System.Collections.ObjectModel.ObservableCollection that raises
    // individual item removal notifications on clear and prevents adding duplicates.
    public class TrackableCollection<T> : ObservableCollection<T>
    {
        protected override void ClearItems()
        {
            new List<T>(this).ForEach(t => Remove(t));
        }
    
        protected override void InsertItem(int index, T item)
        {
            if (!this.Contains(item))
            {
                base.InsertItem(index, item);
            }
        }
    }
    
    // An interface that provides an event that fires when complex properties change.
    // Changes can be the replacement of a complex property with a new complex type instance or
    // a change to a scalar property within a complex type instance.
    public interface INotifyComplexPropertyChanging
    {
        event EventHandler ComplexPropertyChanging;
    }
    
    public static class EqualityComparer
    {
        // Helper method to determine if two byte arrays are the same value even if they are different object references
        public static bool BinaryEquals(object binaryValue1, object binaryValue2)
        {
            if (ReferenceEquals(binaryValue1, binaryValue2))
            {
                return true;
            }
    
            byte[] array1 = binaryValue1 as byte[];
            byte[] array2 = binaryValue2 as byte[];
    
            if (array1 != null && array2 != null)
            {
                if (array1.Length != array2.Length)
                {
                    return false;
                }
    
                for (int i = 0; i < array1.Length; i++)
                {
                    if (array1[i] != array2[i])
                    {
                        return false;
                    }
                }
    
                return true;
            }
    
            return false;
        }
    }
}

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
Web03 | 2.8.1411023.1 | Last Updated 20 Feb 2012
Article Copyright 2011 by Weidong Shen
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid