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
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
<#@ template language="C#" debug="false" hostspecific="true"#>
<#@ include file="EF.Utility.CS.ttinclude"#><#@
output extension=".cs"#>
<#@ assembly name="PEM.Validations.dll" #>
<#@ assembly name="Microsoft.Data.Entity.Design.Extensibility.dll" #>
<#@ import namespace="PEM.Validations" #><#

CodeGenerationTools code = new CodeGenerationTools(this);
MetadataLoader loader = new MetadataLoader(this);
CodeRegion region = new CodeRegion(this, 1);
MetadataTools ef = new MetadataTools(this);

string inputFile = @"SchoolModel.edmx";
MetadataWorkspace metadataWorkspace = null;
bool allMetadataLoaded =loader.TryLoadAllMetadata(inputFile, out metadataWorkspace);
EdmItemCollection ItemCollection = (EdmItemCollection)metadataWorkspace.GetItemCollection(DataSpace.CSpace);
OriginalValueMembers originalValueMembers = new OriginalValueMembers(false, metadataWorkspace, ef);
string namespaceName = code.EscapeNamespace(@"SchoolSample.EntityModel");

EntityFrameworkTemplateFileManager fileManager = EntityFrameworkTemplateFileManager.Create(this);

// Write out support code to primary template output file
WriteHeader(fileManager);
BeginNamespace(namespaceName, code);
WriteObjectChangeTracker();
WriteIObjectWithChangeTracker();
WriteCustomObservableCollection();
WriteINotifyComplexPropertyChanging();
WriteEqualityComparer();
EndNamespace(namespaceName);

