Click here to Skip to main content
15,884,298 members
Articles / Desktop Programming / WPF

Building WPF Applications with Self-Tracking Entity Generator and Visual Studio 2012 - Project Setup

Rate me:
Please Sign up or sign in to vote.
5.00/5 (14 votes)
17 Mar 2013CPOL8 min read 68.4K   3.5K   44  
This article describes the project setup of building a WPF sample application with Self-Tracking Entity Generator and Visual Studio 2012.
//------------------------------------------------------------------------------
// <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;
using System.Xml.Linq;
using System.IO.Compression;

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 Methods for Change Tracking on Client
    
        [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 "Enum for Object State"
    [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")]
    [KnownType(typeof(StatusEnum))]
    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();
    
            using (Stream stream = new MemoryStream())
            {
                var serializer = new DataContractSerializer(type);
                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;
    }
    
    /// <summary>
    /// ClientQuery stores a list of include paths and serialized expression tree.
    /// </summary>
    [DataContract(IsReference = true)]
    public class ClientQuery
    {
        #region Constructor
    
        public ClientQuery(List<string> includeList, XElement xmlExpression)
        {
            IncludeList = includeList;
            CompressedExpression = CompressUtil.Compress(xmlExpression);
        }
    
        #endregion Constructor
    
        #region Public DataMember
    
        [DataMember]
        public List<string> IncludeList { get; set; }
    
        [DataMember]
        public string CompressedExpression { get; set; }
    
        public XElement XmlExpression
        {
            get { return CompressUtil.Decompress(CompressedExpression); }
        }
    
        #endregion Public DataMember
    }
    
    /// <summary>
    /// Helper class to compress and decompress between XElement and string.
    /// </summary>
    public static class CompressUtil
    {
        public static string Compress(XElement xmlExpression)
        {
            using (MemoryStream gStream = new MemoryStream())
            {
                using (GZipStream gZipStream = new GZipStream(gStream, CompressionMode.Compress))
                using (MemoryStream mStream = new MemoryStream())
                {
                    xmlExpression.Save(mStream);
                    mStream.Seek(0, SeekOrigin.Begin);
                    var unCompressedBytes = mStream.ToArray();
                    gZipStream.Write(unCompressedBytes, 0, unCompressedBytes.Length);
                }
                return Convert.ToBase64String(gStream.ToArray());
            }
        }
    
        public static XElement Decompress(string compressedText)
        {
            using (MemoryStream gStream = new MemoryStream(Convert.FromBase64String(compressedText)))
            using (GZipStream gZipStream = new GZipStream(gStream, CompressionMode.Decompress))
            using (MemoryStream mStream = new MemoryStream())
            {
                var bufffer = new byte[0x400];
                var count = gZipStream.Read(bufffer, 0, bufffer.Length);
                while (count != 0)
                {
                    mStream.Write(bufffer, 0, count);
                    count = gZipStream.Read(bufffer, 0, bufffer.Length);
                }
                mStream.Seek(0, SeekOrigin.Begin);
                return XElement.Load(mStream);
            }
        }
    }
    
    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)


Written By
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

Comments and Discussions