// Emit Entity Types
foreach (EntityType entity in ItemCollection.GetItems<EntityType>().OrderBy(e => e.Name))
{
    fileManager.StartNewFile(entity.Name + ".cs");
    BeginNamespace(namespaceName, code);
    WriteEntityTypeSerializationInfo(entity, ItemCollection, code, ef);
#>
<#=Accessibility.ForType(entity)#> <#=code.SpaceAfter(code.AbstractOption(entity))#>partial class <#=code.Escape(entity)#><#=code.StringBefore(" : ", code.Escape(entity.BaseType))#><#=entity.BaseType == null ? ": " : ", "#>IObjectWithChangeTracker, INotifyPropertyChanged
{
<#
    region.Begin("Primitive Properties");

    foreach (EdmProperty edmProperty in entity.Properties.Where(p => p.TypeUsage.EdmType is PrimitiveType && p.DeclaringType == entity))
    {
#>

    [DataMember]
<#BuildDataAnnotations(edmProperty);#>
    <#=Accessibility.ForProperty(edmProperty)#> <#=code.Escape(edmProperty.TypeUsage)#> <#=code.Escape(edmProperty)#>
    {
        <#=code.SpaceAfter(Accessibility.ForGetter(edmProperty))#>get { return <#=code.FieldName(edmProperty)#>; }
        <#=code.SpaceAfter(Accessibility.ForSetter(edmProperty))#>set
        {
<#
        if (((PrimitiveType)edmProperty.TypeUsage.EdmType).PrimitiveTypeKind == PrimitiveTypeKind.Binary &&
            (ef.IsKey(edmProperty) || entity.NavigationProperties.Where(np=>np.GetDependentProperties().Contains(edmProperty)).Any()))
        {
#>
            if (!EqualityComparer.BinaryEquals(<#=code.FieldName(edmProperty)#>, value))
<#
        }
        else
        {
#>
            if (<#=code.FieldName(edmProperty)#> != value)
<#
        }
#>
            {
<#
        if (ef.IsKey(edmProperty))
        {
            string errorMessage = String.Format("The property '{0}' is part of the object's key and cannot be changed. Changes to key properties can only be made when the object is not being tracked or is in the Added state.", edmProperty.Name);
#>
                if (ChangeTracker.ChangeTrackingEnabled && ChangeTracker.State != ObjectState.Added)
                {
                    throw new InvalidOperationException("<#=errorMessage#>");
                }
<#
        }
        else if (originalValueMembers.IsOriginalValueMember(edmProperty))
        {
#>
                ChangeTracker.RecordOriginalValue("<#=edmProperty.Name#>", <#=code.FieldName(edmProperty)#>);
<#
        }

        bool hasDependentProperties = entity.NavigationProperties.Where(np=>np.GetDependentProperties().Contains(edmProperty)).Any();
        if (hasDependentProperties)
        {
#>
                if (!IsDeserializing)
                {
<#
        }
        foreach (var np in entity.NavigationProperties.Where(np=>np.GetDependentProperties().Contains(edmProperty)))
        {
            EdmProperty principalProperty = ef.GetCorrespondingPrincipalProperty(np, edmProperty);
            if (((PrimitiveType)principalProperty.TypeUsage.EdmType).PrimitiveTypeKind == PrimitiveTypeKind.Binary)
            {
#>
                    if (<#=code.Escape(np)#> != null && !EqualityComparer.BinaryEquals(<#=code.Escape(np)#>.<#=code.Escape(principalProperty)#>, value))
<#
            }
            else
            {
#>
                    if (<#=code.Escape(np)#> != null && <#=code.Escape(np)#>.<#=code.Escape(principalProperty)#> != value)
<#
            }
#>
                    {
<#
            if (!(np.GetDependentProperties().Where(p=>ef.IsNullable(p)).Any() &&
                  np.GetDependentProperties().Count() > 1))
            {
#>
                        <#=code.Escape(np)#> = null;
<#
            }
            else
            {
#>
                        var previousValue = <#=code.FieldName(np)#>;
                        <#=code.FieldName(np)#> = null;
                        Fixup<#=np.Name#>(previousValue, skipKeys: true);
                        OnNavigationPropertyChanged("<#=np.Name#>");
<#
            }
#>
                    }
<#
        }
        if (hasDependentProperties)
        {
#>
                }
<#
        }
#>
                PropertySetterEntry("<#=edmProperty.Name#>");
                <#=code.FieldName(edmProperty)#> = value;
                PropertySetterExit("<#=edmProperty.Name#>", value);
                OnPropertyChanged("<#=edmProperty.Name#>");
            }
        }
    }
    private <#=code.Escape(edmProperty.TypeUsage)#> <#=code.FieldName(edmProperty)#><#=code.StringBefore(" = ", code.CreateLiteral(edmProperty.DefaultValue))#>;
<#
    }
    region.End();

    region.Begin("Complex Properties");

    foreach(EdmProperty edmProperty in entity.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == entity))
    {
#>

    [DataMember]
    <#=Accessibility.ForProperty(edmProperty)#> <#=code.Escape(edmProperty.TypeUsage)#> <#=code.Escape(edmProperty)#>
    {
        <#=code.SpaceAfter(Accessibility.ForGetter(edmProperty))#>get
        {
            if (!<#=InitializedTrackingField(edmProperty, code)#> && <#=code.FieldName(edmProperty)#> == null)
            {
                <#=code.FieldName(edmProperty)#> = new <#=code.Escape(edmProperty.TypeUsage)#>();
                ((INotifyComplexPropertyChanging)<#=code.FieldName(edmProperty)#>).ComplexPropertyChanging += Handle<#=edmProperty.Name#>Changing;
#if (SILVERLIGHT || WPF)
                ((INotifyValidationResultChanged)<#=code.FieldName(edmProperty)#>).ValidationResultChanged += Handle<#=edmProperty.Name#>ValidationResultChanged;
#endif
            }
            <#=InitializedTrackingField(edmProperty, code)#> = true;
            return <#=code.FieldName(edmProperty)#>;
        }
        <#=code.SpaceAfter(Accessibility.ForSetter(edmProperty))#>set
        {
            <#=InitializedTrackingField(edmProperty, code)#> = true;
            if (!Equals(<#=code.FieldName(edmProperty)#>, value))
            {
                if (<#=code.FieldName(edmProperty)#> != null)
                {
                    ((INotifyComplexPropertyChanging)<#=code.FieldName(edmProperty)#>).ComplexPropertyChanging -= Handle<#=edmProperty.Name#>Changing;
#if (SILVERLIGHT || WPF)
                    ((INotifyValidationResultChanged)<#=code.FieldName(edmProperty)#>).ValidationResultChanged -= Handle<#=edmProperty.Name#>ValidationResultChanged;
#endif
                }

                Handle<#=edmProperty.Name#>Changing(this, null);
                PropertySetterEntry("<#=edmProperty.Name#>");
                <#=code.FieldName(edmProperty)#> = value;
                PropertySetterExit("<#=edmProperty.Name#>", value);
                OnPropertyChanged("<#=edmProperty.Name#>");

                if (value != null)
                {
                    ((INotifyComplexPropertyChanging)<#=code.FieldName(edmProperty)#>).ComplexPropertyChanging += Handle<#=edmProperty.Name#>Changing;
#if (SILVERLIGHT || WPF)
                    ((INotifyValidationResultChanged)<#=code.FieldName(edmProperty)#>).ValidationResultChanged += Handle<#=edmProperty.Name#>ValidationResultChanged;
#endif
                }
            }
        }
    }
    private <#=code.Escape(edmProperty.TypeUsage)#> <#=code.FieldName(edmProperty)#>;
    private bool <#=InitializedTrackingField(edmProperty, code)#>;
<#
    }

    region.End();

    ////////
    //////// Write Navigation properties -------------------------------------------------------------------------------------------
    ////////

    region.Begin("Navigation Properties");

    foreach (NavigationProperty navProperty in entity.NavigationProperties.Where(np => np.DeclaringType == entity))
    {
        NavigationProperty inverse = ef.Inverse(navProperty);
        if (inverse != null &&  !IsReadWriteAccessibleProperty(inverse))
        {
            inverse = null;
        }
#>

    [DataMember]
<#
        if (navProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
        {
#>
    <#=Accessibility.ForReadOnlyProperty(navProperty)#> TrackableCollection<<#=code.Escape(navProperty.ToEndMember.GetEntityType())#>> <#=code.Escape(navProperty)#>
    {
        get
        {
            if (<#=code.FieldName(navProperty)#> == null)
            {
                <#=code.FieldName(navProperty)#> = new TrackableCollection<<#=code.Escape(navProperty.ToEndMember.GetEntityType())#>>();
                <#=code.FieldName(navProperty)#>.CollectionChanged += Fixup<#=navProperty.Name#>;
            }
            return <#=code.FieldName(navProperty)#>;
        }
        set
        {
            if (!ReferenceEquals(<#=code.FieldName(navProperty)#>, value))
            {
                if (ChangeTracker.ChangeTrackingEnabled)
                {
                    throw new InvalidOperationException("Cannot set the FixupChangeTrackingCollection when ChangeTracking is enabled");
                }
                if (<#=code.FieldName(navProperty)#> != null)
                {
                    <#=code.FieldName(navProperty)#>.CollectionChanged -= Fixup<#=navProperty.Name#>;
<#
        if (ef.IsCascadeDeletePrincipal(navProperty))
        {
#>
                    // This is the principal end in an association that performs cascade deletes.
                    // Remove the cascade delete event handler for any entities in the current collection.
                    foreach (<#=code.Escape(navProperty.ToEndMember.GetEntityType())#> item in <#=code.FieldName(navProperty)#>)
                    {
                        ChangeTracker.ObjectStateChanging -= item.HandleCascadeDelete;
                    }
<#
        }
#>
                }
                <#=code.FieldName(navProperty)#> = value;
                if (<#=code.FieldName(navProperty)#> != null)
                {
                    <#=code.FieldName(navProperty)#>.CollectionChanged += Fixup<#=navProperty.Name#>;
<#
        if (ef.IsCascadeDeletePrincipal(navProperty))
        {
#>
                    // This is the principal end in an association that performs cascade deletes.
                    // Add the cascade delete event handler for any entities that are already in the new collection.
                    foreach (<#=code.Escape(navProperty.ToEndMember.GetEntityType())#> item in <#=code.FieldName(navProperty)#>)
                    {
                        ChangeTracker.ObjectStateChanging += item.HandleCascadeDelete;
                    }
<#
        }
#>
                }
                OnNavigationPropertyChanged("<#=navProperty.Name#>");
            }
        }
    }
    private TrackableCollection<<#=code.Escape(navProperty.ToEndMember.GetEntityType())#>> <#=code.FieldName(navProperty)#>;
<#
        }
        else
        {
#>
    <#=Accessibility.ForProperty(navProperty)#> <#=code.Escape(navProperty.ToEndMember.GetEntityType())#> <#=code.Escape(navProperty)#>
    {
        <#=code.SpaceAfter(Accessibility.ForGetter(navProperty))#>get { return <#=code.FieldName(navProperty)#>; }
        <#=code.SpaceAfter(Accessibility.ForSetter(navProperty))#>set
        {
            if (!ReferenceEquals(<#=code.FieldName(navProperty)#>, value))
            {
<#
            // If this is the dependent end of an identifying relationship, the principal end can only be changed if the dependent is in the Added state and the principal's key matches the foreign key on the dependent
            if (ef.IsPrincipalEndOfIdentifyingRelationship((AssociationEndMember)navProperty.ToEndMember))
            {
#>
                if (ChangeTracker.ChangeTrackingEnabled && ChangeTracker.State != ObjectState.Added && value != null)
                {
<#
                List<EdmProperty> dependents = navProperty.GetDependentProperties().ToList();
                int dependentCount = dependents.Count;
                StringBuilder keyMatchCondition = new StringBuilder();
                for (int i = 0; i < dependentCount; i++)
                {
                    EdmProperty dependentProperty = dependents[i];
                    EdmProperty principalProperty = ef.GetCorrespondingPrincipalProperty(navProperty, dependentProperty);
                    string escapedDependent = code.Escape(dependentProperty);
                    string escapedPrincipal = code.Escape(principalProperty);

                    if (i > 0)
                    {
                        keyMatchCondition.AppendFormat(" || ");
                    }

                    string equality = null;
                    if (((PrimitiveType)principalProperty.TypeUsage.EdmType).PrimitiveTypeKind == PrimitiveTypeKind.Binary)
                    {
                        equality = "!EqualityComparer.BinaryEquals({0}, value.{1})";
                    }
                    else
                    {
                        equality = "{0} != value.{1}";
                    }
                    keyMatchCondition.AppendFormat(CultureInfo.InvariantCulture, equality, escapedDependent, escapedPrincipal);
                }
#>
                    // This the dependent end of an identifying relationship, so the principal end cannot be changed if it is already set,
                    // otherwise it can only be set to an entity with a primary key that is the same value as the dependent's foreign key.
                    if (<#=keyMatchCondition.ToString()#>)
                    {
                        throw new InvalidOperationException("The principal end of an identifying relationship can only be changed when the dependent end is in the Added state.");
                    }
                }
<#
            }
#>
                var previousValue = <#=code.FieldName(navProperty)#>;
                PropertySetterEntry("<#=navProperty.Name#>");
                <#=code.FieldName(navProperty)#> = value;
                PropertySetterExit("<#=navProperty.Name#>", value);
                Fixup<#=navProperty.Name#>(previousValue);
                OnNavigationPropertyChanged("<#=navProperty.Name#>");
            }
        }
    }
    private <#=code.Escape(navProperty.ToEndMember.GetEntityType())#> <#=code.FieldName(navProperty)#>;
<#
        }
    }
    region.End();

    region.Begin("ChangeTracking");
    if (entity.BaseType == null)
    {
#>

    protected virtual void OnPropertyChanged(String propertyName)
    {
        if (ChangeTracker.State != ObjectState.Added && ChangeTracker.State != ObjectState.Deleted)
        {
            ChangeTracker.State = ObjectState.Modified;
        }
        if (_propertyChanged != null)
        {
            _propertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    protected virtual void OnNavigationPropertyChanged(String propertyName)
    {
        if (_propertyChanged != null)
        {
            _propertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged{ add { _propertyChanged += value; } remove { _propertyChanged -= value; } }
    private event PropertyChangedEventHandler _propertyChanged;
    private ObjectChangeTracker _changeTracker;

    [DataMember]
    public ObjectChangeTracker ChangeTracker
    {
        get
        {
            if (_changeTracker == null)
            {
                _changeTracker = new ObjectChangeTracker();
                _changeTracker.SetParentObject(this);
                _changeTracker.ObjectStateChanging += HandleObjectStateChanging;
                _changeTracker.ObjectStateChanged += HandleObjectStateChanged;
                _changeTracker.UpdateHasChanges += HandleUpdateHasChanges;
            }
            return _changeTracker;
        }
        set
        {
            if(_changeTracker != null)
            {
                _changeTracker.ObjectStateChanging -= HandleObjectStateChanging;
                _changeTracker.ObjectStateChanged -= HandleObjectStateChanged;
                _changeTracker.UpdateHasChanges -= HandleUpdateHasChanges;
            }
            _changeTracker = value;
            _changeTracker.SetParentObject(this);
            if(_changeTracker != null)
            {
                _changeTracker.ObjectStateChanging += HandleObjectStateChanging;
                _changeTracker.ObjectStateChanged += HandleObjectStateChanged;
                _changeTracker.UpdateHasChanges += HandleUpdateHasChanges;
            }
        }
    }

    private void HandleObjectStateChanging(object sender, ObjectStateChangingEventArgs e)
    {
        if (e.NewState == ObjectState.Deleted)
        {
            ClearNavigationProperties();
        }
    }

    private void HandleObjectStateChanged(object sender, ObjectStateChangedEventArgs e)
    {
#if (SILVERLIGHT || WPF)
        // update HasChanges property
        HasChanges = (ChangeTracker.State == ObjectState.Added) ||
            (ChangeTracker.ChangeTrackingEnabled &&
            (ChangeTracker.State != ObjectState.Unchanged ||
             ChangeTracker.ObjectsAddedToCollectionProperties.Count != 0 ||
             ChangeTracker.ObjectsRemovedFromCollectionProperties.Count != 0));
#endif
    }

    private void HandleUpdateHasChanges(object sender, EventArgs e)
    {
#if (SILVERLIGHT || WPF)
        // update HasChanges property
        HasChanges = (ChangeTracker.State == ObjectState.Added) ||
            (ChangeTracker.ChangeTrackingEnabled &&
            (ChangeTracker.State != ObjectState.Unchanged ||
             ChangeTracker.ObjectsAddedToCollectionProperties.Count != 0 ||
             ChangeTracker.ObjectsRemovedFromCollectionProperties.Count != 0));
#endif
    }
<#
    // If this entity type participates in any relationships where the other end has an OnDelete
    // cascade delete defined, or if it is the dependent in any identifying relationships, it needs
    // an event handler to handle notifications that are fired when the parent is deleted.
    if (ItemCollection.GetItems<AssociationType>().Where(a =>
        ((RefType)a.AssociationEndMembers[0].TypeUsage.EdmType).ElementType == entity && ef.IsCascadeDeletePrincipal(a.AssociationEndMembers[1]) ||
        ((RefType)a.AssociationEndMembers[1].TypeUsage.EdmType).ElementType == entity && ef.IsCascadeDeletePrincipal(a.AssociationEndMembers[0])).Any())
    {
#>

    // This entity type is the dependent end in at least one association that performs cascade deletes.
    // This event handler will process notifications that occur when the principal end is deleted.
    internal void HandleCascadeDelete(object sender, ObjectStateChangingEventArgs e)
    {
        if (e.NewState == ObjectState.Deleted)
        {
            this.MarkAsDeleted();
        }
    }
<#
    }
#>

    protected bool IsDeserializing { get; private set; }

    [OnDeserializing]
    public void OnDeserializingMethod(StreamingContext context)
    {
        IsDeserializing = true;
    }

    [OnDeserialized]
    public void OnDeserializedMethod(StreamingContext context)
    {
        IsDeserializing = false;
        ChangeTracker.ChangeTrackingEnabled = true;
    }
<#
    }

    foreach(EdmProperty edmProperty in entity.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == entity))
    {
#>

    // <#=String.Format(CultureInfo.CurrentCulture, "Records the original values for the complex property {0}", edmProperty.Name)#>
    private void Handle<#=edmProperty.Name#>Changing(object sender, EventArgs args)
    {
        if (ChangeTracker.State != ObjectState.Added && ChangeTracker.State != ObjectState.Deleted)
        {
            ChangeTracker.State = ObjectState.Modified;
        }
<#
        if (originalValueMembers.IsOriginalValueMember(edmProperty))
        {
#>
        ChangeTracker.RecordComplexTypeOriginalValue("<#=edmProperty.Name#>", <#=code.FieldName(edmProperty)#>);
        <#=code.Escape(edmProperty.TypeUsage)#>.RecordComplexOriginalValues("<#=edmProperty.Name#>", this.<#=code.Escape(edmProperty)#>, ChangeTracker);
<#
        }
#>
    }
<#
    }

    List<AssociationEndMember> shadowAssociationEnds = new List<AssociationEndMember>();
    foreach(var association in ItemCollection.GetItems<AssociationType>().Where(x => !IsForeignKeyOrIdentifyingRelationship(ef, x) &&
                                                                                ((((RefType)x.AssociationEndMembers[0].TypeUsage.EdmType).ElementType == entity &&
                                                                                   x.AssociationEndMembers[0].RelationshipMultiplicity != RelationshipMultiplicity.One &&
                                                                                   x.AssociationEndMembers[1].RelationshipMultiplicity != RelationshipMultiplicity.Many) ||
                                                                                 ((RefType)x.AssociationEndMembers[1].TypeUsage.EdmType).ElementType == entity &&
                                                                                   x.AssociationEndMembers[1].RelationshipMultiplicity != RelationshipMultiplicity.One &&
                                                                                   x.AssociationEndMembers[0].RelationshipMultiplicity != RelationshipMultiplicity.Many)))
    {
        if (!entity.NavigationProperties.Any(x => x.RelationshipType == association && IsReadWriteAccessibleProperty(x)))
        {
            for (int i = 0; i < 2; i++)
            {
                int targetRoleIndex = 0;
                if (((RefType)association.AssociationEndMembers[i].TypeUsage.EdmType).ElementType == entity)
                {
                    targetRoleIndex = (i + 1) % 2;
                    shadowAssociationEnds.Add(association.AssociationEndMembers[targetRoleIndex]);
                }
            }
        }
    }
#>

    protected <#=entity.BaseType == null ? "virtual " : "override " #>void ClearNavigationProperties()
    {
<#
    if (entity.BaseType != null)
    {
#>
        base.ClearNavigationProperties();
<#
    }
    foreach (NavigationProperty navProperty in entity.NavigationProperties.Where(np => np.DeclaringType == entity))
    {
        if (navProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
        {
#>
        <#=code.Escape(navProperty)#>.Clear();
<#
        }
        else
        {
#>
        <#=code.Escape(navProperty)#> = null;
<#
            if (IsSaveReference(ef, navProperty))
            {
#>
        Fixup<#=navProperty.Name#>Keys();
<#
            }
        }
    }
    foreach(var associationEnd in shadowAssociationEnds)
    {
        AssociationType association = associationEnd.DeclaringType as AssociationType;
#>
        <#=CreateFixupMethodName(associationEnd)#>(null, true);
<#
    }
#>
    }
<#
    region.End();

    region.Begin("Validation and Helper Method");
#>

    /// <summary>
    /// Loops through all related data annotation attributes as well as all related
    /// custom validation actions for the specified property name. If  propertyName
    /// is String.Empty/null, the validation is on the entity level.
    /// </summary>
    /// <param name="propertyName">Propery name or String.Empty/null for top-level errors</param>
    /// <param name="value">New value that needs to be validated for the specified property</param>
    public <#=entity.BaseType == null ? "virtual " : "override " #>void Validate(string propertyName, object value)
    {
<#
    if (entity.BaseType != null)
    {
#>
        // call base Validate() first
        base.Validate(propertyName, value);
<#
    }
    else
    {
#>
        // process Data Annotation Attributes
        var validationResults = new Collection<ValidationResult>();
        if (string.IsNullOrEmpty(propertyName))
        {
            // this is top-level validation
            ValidationContext context = new ValidationContext(this, null, null);
            Validator.TryValidateObject(this, context, validationResults, true);
        }
        else
        {
            PropertyInfo property = GetType().GetProperty(propertyName);
            if (property != null)
            {
                // this is validation for each property
                ValidationContext context = new ValidationContext(this, null, null) { MemberName = propertyName };
                Validator.TryValidateProperty(value, context, validationResults);
            }
        }
        if (validationResults.Count != 0)
        {
#if (SILVERLIGHT || WPF)
            foreach (ValidationResult error in validationResults)
            {
                if (error.MemberNames.Count() == 0)
                {
                    // this is top-level validation error
                    AddError(string.Empty, error);
                }
                else
                {
                    foreach (string memberName in error.MemberNames)
                    {
                        AddError(memberName, error);
                    }
                }
            }
#else
            FaultReason faultReason = new FaultReason(validationResults.First().ErrorMessage);
            throw new FaultException(faultReason);
#endif
        }
<#
    }
#>
        // next, process custom validation actions
        if (ValidationActions.Count == 0) InitializeValidationSettings();
        if (string.IsNullOrEmpty(propertyName))
        {
            // loop through all properties and call its list of validation actions
            foreach (string memberName in ValidationActions.Keys.ToList())
            {
                List<Action<object>> actions = ValidationActions[memberName];
                if (string.IsNullOrEmpty(memberName))
                {
                    // this is top-level validation
                    actions.ForEach(action => action(this));
                }
                else
                {
                    // this is validation for each property
                    object memberValue = GetType().GetProperty(memberName).GetValue(this, null);
                    actions.ForEach(action => action(memberValue));
                }
            }
<#
    if (entity.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == entity).Count() > 0)
    {
#>
            // validate all Complex Properties
<#
    }
    foreach(EdmProperty edmProperty in entity.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == entity))
    {
#>
            <#=code.Escape(edmProperty)#>.Validate(string.Empty, <#=code.Escape(edmProperty)#>);
<#
    }
#>
        }
        else
        {
            if (ValidationActions.ContainsKey(propertyName))
            {
                List<Action<object>> actions = ValidationActions[propertyName];
                actions.ForEach(action => action(value));
            }
<#
    if (entity.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == entity).Count() > 0)
    {
#>
            // check whether propertyName is a Complex Property
<#
    }
    foreach(EdmProperty edmProperty in entity.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == entity))
    {
#>
            if (propertyName == "<#=edmProperty.Name#>")
            {
                <#=code.Escape(edmProperty)#>.Validate(string.Empty, value);
            }
<#
    }
#>
        }
    }

#if SILVERLIGHT
    /// <summary>
    /// Client-side validation function to loop through all data annotation
    /// attributes and all custom validation actions.  If any validation fails,
    /// this function will return false. Otherwise, true.
    /// </summary>
    /// <returns></returns>
    public <#=entity.BaseType == null ? "virtual " : "override " #>bool TryValidate()
    {
<#
    if (entity.BaseType != null)
    {
#>
        // call base TryValidate() first
        base.TryValidate();
<#
    }
    else
    {
#>
        // clear known errors for all property names.
        ClearErrors();
        // process Data Annotation Attributes
        ValidationContext context = new ValidationContext(this, null, null);
        var validationResults = new Collection<ValidationResult>();
        Validator.TryValidateObject(this, context, validationResults, true);
        if (validationResults.Count != 0)
        {
            foreach (ValidationResult error in validationResults)
            {
                if (error.MemberNames.Count() == 0)
                {
                    // this is top-level validation error
                    AddError(string.Empty, error);
                }
                else
                {
                    foreach (string propertyName in error.MemberNames)
                    {
                        AddError(propertyName, error);
                    }
                }
            }
        }
<#
    }
#>
        // next, process custom validation actions
        if (ValidationActions.Count == 0) InitializeValidationSettings();
        // loop through all properties and call its list of validation actions
        foreach (string propertyName in ValidationActions.Keys.ToList())
        {
            List<Action<object>> actions = ValidationActions[propertyName];
            if (string.IsNullOrEmpty(propertyName))
            {
                // this is top-level validation
                actions.ForEach(action => action(this));
            }
            else
            {
                // this is validation for each property
                object value = GetType().GetProperty(propertyName).GetValue(this, null);
                actions.ForEach(action => action(value));
            }
        }
<#
    if (entity.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == entity).Count() > 0)
    {
#>
        // Last, call TryValidate() on all Complex Properties
<#
    }
    foreach(EdmProperty edmProperty in entity.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == entity))
    {
#>
        <#=code.Escape(edmProperty)#>.TryValidate();
<#
    }
#>
        return !(((INotifyDataErrorInfo)this).HasErrors);
    }
<#
    if (entity.BaseType == null)
    {
#>

    /// <summary>
    /// Client-side validation function for propertyName.
    /// If any validation fails, this function will return false.
    /// Otherwise, true.
    /// </summary>
    /// <param name="propertyName"></param>
    /// <returns></returns>
    public bool TryValidate(string propertyName)
    {
        if (string.IsNullOrEmpty(propertyName))
        {
            return TryValidate();
        }
        PropertyInfo property = GetType().GetProperty(propertyName);
        if (property == null)
        {
            throw new ArgumentException(propertyName + " does not exist.");
        }
        // clear known errors for the property name
        ClearErrors(propertyName);
        // get the value of propertyName
        object value = GetType().GetProperty(propertyName).GetValue(this, null);
        Validate(propertyName, value);
        IEnumerable errors = ((INotifyDataErrorInfo)this).GetErrors(propertyName);
        if (errors == null) return true;
        int count = errors.Cast<object>().Count();
        return count == 0;
    }

    /// <summary>
    /// Client-side validation function to loop through the whole object
    /// graph.  If any validation fails, this function will return false. 
    /// Otherwise, true.
    /// </summary>
    /// <returns></returns>
    public bool TryValidateObjectGraph()
    {
        bool validated = true;
        var visitedGraph = new List<object>();
        TryValidateObjectGraph(ref validated, ref visitedGraph);
        return validated;
    }
<#
    }
#>

    internal <#=entity.BaseType == null ? "virtual " : "override " #>void TryValidateObjectGraph(ref bool validated, ref List<object> visitedGraph)
    {
        // if already visited this object, just return
        if (visitedGraph.Any(n => ReferenceEquals(n, this))) return;

        if (!TryValidate()) validated = false;
        visitedGraph.Add(this);

        // call TryValidateObjectGraph() on all Navigation properties
        TryValidateOnNavigationProperties(ref validated, ref visitedGraph);
    }

<#
    if (entity.BaseType == null)
    {
#>
    internal virtual void TryValidateOnNavigationProperties(ref bool validated, ref List<object> visitedGraph)
    {
<#
        foreach (NavigationProperty navProperty in entity.NavigationProperties.Where(np => np.DeclaringType == entity))
        {
            if (navProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
            {
#>
        foreach (var item in <#=code.Escape(navProperty)#>)
        {
            item.TryValidateObjectGraph(ref validated, ref visitedGraph);
        }
<#
            }
            else
            {
#>
        if (<#=code.Escape(navProperty)#> != null)
        {
            <#=code.Escape(navProperty)#>.TryValidateObjectGraph(ref validated, ref visitedGraph);
        }
<#
            }
        }
#>
    }
<#
    }
    else
    {
        if (entity.NavigationProperties.Where(np => np.DeclaringType == entity).Count() > 0)
        {
#>
    internal override void TryValidateOnNavigationProperties(ref bool validated, ref List<object> visitedGraph)
    {
        // call base TryValidateOnNavigationProperties() first
        base.TryValidateOnNavigationProperties(ref validated, ref visitedGraph);
<#
            foreach (NavigationProperty navProperty in entity.NavigationProperties.Where(np => np.DeclaringType == entity))
            {
                if (navProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
                {
#>
        foreach (var item in <#=code.Escape(navProperty)#>)
        {
            item.TryValidateObjectGraph(ref validated, ref visitedGraph);
        }
<#
                }
                else
                {
#>
        if (<#=code.Escape(navProperty)#> != null)
        {
            <#=code.Escape(navProperty)#>.TryValidateObjectGraph(ref validated, ref visitedGraph);
        }
<#
                }
            }
#>
    }
<#
        }
    }
#>
#elif WPF
    /// <summary>
    /// Client-side validation function to loop through all data annotation
    /// attributes and all custom validation actions.  If any validation fails,
    /// this function will return false. Otherwise, true.
    /// </summary>
    /// <returns></returns>
    public <#=entity.BaseType == null ? "virtual " : "override " #>bool TryValidate()
    {
<#
    if (entity.BaseType != null)
    {
#>
        // call base TryValidate() first
        base.TryValidate();
<#
    }
    else
    {
#>
        // clear known errors for all property names.
        ClearErrors();
        // First, process Data Annotation Attributes
        ValidationContext context = new ValidationContext(this, null, null);
        var validationResults = new Collection<ValidationResult>();
        Validator.TryValidateObject(this, context, validationResults, true);
        if (validationResults.Count != 0)
        {
            foreach (ValidationResult error in validationResults)
            {
                if (error.MemberNames.Count() == 0)
                {
                    // this is top-level validation error
                    AddError(string.Empty, error);
                }
                else
                {
                    foreach (string propertyName in error.MemberNames)
                    {
                        AddError(propertyName, error);
                    }
                }
            }
        }
<#
    }
#>
        // Next, process custom validation actions
        if (ValidationActions.Count == 0) InitializeValidationSettings();
        // loop through all properties and call its list of validation actions
        foreach (string propertyName in ValidationActions.Keys.ToList())
        {
            List<Action<object>> actions = ValidationActions[propertyName];
            if (string.IsNullOrEmpty(propertyName))
            {
                // this is top-level validation
                actions.ForEach(action => action(this));
            }
            else
            {
                // this is validation for each property
                object value = GetType().GetProperty(propertyName).GetValue(this, null);
                actions.ForEach(action => action(value));
            }
        }
<#
    if (entity.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == entity).Count() > 0)
    {
#>
        // Last, call TryValidate() on all Complex Properties
<#
    }
    foreach(EdmProperty edmProperty in entity.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == entity))
    {
#>
        <#=code.Escape(edmProperty)#>.TryValidate();
<#
    }
#>
        return (ValidationErrors.Count == 0);
    }
<#
    if (entity.BaseType == null)
    {
#>

    /// <summary>
    /// Client-side validation function for propertyName.
    /// If any validation fails, this function will return false.
    /// Otherwise, true.
    /// </summary>
    /// <param name="propertyName"></param>
    /// <returns></returns>
    public bool TryValidate(string propertyName)
    {
        if (string.IsNullOrEmpty(propertyName))
        {
            return TryValidate();
        }
        PropertyInfo property = GetType().GetProperty(propertyName);
        if (property == null)
        {
            throw new ArgumentException(propertyName + " does not exist.");
        }
        // clear known errors for the property name
        ClearErrors(propertyName);
        // get the value of propertyName
        object value = GetType().GetProperty(propertyName).GetValue(this, null);
        Validate(propertyName, value);
        return ValidationErrors.ContainsKey(propertyName);
    }

    /// <summary>
    /// Client-side validation function to loop through the whole object
    /// graph.  If any validation fails, this function will return false. 
    /// Otherwise, true.
    /// </summary>
    /// <returns></returns>
    public bool TryValidateObjectGraph()
    {
        bool validated = true;
        var visitedGraph = new List<object>();
        TryValidateObjectGraph(ref validated, ref visitedGraph);
        return validated;
    }
<#
    }
#>

    internal <#=entity.BaseType == null ? "virtual " : "override " #>void TryValidateObjectGraph(ref bool validated, ref List<object> visitedGraph)
    {
        // if already visited this object, just return
        if (visitedGraph.Any(n => ReferenceEquals(n, this))) return;

        if (!TryValidate()) validated = false;
        visitedGraph.Add(this);

        // call TryValidateObjectGraph() on all Navigation properties
        TryValidateOnNavigationProperties(ref validated, ref visitedGraph);
    }

<#
    if (entity.BaseType == null)
    {
#>
    internal virtual void TryValidateOnNavigationProperties(ref bool validated, ref List<object> visitedGraph)
    {
<#
        foreach (NavigationProperty navProperty in entity.NavigationProperties.Where(np => np.DeclaringType == entity))
        {
            if (navProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
            {
#>
        foreach (var item in <#=code.Escape(navProperty)#>)
        {
            item.TryValidateObjectGraph(ref validated, ref visitedGraph);
        }
<#
            }
            else
            {
#>
        if (<#=code.Escape(navProperty)#> != null)
        {
            <#=code.Escape(navProperty)#>.TryValidateObjectGraph(ref validated, ref visitedGraph);
        }
<#
            }
        }
#>
    }
<#
    }
    else
    {
        if (entity.NavigationProperties.Where(np => np.DeclaringType == entity).Count() > 0)
        {
#>
    internal override void TryValidateOnNavigationProperties(ref bool validated, ref List<object> visitedGraph)
    {
        // call base TryValidateOnNavigationProperties() first
        base.TryValidateOnNavigationProperties(ref validated, ref visitedGraph);
<#
            foreach (NavigationProperty navProperty in entity.NavigationProperties.Where(np => np.DeclaringType == entity))
            {
                if (navProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
                {
#>
        foreach (var item in <#=code.Escape(navProperty)#>)
        {
            item.TryValidateObjectGraph(ref validated, ref visitedGraph);
        }
<#
                }
                else
                {
#>
        if (<#=code.Escape(navProperty)#> != null)
        {
            <#=code.Escape(navProperty)#>.TryValidateObjectGraph(ref validated, ref visitedGraph);
        }
<#
                }
            }
#>
    }
<#
        }
    }
#>
#else
    /// <summary>
    /// Server-side validation function to loop through all data annotation
    /// attributes and all custom validation actions.  If any validation fails,
    /// it will throw an exception.
    /// </summary>
    public <#=entity.BaseType == null ? "virtual " : "override " #>void Validate()
    {
<#
    if (entity.BaseType != null)
    {
#>
        // call base Validate() first
        base.Validate();
<#
    }
    else
    {
#>
        // process Data Annotation Attributes
        ValidationContext context = new ValidationContext(this, null, null);
        var validationResults = new Collection<ValidationResult>();
        Validator.TryValidateObject(this, context, validationResults, true);
        if (validationResults.Count != 0)
        {
            ValidationResult error = validationResults.First();
            if (error.MemberNames.Count() == 0)
            {
                // this is top-level validation error
                FaultReason faultReason = new FaultReason(error.ErrorMessage);
                throw new FaultException(faultReason);
            }
            else
            {
                string propertyName = error.MemberNames.First();
                FaultReason faultReason = new FaultReason(propertyName + " : " + error.ErrorMessage);
                throw new FaultException(faultReason);
            }
        }
<#
    }
#>
        // Next, process custom validation actions
        if (ValidationActions.Count == 0) InitializeValidationSettings();
        // loop through all properties and call its list of validation actions
        foreach (string propertyName in ValidationActions.Keys.ToList())
        {
            List<Action<object>> actions = ValidationActions[propertyName];
            if (string.IsNullOrEmpty(propertyName))
            {
                // this is top-level validation
                actions.ForEach(action => action(this));
            }
            else
            {
                // this is validation for each property
                object value = GetType().GetProperty(propertyName).GetValue(this, null);
                actions.ForEach(action => action(value));
            }
        }
<#
    if (entity.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == entity).Count() > 0)
    {
#>
        // Last, validate all Complex Properties
<#
    }
    foreach(EdmProperty edmProperty in entity.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == entity))
    {
#>
        <#=code.Escape(edmProperty)#>.Validate();
<#
    }
#>
    }
<#
    if (entity.BaseType == null)
    {
#>

    /// <summary>
    /// Server-side validation function to loop through the whole object
    /// graph.  If any validation fails, it will throw an exception.
    /// </summary>
    /// <returns></returns>
    public void ValidateObjectGraph()
    {
        var visitedGraph = new List<object>();
        ValidateObjectGraph(ref visitedGraph);
    }
<#
    }
#>

    internal <#=entity.BaseType == null ? "virtual " : "override " #>void ValidateObjectGraph(ref List<object> visitedGraph)
    {
        // if already visited this object, just return
        if (visitedGraph.Any(n => ReferenceEquals(n, this))) return;

        Validate();
        visitedGraph.Add(this);

        // call ValidateObjectGraph() on all Navigation properties
        ValidateOnNavigationProperties(ref visitedGraph);
    }

<#
    if (entity.BaseType == null)
    {
#>
    internal virtual void ValidateOnNavigationProperties(ref List<object> visitedGraph)
    {
<#
        foreach (NavigationProperty navProperty in entity.NavigationProperties.Where(np => np.DeclaringType == entity))
        {
            if (navProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
            {
#>
        foreach (var item in <#=code.Escape(navProperty)#>)
        {
            item.ValidateObjectGraph(ref visitedGraph);
        }
<#
            }
            else
            {
#>
        if (<#=code.Escape(navProperty)#> != null)
        {
            <#=code.Escape(navProperty)#>.ValidateObjectGraph(ref visitedGraph);
        }
<#
            }
        }
#>
    }
<#
    }
    else
    {
        if (entity.NavigationProperties.Where(np => np.DeclaringType == entity).Count() > 0)
        {
#>
    internal override void ValidateOnNavigationProperties(ref List<object> visitedGraph)
    {
        // call base ValidateOnNavigationProperties() first
        base.ValidateOnNavigationProperties(ref visitedGraph);
<#
            foreach (NavigationProperty navProperty in entity.NavigationProperties.Where(np => np.DeclaringType == entity))
            {
                if (navProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
                {
#>
        foreach (var item in <#=code.Escape(navProperty)#>)
        {
            item.ValidateObjectGraph(ref visitedGraph);
        }
<#
                }
                else
                {
#>
        if (<#=code.Escape(navProperty)#> != null)
        {
            <#=code.Escape(navProperty)#>.ValidateObjectGraph(ref visitedGraph);
        }
<#
                }
            }
#>
    }
<#
        }
    }
#>
#endif
	partial void PropertySetterEntry(string propertyName);
    partial void PropertySetterExit(string propertyName, object propertyValue);
    partial void InitializeValidationSettings();

    private Dictionary<string, List<Action<object>>> ValidationActions
    {
        get
        {
            if (_validationActions == null)
            {
                _validationActions = new Dictionary<string, List<Action<object>>>();
            }
            return _validationActions;
        }
    }
    private Dictionary<string, List<Action<object>>> _validationActions;

    /// <summary>
    /// Add one validation action for the specified property name.
    /// </summary>
    /// <param name="propertyName">Propery name or String.Empty/null for top-level errors</param>
    /// <param name="validationAction">Function to carry out a specific validation action</param>
    private void AddPropertyValidationAction(string propertyName, Action<object> validationAction)
    {
        List<Action<object>> validationActionList;
        if (!ValidationActions.ContainsKey(propertyName))
        {
            validationActionList = new List<Action<object>>();
            ValidationActions.Add(propertyName, validationActionList);
        }
        else
            validationActionList = ValidationActions[propertyName];

        validationActionList.Add(validationAction);
    }
<#
    region.End();

    region.Begin("Association Fixup");

    foreach (NavigationProperty navProperty in entity.NavigationProperties.Where(np => np.DeclaringType == entity))
    {
        NavigationProperty inverse = ef.Inverse(navProperty);

        if (inverse != null && !IsReadWriteAccessibleProperty(inverse))
        {
            inverse = null;
        }

        if (navProperty.ToEndMember.RelationshipMultiplicity != RelationshipMultiplicity.Many)
        {
            var skipKeysArgument = navProperty.GetDependentProperties().Where(p=>ef.IsNullable(p)).Any()
                ? ", bool skipKeys = false"
                : String.Empty;
#>

    private void Fixup<#=navProperty.Name#>(<#=code.Escape(navProperty.ToEndMember.GetEntityType())#> previousValue<#= skipKeysArgument #>)
    {
<#
        if (ef.IsCascadeDeletePrincipal(navProperty))
        {
#>
        // This is the principal end in an association that performs cascade deletes.
        // Update the event listener to refer to the new dependent.
        if (previousValue != null)
        {
            ChangeTracker.ObjectStateChanging -= previousValue.HandleCascadeDelete;
        }

        if (<#=code.Escape(navProperty)#> != null)
        {
            ChangeTracker.ObjectStateChanging += <#=code.Escape(navProperty)#>.HandleCascadeDelete;
        }

<#
        }
        else if (inverse == null && ef.IsCascadeDeletePrincipal((AssociationEndMember)navProperty.ToEndMember))
        {
#>
        // This is the dependent end in an association that performs cascade deletes.
        // Update the principal's event listener to refer to the new dependent.
        // This is a unidirectional relationship from the dependent to the principal, so the dependent end is
        // responsible for managing the cascade delete event handler. In all other cases the principal end will manage it.
        if (previousValue != null)
        {
            previousValue.ChangeTracker.ObjectStateChanging -= HandleCascadeDelete;
        }

        if (<#=code.Escape(navProperty)#> != null)
        {
            <#=code.Escape(navProperty)#>.ChangeTracker.ObjectStateChanging += HandleCascadeDelete;
        }

<#
        }
#>
        if (IsDeserializing)
        {
            return;
        }

<#
        if (inverse != null)
        {
            if (inverse.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
            {
#>
        if (previousValue != null && previousValue.<#=code.Escape(inverse)#>.Contains(this))
        {
            previousValue.<#=code.Escape(inverse)#>.Remove(this);
        }
<#
            }
            else
            {
#>
        if (previousValue != null && ReferenceEquals(previousValue.<#=code.Escape(inverse)#>, this))
        {
            previousValue.<#=code.Escape(inverse)#> = null;
        }
<#
            }

            if (inverse.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
            {
#>

        if (<#=code.Escape(navProperty)#> != null)
        {
            if (!<#=code.Escape(navProperty)#>.<#=code.Escape(inverse)#>.Contains(this))
            {
                <#=code.Escape(navProperty)#>.<#=code.Escape(inverse)#>.Add(this);
            }

<#
                foreach (var dependentProperty in navProperty.GetDependentProperties())
                {
#>
            <#=code.Escape(dependentProperty)#> = <#=code.Escape(navProperty)#>.<#=code.Escape(ef.GetCorrespondingPrincipalProperty(navProperty, dependentProperty))#>;
<#
                }
#>
        }
<#
                if (navProperty.GetDependentProperties().Where(p=>ef.IsNullable(p)).Any())
                {
#>
        else if (!skipKeys)
        {
<#
                foreach (var dependentProperty in navProperty.GetDependentProperties().Where(p => ef.IsNullable(p)))
                {
#>
            <#=code.Escape(dependentProperty)#> = null;
<#
                }
#>
        }

<#
                }
            }
            else
            {
#>

        if (<#=code.Escape(navProperty)#> != null)
        {
            <#=code.Escape(navProperty)#>.<#=code.Escape(inverse)#> = this;
<#
                foreach (var dependentProperty in navProperty.GetDependentProperties())
                {
#>
            <#=code.Escape(dependentProperty)#> = <#=code.Escape(navProperty)#>.<#=code.Escape(ef.GetCorrespondingPrincipalProperty(navProperty, dependentProperty))#>;
<#
                }
#>
        }

<#
            }
        }
        else
        {
            if (navProperty.GetDependentProperties().Any())
            {
#>
        if (<#=code.Escape(navProperty)#> != null)
        {
<#
                foreach (var dependentProperty in navProperty.GetDependentProperties())
                {
#>
            <#=code.Escape(dependentProperty)#> = <#=code.Escape(navProperty)#>.<#=code.Escape(ef.GetCorrespondingPrincipalProperty(navProperty, dependentProperty))#>;
<#
                }
#>
        }

<#
                if (navProperty.GetDependentProperties().Where(p => ef.IsNullable(p)).Any())
                {
#>
        else if (!skipKeys)
        {
<#
                    foreach (var dependentProperty in navProperty.GetDependentProperties().Where(p => ef.IsNullable(p)))
                    {
#>
            <#=code.Escape(dependentProperty)#> = null;
<#
                    }
#>
        }

<#
                }
            }
            else if (IsForeignKeyOrIdentifyingRelationship(ef, navProperty))
            {
#>
        if (<#=code.Escape(navProperty)#> != null)
        {
<#
                foreach (var fromProperty in ef.GetPrincipalProperties(navProperty))
                {
#>
            <#=code.Escape(navProperty)#>.<#=code.Escape(ef.GetCorrespondingDependentProperty(navProperty, fromProperty))#> = <#=code.Escape(fromProperty)#>;
<#
                }
#>
        }

<#
            }
        }
#>
        if (ChangeTracker.ChangeTrackingEnabled)
        {
            if (ChangeTracker.OriginalValues.ContainsKey("<#=navProperty.Name#>")
                && (ChangeTracker.OriginalValues["<#=navProperty.Name#>"] == <#=code.Escape(navProperty)#>))
            {
                ChangeTracker.OriginalValues.Remove("<#=navProperty.Name#>");
            }
            else
            {
                ChangeTracker.RecordOriginalValue("<#=navProperty.Name#>", previousValue);
<#
        if (ef.IsPrincipalEndOfIdentifyingRelationship((AssociationEndMember)navProperty.FromEndMember))
        {
#>
                // This is the principal end of an identifying association, so the dependent must be deleted when the relationship is removed.
                // If the current state of the dependent is Added, the relationship can be changed without causing the dependent to be deleted.
                if (previousValue != null && previousValue.ChangeTracker.State != ObjectState.Added)
                {
                    previousValue.MarkAsDeleted();
                }
<#
        }
        else if (inverse == null && ef.IsPrincipalEndOfIdentifyingRelationship((AssociationEndMember)navProperty.ToEndMember))
        {
#>
                // This is the dependent end of an identifying association, so it must be deleted when the relationship is
                // removed. If the current state is Added, the relationship can be changed without causing the dependent to be deleted.
                // This is a unidirectional relationship from the dependent to the principal, so the dependent end is
                // responsible for cascading the delete. In all other cases the principal end will manage it.
                if (previousValue != null && ChangeTracker.State != ObjectState.Added)
                {
                    this.MarkAsDeleted();
                }
<#
        }
#>
            }
            if (<#=code.Escape(navProperty)#> != null && !<#=code.Escape(navProperty)#>.ChangeTracker.ChangeTrackingEnabled)
            {
                <#=code.Escape(navProperty)#>.StartTracking();
            }
<#
        if (IsSaveReference(ef, navProperty))
        {
#>
            Fixup<#=navProperty.Name#>Keys();
<#
        }
        if (inverse == null &&
            !IsForeignKeyOrIdentifyingRelationship(ef, navProperty) &&
            navProperty.FromEndMember.RelationshipMultiplicity != RelationshipMultiplicity.Many &&
            navProperty.ToEndMember.RelationshipMultiplicity != RelationshipMultiplicity.One)
        {
#>
            if (previousValue != null)
            {
                previousValue.<#=CreateFixupMethodName(navProperty.FromEndMember)#>(null, false);
            }
            if (<#=code.Escape(navProperty)#> != null)
            {
                <#=code.Escape(navProperty)#>.<#=CreateFixupMethodName(navProperty.FromEndMember)#>(this, false);
            }
<#
        }
#>
        }
    }
<#
        if (IsSaveReference(ef, navProperty))
        {
            EntityType targetType = (EntityType)navProperty.TypeUsage.EdmType;
            List<string> keyNames = targetType.KeyMembers.Select(x => x.Name).ToList();
#>

    private void Fixup<#=navProperty.Name#>Keys()
    {
<#
            for(int k=0; k < keyNames.Count; k++)
            {
#>
        const string <#=CreateKeyNameVariable(code.Escape(keyNames[k]))#> = "<#=CreateReferenceValueLookupKey(navProperty, keyNames[k])#>";
<#
            }
#>

        if(ChangeTracker.ExtendedProperties.ContainsKey(<#=CreateKeyNameVariable(code.Escape(keyNames[0]))#>)<#=keyNames.Count > 1 ? " &&" : ")"#>
<#
            for(int k=1; k < keyNames.Count; k++)
            {
#>
           ChangeTracker.ExtendedProperties.ContainsKey(<#=CreateKeyNameVariable(code.Escape(keyNames[k]))#>)<#=k < keyNames.Count - 1 ? " &&" : ")" #>
<#
            }
#>
        {
            if(<#=code.Escape(navProperty)#> == null ||
<#
            for(int k=0; k < keyNames.Count; k++)
            {
                string equality = ((PrimitiveType)targetType.KeyMembers[keyNames[k]].TypeUsage.EdmType).PrimitiveTypeKind == PrimitiveTypeKind.Binary ? "EqualityComparer.Binary" : String.Empty;
#>
               !<#=equality#>Equals(ChangeTracker.ExtendedProperties[<#=CreateKeyNameVariable(code.Escape(keyNames[k]))#>], <#=code.Escape(navProperty)#>.<#=code.Escape(keyNames[k])#>)<#=k < keyNames.Count - 1 ? " ||" : ")" #>
<#
            }
#>
            {
<#
            for(int k=0; k < keyNames.Count; k++)
            {
#>
                ChangeTracker.RecordOriginalValue(<#=CreateKeyNameVariable(code.Escape(keyNames[k]))#>, ChangeTracker.ExtendedProperties[<#=CreateKeyNameVariable(code.Escape(keyNames[k]))#>]);
<#
            }
#>
            }
<#
            for(int k=0; k < keyNames.Count; k++)
            {
#>
            ChangeTracker.ExtendedProperties.Remove(<#=CreateKeyNameVariable(code.Escape(keyNames[k]))#>);
<#
            }
#>
        }
    }
<#
            }
        }
    }

    foreach (NavigationProperty navProperty in entity.NavigationProperties.Where(np => np.DeclaringType == entity))
    {
        NavigationProperty inverse = ef.Inverse(navProperty);

        if (inverse != null && !IsReadWriteAccessibleProperty(inverse))
        {
            inverse = null;
        }

        if (navProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
        {
#>

    private void Fixup<#=navProperty.Name#>(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (IsDeserializing)
        {
            return;
        }

        if (e.NewItems != null)
        {
            foreach (<#=code.Escape(navProperty.ToEndMember.GetEntityType())#> item in e.NewItems)
            {
<#
                if (inverse != null)
                {
                    if (inverse.ToEndMember.RelationshipMultiplicity != RelationshipMultiplicity.Many)
                    {
#>
                item.<#=code.Escape(inverse)#> = this;
<#
                    }
                    else
                    {
#>
                if (!item.<#=code.Escape(inverse)#>.Contains(this))
                {
                    item.<#=code.Escape(inverse)#>.Add(this);
                }
<#
                    }
                }
                else if (IsForeignKeyOrIdentifyingRelationship(ef, navProperty))
                {
                    foreach (var fromProperty in ef.GetPrincipalProperties(navProperty))
                    {
#>
                item.<#=code.Escape(ef.GetCorrespondingDependentProperty(navProperty, fromProperty))#> = <#=code.Escape(fromProperty)#>;
<#
                    }
                }
                else if (navProperty.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne)
                {
#>
                item.<#=CreateFixupMethodName(navProperty.FromEndMember)#>(this, false);
<#
                }
#>
                if (ChangeTracker.ChangeTrackingEnabled)
                {
                    if (!item.ChangeTracker.ChangeTrackingEnabled)
                    {
                        item.StartTracking();
                    }
                    ChangeTracker.RecordAdditionToCollectionProperties("<#=code.Escape(navProperty)#>", item);
                }
<#
                if (ef.IsCascadeDeletePrincipal(navProperty))
                {
#>
                // This is the principal end in an association that performs cascade deletes.
                // Update the event listener to refer to the new dependent.
                ChangeTracker.ObjectStateChanging += item.HandleCascadeDelete;
<#
                }
#>
            }
        }

        if (e.OldItems != null)
        {
            foreach (<#=code.Escape(navProperty.ToEndMember.GetEntityType())#> item in e.OldItems)
            {
<#
                if (inverse != null)
                {
                    if (inverse.ToEndMember.RelationshipMultiplicity != RelationshipMultiplicity.Many)
                    {
#>
                if (ReferenceEquals(item.<#=code.Escape(inverse)#>, this))
                {
                    item.<#=code.Escape(inverse)#> = null;
                }
<#
                    }
                    else
                    {
#>
                if (item.<#=code.Escape(inverse)#>.Contains(this))
                {
                    item.<#=code.Escape(inverse)#>.Remove(this);
                }
<#
                    }
                }
                else if (IsForeignKeyOrIdentifyingRelationship(ef, navProperty))
                {
                    foreach (var fromProperty in ef.GetPrincipalProperties(navProperty))
                    {
                        var p = ef.GetCorrespondingDependentProperty(navProperty, fromProperty);
                        if (ef.IsNullable(p.TypeUsage))
                        {
#>
                item.<#=code.Escape(p)#> = null;
<#
                        }
                    }
                }
                else if (navProperty.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne)
                {
#>
                item.<#=CreateFixupMethodName(navProperty.FromEndMember)#>(null, false);
<#
                }
#>
                if (ChangeTracker.ChangeTrackingEnabled)
                {
                    ChangeTracker.RecordRemovalFromCollectionProperties("<#=code.Escape(navProperty)#>", item);
<#
                if (ef.IsPrincipalEndOfIdentifyingRelationship((AssociationEndMember)navProperty.FromEndMember))
                {
#>
                    // Delete the dependent end of this identifying association. If the current state is Added,
                    // allow the relationship to be changed without causing the dependent to be deleted.
                    if (item.ChangeTracker.State != ObjectState.Added)
                    {
                        item.MarkAsDeleted();
                    }
<#
                }
#>
                }
<#
                if (ef.IsCascadeDeletePrincipal(navProperty))
                {
#>
                // This is the principal end in an association that performs cascade deletes.
                // Remove the previous dependent from the event listener.
                ChangeTracker.ObjectStateChanging -= item.HandleCascadeDelete;
<#
                }
#>
            }
        }
    }
<#
        }
    }

    foreach(var associationEnd in shadowAssociationEnds)
    {
        AssociationType association = associationEnd.DeclaringType as AssociationType;
        EntityType targetType = ((RefType)associationEnd.TypeUsage.EdmType).ElementType as EntityType;
        List<string> keyNames = targetType.KeyMembers.Select(x => x.Name).ToList();
#>

    internal void <#=CreateFixupMethodName(associationEnd)#>(<#=code.Escape(targetType)#> value, bool forceRemove)
    {
<#
            for(int k=0; k < keyNames.Count; k++)
            {
#>
        const string <#=CreateKeyNameVariable(code.Escape(keyNames[k]))#> = "<#=CreateReferenceValueLookupKey(associationEnd, keyNames[k])#>";
<#
            }
#>

        if (ChangeTracker.ChangeTrackingEnabled &&
<#
        for(int k=0; k < keyNames.Count; k++)
        {
#>
            ChangeTracker.ExtendedProperties.ContainsKey(<#=CreateKeyNameVariable(code.Escape(keyNames[k]))#>)<#=k < keyNames.Count - 1 ? " &&" : ")"#>
<#
        }
#>
        {
            if (forceRemove ||
<#
        for(int k=0; k < keyNames.Count; k++)
        {
                string equality = ((PrimitiveType)targetType.KeyMembers[keyNames[k]].TypeUsage.EdmType).PrimitiveTypeKind == PrimitiveTypeKind.Binary ? "EqualityComparer.Binary" : String.Empty;
#>
                !<#=equality#>Equals(ChangeTracker.ExtendedProperties[<#=CreateKeyNameVariable(code.Escape(keyNames[k]))#>], value == null ? null : (object)value.<#=code.Escape(keyNames[k])#>)<#=k < keyNames.Count - 1 ? " ||" : ")"#>
<#
        }
#>
            {
<#
        for(int k=0; k < keyNames.Count; k++)
        {
#>
                ChangeTracker.RecordOriginalValue(<#=CreateKeyNameVariable(code.Escape(keyNames[k]))#>, ChangeTracker.ExtendedProperties[<#=CreateKeyNameVariable(code.Escape(keyNames[k]))#>]);
<#
        }
#>
                if (value == null)
                {
<#
        for(int k=0; k < keyNames.Count; k++)
        {
#>
                    ChangeTracker.ExtendedProperties.Remove(<#=CreateKeyNameVariable(code.Escape(keyNames[k]))#>);
<#
        }
#>
                }
                else
                {
<#
        for(int k=0; k < keyNames.Count; k++)
        {
#>
                    ChangeTracker.ExtendedProperties[<#=CreateKeyNameVariable(code.Escape(keyNames[k]))#>] = value.<#=code.Escape(keyNames[k])#>;
<#
        }
#>
                }
            }
        }
    }
<#
    }

    region.End();
#>
}
<#
    EndNamespace(namespaceName);
}

foreach (ComplexType complex in ItemCollection.GetItems<ComplexType>().OrderBy(e => e.Name))
{
    fileManager.StartNewFile(complex.Name + ".cs");
    BeginNamespace(namespaceName, code);
    WriteComplexTypeSerializationInfo(complex, code);
#>
<#=Accessibility.ForType(complex)#> partial class <#=code.Escape(complex)#> : INotifyComplexPropertyChanging, INotifyPropertyChanged
{
<#
    region.Begin("Primitive Properties");

    foreach(EdmProperty edmProperty in complex.Properties.Where(p => p.TypeUsage.EdmType is PrimitiveType && p.DeclaringType == complex))
    {
#>

    [DataMember]
<#BuildDataAnnotations(edmProperty);#>
    <#=Accessibility.ForProperty(edmProperty)#> <#=code.Escape(edmProperty.TypeUsage)#> <#=code.Escape(edmProperty)#>
    {
        <#=code.SpaceAfter(Accessibility.ForGetter(edmProperty))#>get { return <#=code.FieldName(edmProperty)#>; }
        <#=code.SpaceAfter(Accessibility.ForSetter(edmProperty))#>set
        {
            if (<#=code.FieldName(edmProperty)#> != value)
            {
                OnComplexPropertyChanging();
                PropertySetterEntry("<#=edmProperty.Name#>");
                <#=code.FieldName(edmProperty)#> = value;
                PropertySetterExit("<#=edmProperty.Name#>", value);
                OnPropertyChanged("<#=edmProperty.Name#>");
            }
        }
    }
    private <#=code.Escape(edmProperty.TypeUsage)#> <#=code.FieldName(edmProperty)#><#=code.StringBefore(" = ", code.CreateLiteral(edmProperty.DefaultValue))#>;
<#
    }

    region.End();

    region.Begin("Complex Properties");

    foreach(EdmProperty edmProperty in complex.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == complex))
    {
#>

    [DataMember]
    <#=Accessibility.ForProperty(edmProperty)#> <#=code.Escape(edmProperty.TypeUsage)#> <#=code.Escape(edmProperty)#>
    {
        <#=code.SpaceAfter(Accessibility.ForGetter(edmProperty))#>get
        {
            if (!<#=InitializedTrackingField(edmProperty, code)#> && <#=code.FieldName(edmProperty)#> == null)
            {
                <#=code.FieldName(edmProperty)#> = new <#=code.Escape(edmProperty.TypeUsage)#>();
                ((INotifyComplexPropertyChanging)<#=code.FieldName(edmProperty)#>).ComplexPropertyChanging += HandleComplexPropertyChanging;
#if (SILVERLIGHT || WPF)
                ((INotifyValidationResultChanged)<#=code.FieldName(edmProperty)#>).ValidationResultChanged += Handle<#=edmProperty.Name#>ValidationResultChanged;
#endif
            }
            <#=InitializedTrackingField(edmProperty, code)#> = true;
            return <#=code.FieldName(edmProperty)#>;
        }
        <#=code.SpaceAfter(Accessibility.ForSetter(edmProperty))#>set
        {
            <#=InitializedTrackingField(edmProperty, code)#> = true;
            if (!Equals(<#=code.FieldName(edmProperty)#>, value))
            {
                if (<#=code.FieldName(edmProperty)#> != null)
                {
                    ((INotifyComplexPropertyChanging)<#=code.FieldName(edmProperty)#>).ComplexPropertyChanging -= HandleComplexPropertyChanging;
#if (SILVERLIGHT || WPF)
                    ((INotifyValidationResultChanged)<#=code.FieldName(edmProperty)#>).ValidationResultChanged -= Handle<#=edmProperty.Name#>ValidationResultChanged;
#endif
                }

                OnComplexPropertyChanging();
                PropertySetterEntry("<#=edmProperty.Name#>");
                <#=code.FieldName(edmProperty)#> = value;
                PropertySetterExit("<#=edmProperty.Name#>", value);
                OnPropertyChanged("<#=edmProperty.Name#>");

                if (value != null)
                {
                    ((INotifyComplexPropertyChanging)value).ComplexPropertyChanging += HandleComplexPropertyChanging;
#if (SILVERLIGHT || WPF)
                    ((INotifyValidationResultChanged)<#=code.FieldName(edmProperty)#>).ValidationResultChanged += Handle<#=edmProperty.Name#>ValidationResultChanged;
#endif
                }
            }
        }
    }
    private <#=code.Escape(edmProperty.TypeUsage)#> <#=code.FieldName(edmProperty)#>;
    private bool <#=InitializedTrackingField(edmProperty, code)#>;
<#
    }

    region.End();

    region.Begin("ChangeTracking");
#>

    private void OnComplexPropertyChanging()
    {
        if (_complexPropertyChanging != null)
        {
            _complexPropertyChanging(this, new EventArgs());
        }
    }

    event EventHandler INotifyComplexPropertyChanging.ComplexPropertyChanging { add { _complexPropertyChanging += value; } remove { _complexPropertyChanging -= value; } }
    private event EventHandler _complexPropertyChanging;

    private void OnPropertyChanged(String propertyName)
    {
        if (_propertyChanged != null)
        {
            _propertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged { add { _propertyChanged += value; } remove { _propertyChanged -= value; } }
    private event PropertyChangedEventHandler _propertyChanged;
<#
    if(complex.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == complex).Count() > 0)
    {
#>

    private void HandleComplexPropertyChanging(object sender, EventArgs args)
    {
        // Bubble the event to all listeners because something changed in a nested complex property
        OnComplexPropertyChanging();
    }
<#
    }
#>

    protected bool IsDeserializing { get; private set; }

    [OnDeserializing]
    public void OnDeserializingMethod(StreamingContext context)
    {
        IsDeserializing = true;
    }

    [OnDeserialized]
    public void OnDeserializedMethod(StreamingContext context)
    {
        IsDeserializing = false;
    }

    public static void RecordComplexOriginalValues(String parentPropertyName, <#=code.Escape(complex)#> complexObject, ObjectChangeTracker changeTracker)
    {
        if (String.IsNullOrEmpty(parentPropertyName))
        {
            throw new ArgumentException("String parameter cannot be null or empty.", "parentPropertyName");
        }

        if (changeTracker == null)
        {
            throw new ArgumentNullException("changeTracker");
        }
<#
        foreach(EdmProperty complexProperty in complex.Properties)
        {
            if (complexProperty.TypeUsage.EdmType is ComplexType)
            {
#>
        <#=code.Escape(complexProperty.TypeUsage)#>.RecordComplexOriginalValues(String.Format(CultureInfo.InvariantCulture, "{0}.<#=complexProperty.Name#>", parentPropertyName), complexObject == null ? null : complexObject.<#=code.Escape(complexProperty)#>, changeTracker);
<#
            }
            else
            {
#>
        changeTracker.RecordOriginalValue(String.Format(CultureInfo.InvariantCulture, "{0}.<#=complexProperty.Name#>", parentPropertyName), complexObject == null ? null : (object)complexObject.<#=code.Escape(complexProperty)#>);
<#
            }
        }
#>
    }
<#
    region.End();

    region.Begin("Validation and Helper Method");
#>

    /// <summary>
    /// Loops through all related data annotation attributes as well as all related
    /// custom validation actions for the specified property name. If  propertyName
    /// is String.Empty/null, the validation is on the class level.
    /// </summary>
    /// <param name="propertyName">Propery name or String.Empty/null for top-level errors</param>
    /// <param name="value">New value that needs to be validated for the specified property</param>
    public void Validate(string propertyName, object value)
    {
        // First, process Data Annotation Attributes
        var validationResults = new Collection<ValidationResult>();
        if (string.IsNullOrEmpty(propertyName))
        {
            // this is top-level validation
            ValidationContext context = new ValidationContext(this, null, null);
            Validator.TryValidateObject(this, context, validationResults, true);
        }
        else
        {
            PropertyInfo property = GetType().GetProperty(propertyName);
            if (property == null)
            {
                throw new ArgumentException(propertyName + " does not exist.");
            }
            // this is validation for each property
            ValidationContext context = new ValidationContext(this, null, null) { MemberName = propertyName };
            Validator.TryValidateProperty(value, context, validationResults);
        }
        if (validationResults.Count != 0)
        {
#if (SILVERLIGHT || WPF)
            foreach (ValidationResult error in validationResults)
            {
                if (error.MemberNames.Count() == 0)
                {
                    // this is top-level validation error
                    AddError(string.Empty, error);
                }
                else
                {
                    foreach (string memberName in error.MemberNames)
                    {
                        AddError(memberName, error);
                    }
                }
            }
#else
            FaultReason faultReason = new FaultReason(validationResults.First().ErrorMessage);
            throw new FaultException(faultReason);
#endif
        }
        // Next, process custom validation actions
        if (ValidationActions.Count == 0) InitializeValidationSettings();
        if (string.IsNullOrEmpty(propertyName))
        {
            // loop through all properties and call its list of validation actions
            foreach (string memberName in ValidationActions.Keys.ToList())
            {
                List<Action<object>> actions = ValidationActions[memberName];
                if (string.IsNullOrEmpty(memberName))
                {
                    // this is top-level validation
                    actions.ForEach(action => action(this));
                }
                else
                {
                    // this is validation for each property
                    object memberValue = GetType().GetProperty(memberName).GetValue(this, null);
                    actions.ForEach(action => action(memberValue));
                }
            }
<#
    if(complex.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == complex).Count() > 0)
    {
#>
            // validate all Complex Properties
<#
    }
    foreach(EdmProperty edmProperty in complex.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == complex))
    {
#>
            <#=code.Escape(edmProperty)#>.Validate(string.Empty, this.<#=code.Escape(edmProperty)#>);
<#
    }
#>
        }
        else
        {
            if (ValidationActions.ContainsKey(propertyName))
            {
                List<Action<object>> actions = ValidationActions[propertyName];
                actions.ForEach(action => action(value));
            }
<#
    if(complex.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == complex).Count() > 0)
    {
#>
            // check whether propertyName is a Complex Property
<#
    }
    foreach(EdmProperty edmProperty in complex.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == complex))
    {
#>
            if (propertyName == "<#=edmProperty.Name#>")
            {
                <#=code.Escape(edmProperty)#>.Validate(string.Empty, value);
            }
<#
    }
#>
        }
    }

#if SILVERLIGHT
    /// <summary>
    /// Client-side validation function to loop through all data annotation
    /// attributes and all custom validation actions.  If any validation fails,
    /// this function will return false. Otherwise, true.
    /// </summary>
    /// <returns></returns>
    public bool TryValidate()
    {
        // clear known errors for all property names.
        ClearErrors();
        // First, process Data Annotation Attributes
        ValidationContext context = new ValidationContext(this, null, null);
        var validationResults = new Collection<ValidationResult>();
        Validator.TryValidateObject(this, context, validationResults, true);
        if (validationResults.Count != 0)
        {
            foreach (ValidationResult error in validationResults)
            {
                if (error.MemberNames.Count() == 0)
                {
                    // this is top-level validation error
                    AddError(string.Empty, error);
                }
                else
                {
                    foreach (string propertyName in error.MemberNames)
                    {
                        AddError(propertyName, error);
                    }
                }
            }
        }
        // Next, process custom validation actions
        if (ValidationActions.Count == 0) InitializeValidationSettings();
        // loop through all properties and call its list of validation actions
        foreach (string propertyName in ValidationActions.Keys.ToList())
        {
            List<Action<object>> actions = ValidationActions[propertyName];
            if (string.IsNullOrEmpty(propertyName))
            {
                // this is top-level validation
                actions.ForEach(action => action(this));
            }
            else
            {
                // this is validation for each property
                object value = GetType().GetProperty(propertyName).GetValue(this, null);
                actions.ForEach(action => action(value));
            }
        }
<#
    if(complex.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == complex).Count() > 0)
    {
#>
        // Last, call TryValidate() on all Complex Properties
<#
    }
    foreach(EdmProperty edmProperty in complex.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == complex))
    {
#>
        <#=code.Escape(edmProperty)#>.TryValidate();
<#
    }
#>
        return !(((INotifyDataErrorInfo)this).HasErrors);
    }

    /// <summary>
    /// Client-side validation function for propertyName.
    /// If any validation fails, this function will return false.
    /// Otherwise, true.
    /// </summary>
    /// <param name="propertyName"></param>
    /// <returns></returns>
    public bool TryValidate(string propertyName)
    {
        if (string.IsNullOrEmpty(propertyName))
        {
            return TryValidate();
        }
        PropertyInfo property = GetType().GetProperty(propertyName);
        if (property == null)
        {
            throw new ArgumentException(propertyName + " does not exist.");
        }
        // clear known errors for the property name
        ClearErrors(propertyName);
        // get the value of propertyName
        object value = GetType().GetProperty(propertyName).GetValue(this, null);
        Validate(propertyName, value);
        IEnumerable errors = ((INotifyDataErrorInfo)this).GetErrors(propertyName);
        if (errors == null) return true;
        int count = errors.Cast<object>().Count();
        return count == 0;
    }
#elif WPF
    /// <summary>
    /// Client-side validation function to loop through all data annotation
    /// attributes and all custom validation actions.  If any validation fails,
    /// this function will return false. Otherwise, true.
    /// </summary>
    /// <returns></returns>
    public bool TryValidate()
    {
        // clear known errors for all property names.
        ClearErrors();
        // First, process Data Annotation Attributes
        ValidationContext context = new ValidationContext(this, null, null);
        var validationResults = new Collection<ValidationResult>();
        Validator.TryValidateObject(this, context, validationResults, true);
        if (validationResults.Count != 0)
        {
            foreach (ValidationResult error in validationResults)
            {
                if (error.MemberNames.Count() == 0)
                {
                    // this is top-level validation error
                    AddError(string.Empty, error);
                }
                else
                {
                    foreach (string propertyName in error.MemberNames)
                    {
                        AddError(propertyName, error);
                    }
                }
            }
        }
        // Next, process custom validation actions
        if (ValidationActions.Count == 0) InitializeValidationSettings();
        // loop through all properties and call its list of validation actions
        foreach (string propertyName in ValidationActions.Keys.ToList())
        {
            List<Action<object>> actions = ValidationActions[propertyName];
            if (string.IsNullOrEmpty(propertyName))
            {
                // this is top-level validation
                actions.ForEach(action => action(this));
            }
            else
            {
                // this is validation for each property
                object value = GetType().GetProperty(propertyName).GetValue(this, null);
                actions.ForEach(action => action(value));
            }
        }
<#
    if(complex.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == complex).Count() > 0)
    {
#>
        // Last, call TryValidate() on all Complex Properties
<#
    }
    foreach(EdmProperty edmProperty in complex.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == complex))
    {
#>
        <#=code.Escape(edmProperty)#>.TryValidate();
<#
    }
#>
        return (ValidationErrors.Count == 0);
    }
    
    /// <summary>
    /// Client-side validation function for propertyName.
    /// If any validation fails, this function will return false.
    /// Otherwise, true.
    /// </summary>
    /// <param name="propertyName"></param>
    /// <returns></returns>
    public bool TryValidate(string propertyName)
    {
        if (string.IsNullOrEmpty(propertyName))
        {
            return TryValidate();
        }
        PropertyInfo property = GetType().GetProperty(propertyName);
        if (property == null)
        {
            throw new ArgumentException(propertyName + " does not exist.");
        }
        // clear known errors for the property name
        ClearErrors(propertyName);
        // get the value of propertyName
        object value = GetType().GetProperty(propertyName).GetValue(this, null);
        Validate(propertyName, value);
        return ValidationErrors.ContainsKey(propertyName);
    }
#else
    /// <summary>
    /// Server-side validation function to loop through all data annotation
    /// attributes and all custom validation actions.  If any validation fails,
    /// it will throw an exception.
    /// </summary>
    public void Validate()
    {
        // First, process Data Annotation Attributes
        ValidationContext context = new ValidationContext(this, null, null);
        var validationResults = new Collection<ValidationResult>();
        Validator.TryValidateObject(this, context, validationResults, true);
        if (validationResults.Count != 0)
        {
            ValidationResult error = validationResults.First();
            if (error.MemberNames.Count() == 0)
            {
                // this is top-level validation error
                FaultReason faultReason = new FaultReason(error.ErrorMessage);
                throw new FaultException(faultReason);
            }
            else
            {
                string propertyName = error.MemberNames.First();
                FaultReason faultReason = new FaultReason(propertyName + " : " + error.ErrorMessage);
                throw new FaultException(faultReason);
            }
        }
        // Next, process custom validation actions
        if (ValidationActions.Count == 0) InitializeValidationSettings();
        // loop through all properties and call its list of validation actions
        foreach (string propertyName in ValidationActions.Keys.ToList())
        {
            List<Action<object>> actions = ValidationActions[propertyName];
            if (string.IsNullOrEmpty(propertyName))
            {
                // this is top-level validation
                actions.ForEach(action => action(this));
            }
            else
            {
                // this is validation for each property
                object value = GetType().GetProperty(propertyName).GetValue(this, null);
                actions.ForEach(action => action(value));
            }
        }
<#
    if(complex.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == complex).Count() > 0)
    {
#>
        // Last, validate all Complex Properties
<#
    }
    foreach(EdmProperty edmProperty in complex.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == complex))
    {
#>
        <#=code.Escape(edmProperty)#>.Validate();
<#
    }
#>
    }
#endif
    partial void PropertySetterEntry(string propertyName);
    partial void PropertySetterExit(string propertyName, object propertyValue);
    partial void InitializeValidationSettings();

    private Dictionary<string, List<Action<object>>> ValidationActions
    {
        get
        {
            if (_validationActions == null)
            {
                _validationActions = new Dictionary<string, List<Action<object>>>();
            }
            return _validationActions;
        }
    }
    private Dictionary<string, List<Action<object>>> _validationActions;

    /// <summary>
    /// Add one validation action for the specified property name.
    /// </summary>
    /// <param name="propertyName">Propery name or String.Empty/null for top-level errors</param>
    /// <param name="validationAction">Function to carry out a specific validation action</param>
    private void AddPropertyValidationAction(string propertyName, Action<object> validationAction)
    {
        List<Action<object>> validationActionList;
        if (!ValidationActions.ContainsKey(propertyName))
        {
            validationActionList = new List<Action<object>>();
            ValidationActions.Add(propertyName, validationActionList);
        }
        else
            validationActionList = ValidationActions[propertyName];

        validationActionList.Add(validationAction);
    }
<#
    region.End();
#>
}
<#
    EndNamespace(namespaceName);
}

if (!VerifyTypesAreCaseInsensitiveUnique(ItemCollection))
{
    return "";
}

fileManager.Process();

#>
<#+
void WriteHeader(EntityFrameworkTemplateFileManager fileManager, params string[] extraUsings)
{
    fileManager.StartHeader();
#>
//------------------------------------------------------------------------------
// <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;
<#=String.Join(String.Empty, extraUsings.Select(u => "using " + u + ";" + Environment.NewLine).ToArray())#>
<#+
    fileManager.EndBlock();
}

void BeginNamespace(string namespaceName, CodeGenerationTools code)
{
    CodeRegion region = new CodeRegion(this);
    if (!String.IsNullOrEmpty(namespaceName))
    {
#>
namespace <#=code.EscapeNamespace(namespaceName)#>
{
<#+
        PushIndent(CodeRegion.GetIndent(1));
    }
}

void EndNamespace(string namespaceName)
{
    if (!String.IsNullOrEmpty(namespaceName))
    {
        PopIndent();
#>
}
<#+
    }
}

bool IsReadWriteAccessibleProperty(EdmMember member)
{
    string setter = Accessibility.ForWriteOnlyProperty(member);
    string getter = Accessibility.ForReadOnlyProperty(member);

    return getter != "private" && getter != "protected" && setter != "private" && setter != "protected";
}

string InitializedTrackingField(EdmProperty property, CodeGenerationTools code)
{
    string namePart = property.Name + "Initialized";
    if (code.CamelCaseFields)
    {
        namePart = code.CamelCase(namePart);
    }
    return "_" + namePart;
}

void WriteEntityTypeSerializationInfo(EntityType type, ItemCollection itemCollection, CodeGenerationTools code, MetadataTools tools)
{
#>
[DataContract(IsReference = true)]
<#+
    foreach(EntityType subtype in tools.GetSubtypesOf(type, itemCollection, true))
    {
#>
[KnownType(typeof(<#=code.Escape(subtype)#>))]
<#+
    }
    List<EntityType> knownNavPropertyTypes = new List<EntityType>();
    foreach(NavigationProperty navProperty in type.NavigationProperties.Where(np => np.DeclaringType == type))
    {
        EntityType navPropertyType = navProperty.ToEndMember.GetEntityType();
        if(!knownNavPropertyTypes.Contains(navPropertyType))
        {
            knownNavPropertyTypes.Add(navPropertyType);
        }
    }
    foreach(EntityType knownNavPropertyType in knownNavPropertyTypes)
    {
#>
[KnownType(typeof(<#=code.Escape(knownNavPropertyType)#>))]
<#+
    }
	List<TypeUsage> knownComplexPropertyTypeUsages = new List<TypeUsage>();
	foreach (EdmProperty edmProperty in type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type))
	{
		TypeUsage complexPropertyTypeUsage = edmProperty.TypeUsage;
		if (!knownComplexPropertyTypeUsages.Contains(complexPropertyTypeUsage))
		{
			knownComplexPropertyTypeUsages.Add(complexPropertyTypeUsage);
		}
	}
    foreach(TypeUsage knownComplexPropertyTypeUsage in knownComplexPropertyTypeUsages)
    {
#>
[KnownType(typeof(<#=code.Escape(knownComplexPropertyTypeUsage)#>))]
<#+
    }
}

void WriteComplexTypeSerializationInfo(ComplexType complexType, CodeGenerationTools code)
{
#>
[DataContract(IsReference = true)]
<#+
	List<TypeUsage> knownComplexPropertyTypeUsages = new List<TypeUsage>();
	foreach (EdmProperty edmProperty in complexType.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == complexType))
	{
		TypeUsage complexPropertyTypeUsage = edmProperty.TypeUsage;
		if (!knownComplexPropertyTypeUsages.Contains(complexPropertyTypeUsage))
		{
			knownComplexPropertyTypeUsages.Add(complexPropertyTypeUsage);
		}
	}
    foreach(TypeUsage knownComplexPropertyTypeUsage in knownComplexPropertyTypeUsages)
    {
#>
[KnownType(typeof(<#=code.Escape(knownComplexPropertyTypeUsage)#>))]
<#+
    }
}

bool IsSaveReference(MetadataTools tools, NavigationProperty navProperty)
{
    return !IsForeignKeyOrIdentifyingRelationship(tools, navProperty) &&
           navProperty.ToEndMember.RelationshipMultiplicity != RelationshipMultiplicity.Many &&         // Target is a reference
           navProperty.FromEndMember.RelationshipMultiplicity != RelationshipMultiplicity.One;          // Source is nullable (i.e. not a PK)
}

string CreateFixupMethodName(RelationshipEndMember endMember)
{
    return String.Format(CultureInfo.InvariantCulture, "Fixup{0}_{1}_{2}Keys", endMember.DeclaringType.NamespaceName.Replace(".", "_"), endMember.DeclaringType.Name, endMember.Name);
}

string CreateKeyNameVariable(string keyName)
{
    return String.Format(CultureInfo.InvariantCulture, "{0}KeyName", keyName);
}

string CreateReferenceValueLookupKey(AssociationEndMember endMember, string keyName)
{
    return String.Format(CultureInfo.InvariantCulture, "Navigate({0}.{1}).{2}", endMember.DeclaringType.FullName, endMember.Name, keyName);
}

string CreateReferenceValueLookupKey(NavigationProperty navProp, string keyName)
{
    return String.Format(CultureInfo.InvariantCulture, "{0}.{1}", navProp.Name, keyName);
}

void WriteCustomObservableCollection()
{
#>

// 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);
        }
    }
}
<#+
}

void WriteObjectChangeTracker()
{
#>
// 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> { }
<#+
}

void WriteINotifyComplexPropertyChanging()
{
#>

// 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;
}
<#+
}

void WriteIObjectWithChangeTracker()
{
#>
// 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);
        }
    }
}
<#+
}

void WriteEqualityComparer()
{
#>

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;
    }
}
<#+
}

bool VerifyTypesAreCaseInsensitiveUnique(EdmItemCollection itemCollection)
{
    Dictionary<string, bool> alreadySeen = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
    foreach(StructuralType type in itemCollection.GetItems<StructuralType>())
    {
        if (!(type is EntityType || type is ComplexType))
        {
            continue;
        }

        if (alreadySeen.ContainsKey(type.FullName))
        {
            Error(String.Format(CultureInfo.CurrentCulture, "This template does not support types that differ only by case, the types {0} are not supported", type.FullName));
            return false;
        }
        else
        {
            alreadySeen.Add(type.FullName, true);
        }

    }

    return true;
}

// True if the association for the specified navigation property is an identifying relationship or a foreign key relationship.
private bool IsForeignKeyOrIdentifyingRelationship(MetadataTools tools, NavigationProperty navProperty)
{
    if (tools == null)
    {
        throw new ArgumentNullException("tools");
    }

    if (navProperty == null)
    {
        throw new ArgumentNullException("navProperty");
    }

    return IsForeignKeyOrIdentifyingRelationship(tools, (AssociationType)navProperty.RelationshipType);
}

// True if the specified association is an identifying relationship or a foreign key relationship.
private bool IsForeignKeyOrIdentifyingRelationship(MetadataTools tools, AssociationType association)
{
    if (tools == null)
    {
        throw new ArgumentNullException("tools");
    }

    if (association == null)
    {
        throw new ArgumentNullException("association");
    }

    return association.IsForeignKey || tools.IsIdentifyingRelationship(association);
}

// Set recordRequiredOriginalValuesOnly to false in the OriginalValueMembers constructor in order to always record all original values
public class OriginalValueMembers
{
    private readonly HashSet<EdmProperty> _concurrencyMembers;

    public OriginalValueMembers(bool recordRequiredOriginalValuesOnly, MetadataWorkspace metadataWorkspace, MetadataTools metadataTools)
    {
        if (recordRequiredOriginalValuesOnly)
        {
            try
            {
                _concurrencyMembers = new HashSet<EdmProperty>();
                foreach (EntityContainer container in metadataWorkspace.GetItems<EntityContainer>(DataSpace.CSpace))
                {
                    ILookup<EntityType, EntityType> directSubTypeLookup = metadataWorkspace.GetItems<EntityType>(DataSpace.CSpace).ToLookup(e => (EntityType)e.BaseType);
                    foreach (EntitySetBase eSet in container.BaseEntitySets.Where(es => es.BuiltInTypeKind == BuiltInTypeKind.EntitySet))
                    {
                        List<EntityType> subTypes = new List<EntityType>();
                        GetSubtypes(directSubTypeLookup, (EntityType)eSet.ElementType, subTypes);
                        foreach (EntityType eType in subTypes)
                        {
                            foreach (EdmProperty member in metadataWorkspace.GetRequiredOriginalValueMembers(eSet, eType))
                            {
                                _concurrencyMembers.Add(member);
                            }
                        }
                    }
                }

                // GetRequiredOriginalValueMembers will not always return foreign key properties, but they are required
                foreach (AssociationType assoc in metadataWorkspace.GetItems<AssociationType>(DataSpace.CSpace).Where(a => a.IsForeignKey))
                {
                    foreach (EdmProperty toProperty in assoc.ReferentialConstraints[0].ToProperties)
                    {
                        _concurrencyMembers.Add(toProperty);
                    }
                }
            }
            catch (Exception)
            {
                // If any exceptions occur, fall back to always recording original values for all properties
                _concurrencyMembers = null;
            }
        }
    }

    public bool IsOriginalValueMember(EdmProperty edmProperty)
    {
        return _concurrencyMembers == null || _concurrencyMembers.Contains(edmProperty);
    }

    private static void GetSubtypes(ILookup<EntityType, EntityType> lookup, EntityType eType, List<EntityType> subTypes)
    {
        subTypes.Add(eType);
        foreach (EntityType subType in lookup[eType])
        {
            GetSubtypes(lookup, subType, subTypes);
        }
    }
}

public void BuildDataAnnotations(EdmProperty edmProperty)
{
    MetadataProperty displayMetadataProperty = edmProperty.MetadataProperties.FirstOrDefault(mpr => mpr.Name == @"http://schemas.microsoft.com/PEM/2009/01:Display");
    
    if (displayMetadataProperty != null)
    {
        XElement modelElement = new XElement("Model", new object[] { new XAttribute("Type", edmProperty.TypeUsage.EdmType.Name), displayMetadataProperty.Value as XElement });
                
        ValueValidationExtendedProperty property = new ValueValidationExtendedProperty(modelElement, null, null);

        PEMDisplay pemDisplay = property.Display as PEMDisplay;
        if (pemDisplay != null)
        {
            if (!string.IsNullOrWhiteSpace(pemDisplay.Name) && !string.IsNullOrWhiteSpace(pemDisplay.Description))
            {
                PushIndent(CodeRegion.GetIndent(1));
                WriteLine("[Display(Name=\"{0}\", Description=\"{1}\")]", pemDisplay.Name, pemDisplay.Description);
                PopIndent();
            }
            else if (!string.IsNullOrWhiteSpace(pemDisplay.Name))
            {
                PushIndent(CodeRegion.GetIndent(1));
                WriteLine("[Display(Name=\"{0}\")]", pemDisplay.Name);
                PopIndent();
            }
        }
    }
    
    MetadataProperty validationsMetadataProperty = edmProperty.MetadataProperties.FirstOrDefault(mpr => mpr.Name == @"http://schemas.microsoft.com/PEM/2009/01:Validations");

    if (validationsMetadataProperty != null)
    {
        XElement modelElement = new XElement("Model", new object[] { new XAttribute("Type", edmProperty.TypeUsage.EdmType.Name), validationsMetadataProperty.Value as XElement });
                
        ValueValidationExtendedProperty property = new ValueValidationExtendedProperty(modelElement, null, null);

        PushIndent(CodeRegion.GetIndent(1));
        foreach(PEM.Validations.ValidationsBase validation in property.Validation.Validations)
        {
            switch (validation.GetType().Name)
            {
                case "DataFieldLength":
                    if (!string.IsNullOrWhiteSpace(validation.ErrorMessage))
                    {
                        WriteLine("[StringLength({0}, MinimumLength={1}, ErrorMessage=\"{2}\")]", (validation as DataFieldLength).MaximumLength, (validation as DataFieldLength).MinimumLength, validation.ErrorMessage);
                    }
                    else if ((validation as DataFieldLength).MinimumLength != 0)
                    {
                        WriteLine("[StringLength({0}, MinimumLength={1})]", (validation as DataFieldLength).MaximumLength, (validation as DataFieldLength).MinimumLength);
                    }
                    else
                    {
                        WriteLine("[StringLength({0})]", (validation as DataFieldLength).MaximumLength);
                    }
                    break;
                case "RangeDateTime":
                    if (!string.IsNullOrWhiteSpace(validation.ErrorMessage))
                    {
                        WriteLine("[Range(typeof(DateTime), \"{0}\", \"{1}\", ErrorMessage=\"{2}\")]", (validation as RangeDateTime).MinimumValue, (validation as RangeDateTime).MaximumValue, validation.ErrorMessage);
                    }
                    else
                    {
                        WriteLine("[Range(typeof(DateTime), \"{0}\", \"{1}\")]", (validation as RangeDateTime).MinimumValue, (validation as RangeDateTime).MaximumValue);
                    }
                    break;
                case "RangeDecimal":
                    if (!string.IsNullOrWhiteSpace(validation.ErrorMessage))
                    {
                        WriteLine("[Range(typeof(Decimal), \"{0}\", \"{1}\", ErrorMessage=\"{2}\")]", (validation as RangeDecimal).MinimumValue, (validation as RangeDecimal).MaximumValue, validation.ErrorMessage);
                    }
                    else
                    {
                        WriteLine("[Range(typeof(Decimal), \"{0}\", \"{1}\")]", (validation as RangeDecimal).MinimumValue, (validation as RangeDecimal).MaximumValue);
                    }
                    break;
                case "RangeDouble":
                    if (!string.IsNullOrWhiteSpace(validation.ErrorMessage))
                    {
                        WriteLine("[Range({0}, {1}, ErrorMessage=\"{2}\")]", (validation as RangeDouble).MinimumValue, (validation as RangeDouble).MaximumValue, validation.ErrorMessage);
                    }
                    else
                    {
                        WriteLine("[Range({0}, {1})]", (validation as RangeDouble).MinimumValue, (validation as RangeDouble).MaximumValue);
                    }
                    break;
                case "RangeInt16":
                    if (!string.IsNullOrWhiteSpace(validation.ErrorMessage))
                    {
                        WriteLine("[Range(typeof(Int16), \"{0}\", \"{1}\", ErrorMessage=\"{2}\")]", (validation as RangeInt16).MinimumValue, (validation as RangeInt16).MaximumValue, validation.ErrorMessage);
                    }
                    else
                    {
                        WriteLine("[Range(typeof(Int16), \"{0}\", \"{1}\")]", (validation as RangeInt16).MinimumValue, (validation as RangeInt16).MaximumValue);
                    }
                    break;
                case "RangeInt32":
                    if (!string.IsNullOrWhiteSpace(validation.ErrorMessage))
                    {
                        WriteLine("[Range({0}, {1}, ErrorMessage=\"{2}\")]", (validation as RangeInt32).MinimumValue, (validation as RangeInt32).MaximumValue, validation.ErrorMessage);
                    }
                    else
                    {
                        WriteLine("[Range({0}, {1})]", (validation as RangeInt32).MinimumValue, (validation as RangeInt32).MaximumValue);
                    }
                    break;
                case "RangeInt64":
                    if (!string.IsNullOrWhiteSpace(validation.ErrorMessage))
                    {
                        WriteLine("[Range(typeof(Int64), \"{0}\", \"{1}\", ErrorMessage=\"{2}\")]", (validation as RangeInt64).MinimumValue, (validation as RangeInt64).MaximumValue, validation.ErrorMessage);
                    }
                    else
                    {
                        WriteLine("[Range(typeof(Int64), \"{0}\", \"{1}\")]", (validation as RangeInt64).MinimumValue, (validation as RangeInt64).MaximumValue);
                    }
                    break;
                case "RangeSingle":
                    if (!string.IsNullOrWhiteSpace(validation.ErrorMessage))
                    {
                        WriteLine("[Range(typeof(Single), \"{0}\", \"{1}\", ErrorMessage=\"{2}\")]", (validation as RangeSingle).MinimumValue, (validation as RangeSingle).MaximumValue, validation.ErrorMessage);
                    }
                    else
                    {
                        WriteLine("[Range(typeof(Single), \"{0}\", \"{1}\")]", (validation as RangeSingle).MinimumValue, (validation as RangeSingle).MaximumValue);
                    }
                    break;
                case "RegularExpression":
                    if (!string.IsNullOrWhiteSpace(validation.ErrorMessage))
                    {
                        WriteLine("[RegularExpression(\"{0}\", ErrorMessage=\"{1}\")]", (validation as RegularExpression).Pattern, validation.ErrorMessage);
                    }
                    else
                    {
                        WriteLine("[RegularExpression(\"{0}\")]", (validation as RegularExpression).Pattern);
                    }
                    break;
                case "Required":
                    if (!string.IsNullOrWhiteSpace(validation.ErrorMessage))
                    {
                        WriteLine("[Required(ErrorMessage=\"{0}\")]", validation.ErrorMessage);
                    }
                    else
                    {
                        WriteLine("[Required]");
                    }
                    break;
                default:
                    WriteLine("//{1}[{0}]", validation.ToString(), validation.GetType().Name);
                    break;
            }                            
        }
        PopIndent();
    }
}
#>

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)

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 | Mobile
Web02 | 2.8.140721.1 | Last Updated 20 Feb 2012
Article Copyright 2011 by Weidong Shen
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid