Click here to Skip to main content
15,885,141 members
Articles / Desktop Programming / WPF

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

Rate me:
Please Sign up or sign in to vote.
5.00/5 (14 votes)
17 Mar 2013CPOL8 min read 68.5K   3.5K   44  
This article describes the project setup of building a WPF sample application with Self-Tracking Entity Generator and Visual Studio 2012.
<#@ template language="C#" debug="false" hostspecific="true"#>
<#@ include file="EF.Utility.CS.ttinclude"#><#@
output extension=".cs"#>
<#@ assembly name="Microsoft.Data.Entity.Design.Extensibility.dll" #>
<#@ assembly name="SelfTrackingEntityGenerator.EFExtension.dll" #>
<#@ import namespace="SelfTrackingEntityGenerator.EFExtension" #><#

DefineMetadata();

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");
string codeForSilverlight = @"None";
string codeForWPF = @"WPF4.5";
string codeForClientQuery = @"true";

EntityFrameworkTemplateFileManager fileManager = EntityFrameworkTemplateFileManager.Create(this);

if (!code.VerifyCaseInsensitiveTypeUniqueness(code.GetAllGlobalItems(ItemCollection), inputFile))
{
    return string.Empty;
}

// Write out support code to primary template output file
if (codeForSilverlight == @"Silverlight4" || codeForSilverlight == @"Silverlight5")
{
    WriteHeader(fileManager);
}
else if (codeForWPF == @"WPF4" || codeForWPF == @"WPF4.5")
{
    WriteHeader(fileManager, "System.IO.Compression");
}
BeginNamespace(namespaceName, code);
WriteObjectChangeTracker(ItemCollection, code);
WriteIObjectWithChangeTracker();
WriteCustomObservableCollection();
WriteINotifyComplexPropertyChanging();
if (codeForClientQuery == "true")
{

    if (codeForSilverlight == @"Silverlight4" || codeForSilverlight == @"Silverlight5")
    {
        WriteClientQuery();
    }
    else if (codeForWPF == @"WPF4" || codeForWPF == @"WPF4.5")
    {
        WriteClientQueryForWPF();
    }
}
WriteEqualityComparer();
EndNamespace(namespaceName);

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

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

    [DataMember]
<#BuildPropertyDataAnnotations(edmProperty);#>
    <#=Accessibility.ForProperty(edmProperty)#> <#=code.GetTypeName(edmProperty.TypeUsage)#> <#=code.Escape(edmProperty)#>
    {
        <#=code.SpaceAfter(Accessibility.ForGetter(edmProperty))#>get { return <#=code.FieldName(edmProperty)#>; }
        <#=code.SpaceAfter(Accessibility.ForSetter(edmProperty))#>set
        {
<#
        if (ef.UnderlyingClrType(edmProperty.TypeUsage.EdmType) == typeof(byte[]) &&
            (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 (ef.UnderlyingClrType(principalProperty.TypeUsage.EdmType) == typeof(byte[]))
            {
#>
                    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.GetTypeName(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.GetTypeName(edmProperty.TypeUsage)#> <#=code.Escape(edmProperty)#>
    {
        <#=code.SpaceAfter(Accessibility.ForGetter(edmProperty))#>get
        {
            if (!<#=InitializedTrackingField(edmProperty, code)#> && <#=code.FieldName(edmProperty)#> == null)
            {
                <#=code.FieldName(edmProperty)#> = new <#=code.GetTypeName(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.GetTypeName(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.GetTypeName(navProperty.ToEndMember.GetEntityType())#>> <#=code.Escape(navProperty)#>
    {
        get
        {
            if (<#=code.FieldName(navProperty)#> == null)
            {
                <#=code.FieldName(navProperty)#> = new TrackableCollection<<#=code.GetTypeName(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.GetTypeName(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.GetTypeName(navProperty.ToEndMember.GetEntityType())#> item in <#=code.FieldName(navProperty)#>)
                    {
                        ChangeTracker.ObjectStateChanging += item.HandleCascadeDelete;
                    }
<#
        }
#>
                }
                OnNavigationPropertyChanged("<#=navProperty.Name#>");
            }
        }
    }
    private TrackableCollection<<#=code.GetTypeName(navProperty.ToEndMember.GetEntityType())#>> <#=code.FieldName(navProperty)#>;
<#
        }
        else
        {
#>
    <#=Accessibility.ForProperty(navProperty)#> <#=code.GetTypeName(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 (ef.UnderlyingClrType(principalProperty.TypeUsage.EdmType) == typeof(byte[]))
                    {
                        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.GetTypeName(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 (ef.NeedsHandleCascadeDeleteMethod(ItemCollection, entity))
    {
#>

    // 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.GetTypeName(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.GetTypeName(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 ? " &&" : ")" #>
<#
            }
#>
        {
<#
            for(int k=0; k < keyNames.Count; k++)
            {
#>
            ChangeTracker.RecordOriginalValue(<#=CreateKeyNameVariable(code.Escape(keyNames[k]))#>, ChangeTracker.ExtendedProperties[<#=CreateKeyNameVariable(code.Escape(keyNames[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.GetTypeName(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.GetTypeName(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.GetTypeName(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 = ef.UnderlyingClrType(targetType.KeyMembers[keyNames[k]].TypeUsage.EdmType) == typeof(byte[]) ? "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);
} // End of Block for Entity Types

// Block for Complex Types
foreach (ComplexType complex in code.GetItemsToGenerate<ComplexType>(ItemCollection).OrderBy(e => e.Name))
{
    fileManager.StartNewFile(complex.Name + ".cs");
    BeginNamespace(namespaceName, code);
#>
<#=Accessibility.ForType(complex)#> partial class <#=code.Escape(complex)#> : INotifyComplexPropertyChanging, INotifyPropertyChanged
{
<#
    region.Begin("Simple Properties");

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

    [DataMember]
<#BuildPropertyDataAnnotations(edmProperty);#>
    <#=Accessibility.ForProperty(edmProperty)#> <#=code.GetTypeName(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.GetTypeName(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.GetTypeName(edmProperty.TypeUsage)#> <#=code.Escape(edmProperty)#>
    {
        <#=code.SpaceAfter(Accessibility.ForGetter(edmProperty))#>get
        {
            if (!<#=InitializedTrackingField(edmProperty, code)#> && <#=code.FieldName(edmProperty)#> == null)
            {
                <#=code.FieldName(edmProperty)#> = new <#=code.GetTypeName(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.GetTypeName(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.GetTypeName(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);
}  // End of Block for Complex Types

// Block for Enum Types
foreach (EnumType enumType in code.GetItemsToGenerate<EnumType>(ItemCollection).OrderBy(e => e.Name))
{
    fileManager.StartNewFile(enumType.Name + ".cs");
    BeginNamespace(namespaceName, code);

    if (enumType.IsFlags)
    {
#>
[Flags]
<#
    }
#>
[DataContract]
<#=Accessibility.ForType(enumType)#> enum <#=code.Escape(enumType)#> : <#=code.Escape(enumType.UnderlyingType.ClrEquivalentType, fullyQualifySystemTypes: false)#>
{
<#
    foreach (EnumMember member in enumType.Members)
    {
#>
    [EnumMember]
    <#=code.Escape(member)#> = <#=member.Value#>,

<#
    }

    // Remove the last comma and line break
    if (enumType.Members.Any())
    {
        this.GenerationEnvironment.Remove(
            this.GenerationEnvironment.Length - (CurrentIndent.Length + 5), CurrentIndent.Length + 3);
    }
#>
}
<#
    EndNamespace(namespaceName);
}  // End of Block for Enum Types

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;
using System.Xml.Linq;
<#=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 WriteEnumTypeSerializationInfo(ItemCollection itemCollection, CodeGenerationTools code)
{
    foreach (EnumType enumType in code.GetItemsToGenerate<EnumType>(itemCollection).OrderBy(e => e.Name))
    {
#>
[KnownType(typeof(<#=code.GetTypeName(enumType)#>))]
<#+
    }
}

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.GetTypeName(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.GetTypeName(knownNavPropertyType)#>))]
<#+
    }

    List<EdmType> knownSimplePropertyTypes = new List<EdmType>();

    AddSimplePropertiesKnownTypes(type, knownSimplePropertyTypes, code, tools);
    foreach(EdmType complexType in type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type).Select(p => p.TypeUsage.EdmType))
    {
        AddSimplePropertiesKnownTypes((ComplexType)complexType, knownSimplePropertyTypes, code, tools);
    }

    foreach(EdmType knownSimplePropertyType in knownSimplePropertyTypes)
    {
#>
[KnownType(typeof(<#=code.GetTypeName(knownSimplePropertyType)#>))]
<#+
    }
}

void AddSimplePropertiesKnownTypes(StructuralType type, List<EdmType> knownSimplePropertyTypes, CodeGenerationTools code, MetadataTools tools)
{
    foreach(EdmProperty edmProperty in type.Members.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type))
    {
        EdmType propertyType = edmProperty.TypeUsage.EdmType;
        if (tools.UnderlyingClrType(propertyType) == typeof(DateTimeOffset) &&
            !knownSimplePropertyTypes.Contains(propertyType))
        {
            knownSimplePropertyTypes.Add(propertyType);
        }
        if (propertyType is EnumType && !knownSimplePropertyTypes.Contains(propertyType))
        {
            knownSimplePropertyTypes.Add(propertyType);
        }
    }
    foreach(EdmProperty edmProperty in type.Members.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type))
    {
        AddSimplePropertiesKnownTypes((ComplexType)edmProperty.TypeUsage.EdmType, knownSimplePropertyTypes, code, tools);
    }
}

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(ItemCollection itemCollection, CodeGenerationTools code)
{
#>
// Helper class that captures most of the change tracking work that needs to be done
// for self tracking entities.
[DataContract(IsReference = true)]
public class ObjectChangeTracker
{
    #region Fields

    private object _parentObject;
    private bool _isDeserializing;
    private ObjectState _objectState = ObjectState.Added;
    private bool _changeTrackingEnabled;
    private OriginalValuesDictionary _originalValues;
    private ComplexTypeOriginalValuesDictionary _complexTypeOriginalValues;
    private ExtendedPropertiesDictionary _extendedProperties;
    private ObjectsAddedToCollectionProperties _objectsAddedToCollections = new ObjectsAddedToCollectionProperties();
    private ObjectsRemovedFromCollectionProperties _objectsRemovedFromCollections = new ObjectsRemovedFromCollectionProperties();

    #endregion

    #region Events

    public event EventHandler<ObjectStateChangingEventArgs> ObjectStateChanging;
    public event EventHandler<ObjectStateChangedEventArgs> ObjectStateChanged;
    public event EventHandler UpdateHasChanges;

    protected virtual void OnObjectStateChanging(ObjectState newState)
    {
        if (ObjectStateChanging != null)
        {
            ObjectStateChanging(this, new ObjectStateChangingEventArgs { NewState = newState });
        }
    }

    protected virtual void OnObjectStateChanged(ObjectState newState)
    {
        if (ObjectStateChanged != null)
        {
            ObjectStateChanged(this, new ObjectStateChangedEventArgs { NewState = newState });
        }
    }

    protected virtual void OnUpdateHasChanges()
    {
        if (UpdateHasChanges != null)
        {
            UpdateHasChanges(this, new EventArgs());
        }
    }

    #endregion

    #region Public Properties

    [DataMember]
    public ObjectState State
    {
        get { return _objectState; }
        set
        {
            if (_objectState != value)
            {
                if (_isDeserializing || _changeTrackingEnabled)
                {
                    OnObjectStateChanging(value);
                    _objectState = value;
                    OnObjectStateChanged(value);
                }
            }
        }
    }

    public bool ChangeTrackingEnabled
    {
        get { return _changeTrackingEnabled; }
        set
        {
            if (_changeTrackingEnabled != value)
            {
                _changeTrackingEnabled = value;
                OnUpdateHasChanges();
            }
        }
    }

    // Returns the removed objects to collection valued properties that were changed.
    [DataMember]
    public ObjectsRemovedFromCollectionProperties ObjectsRemovedFromCollectionProperties
    {
        get
        {
            if (_objectsRemovedFromCollections == null)
            {
                _objectsRemovedFromCollections = new ObjectsRemovedFromCollectionProperties();
            }
            return _objectsRemovedFromCollections;
        }
    }

    // Returns the original values for properties that were changed.
    [DataMember]
    public OriginalValuesDictionary OriginalValues
    {
        get
        {
            if (_originalValues == null)
            {
                _originalValues = new OriginalValuesDictionary();
            }
            return _originalValues;
        }
    }

    // Returns the original values for Complex properties that were changed.
    [DataMember]
    public ComplexTypeOriginalValuesDictionary ComplexTypeOriginalValues
    {
        get
        {
            if (_complexTypeOriginalValues == null)
            {
                _complexTypeOriginalValues = new ComplexTypeOriginalValuesDictionary();
            }
            return _complexTypeOriginalValues;
        }
    }

    // Returns the extended property values.
    // This includes key values for independent associations that are needed for the
    // concurrency model in the Entity Framework
    [DataMember]
    public ExtendedPropertiesDictionary ExtendedProperties
    {
        get
        {
            if (_extendedProperties == null)
            {
                _extendedProperties = new ExtendedPropertiesDictionary();
            }
            return _extendedProperties;
        }
    }

    // Returns the added objects to collection valued properties that were changed.
    [DataMember]
    public ObjectsAddedToCollectionProperties ObjectsAddedToCollectionProperties
    {
        get
        {
            if (_objectsAddedToCollections == null)
            {
                _objectsAddedToCollections = new ObjectsAddedToCollectionProperties();
            }
            return _objectsAddedToCollections;
        }
    }

    #endregion

    #region Methods for Change Tracking on Client

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

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

    // Resets the ObjectChangeTracker to the Unchanged state and
    // clears the original values as well as the record of changes
    // to collection properties
    public void AcceptChanges()
    {
        OnObjectStateChanging(ObjectState.Unchanged);
        OriginalValues.Clear();
        ComplexTypeOriginalValues.Clear();
        ObjectsAddedToCollectionProperties.Clear();
        ObjectsRemovedFromCollectionProperties.Clear();
        ChangeTrackingEnabled = true;
        _objectState = ObjectState.Unchanged;
        OnObjectStateChanged(ObjectState.Unchanged);
    }

    // Resets the ObjectChangeTracker to the Unchanged state and
    // rollback the original values as well as the record of changes
    // to collection properties
    public void RejectChanges()
    {
        OnObjectStateChanging(ObjectState.Unchanged);
        Type type = _parentObject.GetType();
        // rollback original values for Primitive Properties
        foreach (var originalValue in OriginalValues.ToList())
        {
            PropertyInfo primitiveProperty = type.GetProperty(originalValue.Key);
            if (primitiveProperty != null)
            {
                primitiveProperty.SetValue(_parentObject, originalValue.Value, null);
            }
        }
        // rollback original values for Complex Properties
        foreach (var originalValue in ComplexTypeOriginalValues.ToList())
        {
            PropertyInfo complexProperty = type.GetProperty(originalValue.Key);
            if (complexProperty != null)
            {
                complexProperty.SetValue(_parentObject, originalValue.Value, null);
            }
        }
        // create copy of ObjectsAddedToCollectionProperties and ObjectsRemovedFromCollectionProperties
        Dictionary<string, ObjectList> removeCollection =
            ObjectsAddedToCollectionProperties.ToDictionary(n => n.Key, n => n.Value);
        Dictionary<string, ObjectList> addCollection =
            ObjectsRemovedFromCollectionProperties.ToDictionary(n => n.Key, n => n.Value);
        // rollback ObjectsAddedToCollectionProperties
        foreach (var entry in removeCollection)
        {
            PropertyInfo collectionProperty = type.GetProperty(entry.Key);
            IList collectionObject = (IList) collectionProperty.GetValue(_parentObject, null);
            foreach (object obj in entry.Value.ToList())
                collectionObject.Remove(obj);
        }
        // rollback ObjectsRemovedFromCollectionProperties
        foreach (var entry in addCollection)
        {
            PropertyInfo collectionProperty = type.GetProperty(entry.Key);
            IList collectionObject = (IList) collectionProperty.GetValue(_parentObject, null);
            foreach (var obj in entry.Value.ToList())
                collectionObject.Add(obj);
        }
        OriginalValues.Clear();
        ComplexTypeOriginalValues.Clear();
        ObjectsAddedToCollectionProperties.Clear();
        ObjectsRemovedFromCollectionProperties.Clear();
        _objectState = ObjectState.Unchanged;
        OnObjectStateChanged(ObjectState.Unchanged);
    }

    internal void SetParentObject(object parent)
    {
        _parentObject = parent;
    }

    // Clone the ObjectChangeTracker object
    internal ObjectChangeTracker Clone()
    {
        var objectChangeTracker = new ObjectChangeTracker {_objectState = State};
        if (OriginalValues != null)
        {
            objectChangeTracker._originalValues = new OriginalValuesDictionary();
            foreach (var originalValue in OriginalValues)
            {
                (objectChangeTracker._originalValues).Add(originalValue.Key, originalValue.Value);
            }
        }
        if (ComplexTypeOriginalValues != null)
        {
            objectChangeTracker._complexTypeOriginalValues = new ComplexTypeOriginalValuesDictionary();
            foreach (var complexTypeOriginalValue in ComplexTypeOriginalValues)
            {
                objectChangeTracker._complexTypeOriginalValues.Add(complexTypeOriginalValue.Key,
                                                                   complexTypeOriginalValue.Value);
            }
        }
        if (ExtendedProperties != null)
        {
            objectChangeTracker._extendedProperties = new ExtendedPropertiesDictionary();
            foreach (var extendedProperty in ExtendedProperties)
            {
                objectChangeTracker._extendedProperties.Add(extendedProperty.Key, extendedProperty.Value);
            }
        }
        if (ObjectsAddedToCollectionProperties != null)
        {
            objectChangeTracker._objectsAddedToCollections = new ObjectsAddedToCollectionProperties();
            foreach (var objectsAddedToCollectionProperty in ObjectsAddedToCollectionProperties)
            {
                objectChangeTracker._objectsAddedToCollections.Add(objectsAddedToCollectionProperty.Key,
                                                                   objectsAddedToCollectionProperty.Value);
            }
        }
        if (ObjectsRemovedFromCollectionProperties != null)
        {
            objectChangeTracker._objectsRemovedFromCollections = new ObjectsRemovedFromCollectionProperties();
            foreach (var objectsRemovedFromCollectionProperty in ObjectsRemovedFromCollectionProperties)
            {
                objectChangeTracker._objectsRemovedFromCollections.Add(
                    objectsRemovedFromCollectionProperty.Key, objectsRemovedFromCollectionProperty.Value);
            }
        }
        return objectChangeTracker;
    }

    // Captures the original value for a property that is changing.
    internal void RecordOriginalValue(string propertyName, object value)
    {
        if (_changeTrackingEnabled && _objectState != ObjectState.Added)
        {
            if (!OriginalValues.ContainsKey(propertyName))
            {
                OriginalValues[propertyName] = value;
            }
        }
    }

    // Captures the original value for a Complex property that is changing.
    internal void RecordComplexTypeOriginalValue(string propertyName, object value)
    {
        if (_changeTrackingEnabled && _objectState != ObjectState.Added)
        {
            if (!ComplexTypeOriginalValues.ContainsKey(propertyName))
            {
                if (!ReferenceEquals(value, null))
                {
                    ComplexTypeOriginalValues[propertyName] = value.Clone();
                }
                else
                {
                    ComplexTypeOriginalValues[propertyName] = null;
                }
            }
        }
    }

    // Records an addition to collection valued properties on SelfTracking Entities.
    internal void RecordAdditionToCollectionProperties(string propertyName, object value)
    {
        if (_changeTrackingEnabled)
        {
            // Add the entity back after deleting it, we should do nothing here then
            if (ObjectsRemovedFromCollectionProperties.ContainsKey(propertyName)
                && ObjectsRemovedFromCollectionProperties[propertyName].Contains(value))
            {
                ObjectsRemovedFromCollectionProperties[propertyName].Remove(value);
                if (ObjectsRemovedFromCollectionProperties[propertyName].Count == 0)
                {
                    ObjectsRemovedFromCollectionProperties.Remove(propertyName);
                }
                OnUpdateHasChanges();
                return;
            }

            if (!ObjectsAddedToCollectionProperties.ContainsKey(propertyName))
            {
                ObjectsAddedToCollectionProperties[propertyName] = new ObjectList();
                ObjectsAddedToCollectionProperties[propertyName].Add(value);
            }
            else
            {
                ObjectsAddedToCollectionProperties[propertyName].Add(value);
            }
            OnUpdateHasChanges();
        }
    }

    // Records a removal to collection valued properties on SelfTracking Entities.
    internal void RecordRemovalFromCollectionProperties(string propertyName, object value)
    {
        if (_changeTrackingEnabled)
        {
            // Delete the entity back after adding it, we should do nothing here then
            if (ObjectsAddedToCollectionProperties.ContainsKey(propertyName)
                && ObjectsAddedToCollectionProperties[propertyName].Contains(value))
            {
                ObjectsAddedToCollectionProperties[propertyName].Remove(value);
                if (ObjectsAddedToCollectionProperties[propertyName].Count == 0)
                {
                    ObjectsAddedToCollectionProperties.Remove(propertyName);
                }
                OnUpdateHasChanges();
                return;
            }

            if (!ObjectsRemovedFromCollectionProperties.ContainsKey(propertyName))
            {
                ObjectsRemovedFromCollectionProperties[propertyName] = new ObjectList();
                ObjectsRemovedFromCollectionProperties[propertyName].Add(value);
            }
            else
            {
                if (!ObjectsRemovedFromCollectionProperties[propertyName].Contains(value))
                {
                    ObjectsRemovedFromCollectionProperties[propertyName].Add(value);
                }
            }
            OnUpdateHasChanges();
        }
    }
    #endregion
}

#region "Enum for Object State"
[Flags]
public enum ObjectState
{
    Unchanged = 0x1,
    Added = 0x2,
    Modified = 0x4,
    Deleted = 0x8
}
#endregion

[CollectionDataContract (Name = "ObjectsAddedToCollectionProperties",
    ItemName = "AddedObjectsForProperty", KeyName = "CollectionPropertyName", ValueName = "AddedObjects")]
public class ObjectsAddedToCollectionProperties : Dictionary<string, ObjectList> { }

[CollectionDataContract (Name = "ObjectsRemovedFromCollectionProperties",
    ItemName = "DeletedObjectsForProperty", KeyName = "CollectionPropertyName",ValueName = "DeletedObjects")]
public class ObjectsRemovedFromCollectionProperties : Dictionary<string, ObjectList> { }

[CollectionDataContract(Name = "OriginalValuesDictionary",
    ItemName = "OriginalValues", KeyName = "Name", ValueName = "OriginalValue")]
<#+
WriteEnumTypeSerializationInfo(itemCollection, code);
#>
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 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();

        using (Stream stream = new MemoryStream())
        {
            var serializer = new DataContractSerializer(type);
            serializer.WriteObject(stream, source);
            stream.Seek(0, SeekOrigin.Begin);
            return (T) serializer.ReadObject(stream);
        }
    }
}
<#+
}

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 WriteClientQuery()
{
#>

/// <summary>
/// ClientQuery stores a list of include paths and serialized expression tree.
/// </summary>
[DataContract(IsReference = true)]
public class ClientQuery
{
    #region Constructor

    public ClientQuery(List<string> includeList, XElement xmlExpression)
    {
        IncludeList = includeList;
        XmlExpression = xmlExpression;
    }

    #endregion Constructor

    #region Public DataMember

    [DataMember]
    public List<string> IncludeList { get; set; }

    [DataMember]
    public XElement XmlExpression { get; set; }

    #endregion Public DataMember
}
<#+
}

void WriteClientQueryForWPF()
{
#>

/// <summary>
/// ClientQuery stores a list of include paths and serialized expression tree.
/// </summary>
[DataContract(IsReference = true)]
public class ClientQuery
{
    #region Constructor

    public ClientQuery(List<string> includeList, XElement xmlExpression)
    {
        IncludeList = includeList;
        CompressedExpression = CompressUtil.Compress(xmlExpression);
    }

    #endregion Constructor

    #region Public DataMember

    [DataMember]
    public List<string> IncludeList { get; set; }

    [DataMember]
    public string CompressedExpression { get; set; }

    public XElement XmlExpression
    {
        get { return CompressUtil.Decompress(CompressedExpression); }
    }

    #endregion Public DataMember
}

/// <summary>
/// Helper class to compress and decompress between XElement and string.
/// </summary>
public static class CompressUtil
{
    public static string Compress(XElement xmlExpression)
    {
        using (MemoryStream gStream = new MemoryStream())
        {
            using (GZipStream gZipStream = new GZipStream(gStream, CompressionMode.Compress))
            using (MemoryStream mStream = new MemoryStream())
            {
                xmlExpression.Save(mStream);
                mStream.Seek(0, SeekOrigin.Begin);
                var unCompressedBytes = mStream.ToArray();
                gZipStream.Write(unCompressedBytes, 0, unCompressedBytes.Length);
            }
            return Convert.ToBase64String(gStream.ToArray());
        }
    }

    public static XElement Decompress(string compressedText)
    {
        using (MemoryStream gStream = new MemoryStream(Convert.FromBase64String(compressedText)))
        using (GZipStream gZipStream = new GZipStream(gStream, CompressionMode.Decompress))
        using (MemoryStream mStream = new MemoryStream())
        {
            var bufffer = new byte[0x400];
            var count = gZipStream.Read(bufffer, 0, bufffer.Length);
            while (count != 0)
            {
                mStream.Write(bufffer, 0, count);
                count = gZipStream.Read(bufffer, 0, bufffer.Length);
            }
            mStream.Seek(0, SeekOrigin.Begin);
            return XElement.Load(mStream);
        }
    }
}
<#+
}

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

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

private void DefineMetadata()
{
    TemplateMetadata[MetadataConstants.TT_TEMPLATE_NAME] = "CSharpSelfTracking.Types";
    TemplateMetadata[MetadataConstants.TT_TEMPLATE_VERSION] = "5.0";
}

// 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.GetRelevantMembersForUpdate(eSet, eType, false))
                            {
                                _concurrencyMembers.Add(member);
                            }
                        }
                    }
                }
            }
            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 BuildPropertyDataAnnotations(EdmProperty edmProperty)
{
    MetadataProperty displayMetadataProperty = edmProperty.MetadataProperties.FirstOrDefault(mpr => mpr.Name == @"http://selftrackingentity.codeplex.com/EFExtension/2012/05:SteDisplay");

    if (displayMetadataProperty == null)
    {
        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 });
                
        PropertyExtension propertyExtension = new PropertyExtension(modelElement, null);

        SteDisplay steDisplay = propertyExtension.Display as SteDisplay;
        if (steDisplay != null)
        {
            if (!string.IsNullOrWhiteSpace(steDisplay.ResourceType) && !string.IsNullOrWhiteSpace(steDisplay.Name) && !string.IsNullOrWhiteSpace(steDisplay.Description))
            {
                PushIndent(CodeRegion.GetIndent(1));
                WriteLine("[Display(ResourceType=typeof({0}),", steDisplay.ResourceType);
                WriteLine("   Name=\"{0}\", Description=\"{1}\")]", steDisplay.Name, steDisplay.Description);
                PopIndent();
            }
            else if (!string.IsNullOrWhiteSpace(steDisplay.Name) && !string.IsNullOrWhiteSpace(steDisplay.Description))
            {
                PushIndent(CodeRegion.GetIndent(1));
                WriteLine("[Display(Name=\"{0}\", Description=\"{1}\")]", steDisplay.Name, steDisplay.Description);
                PopIndent();
            }
            else if (!string.IsNullOrWhiteSpace(steDisplay.Name))
            {
                PushIndent(CodeRegion.GetIndent(1));
                WriteLine("[Display(Name=\"{0}\")]", steDisplay.Name);
                PopIndent();
            }
        }
    }
    
    MetadataProperty validationsMetadataProperty = edmProperty.MetadataProperties.FirstOrDefault(mpr => mpr.Name == @"http://selftrackingentity.codeplex.com/EFExtension/2012/05:SteValidations");

    if (validationsMetadataProperty == null)
    {
        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 });
                
        PropertyExtension propertyExtension = new PropertyExtension(modelElement, null);

        PushIndent(CodeRegion.GetIndent(1));
        foreach(ValidationsBase validation in propertyExtension.Validations)
        {
            switch (validation.GetType().Name)
            {
                case "RangeInt16":
                    if (!string.IsNullOrWhiteSpace(validation.ErrorMessageResourceType) && !string.IsNullOrWhiteSpace(validation.ErrorMessageResourceName))
                    {
                        WriteLine("[Range(typeof(Int16), \"{0}\", \"{1}\",", (validation as RangeInt16).MinimumValue, (validation as RangeInt16).MaximumValue);
                        WriteLine("   ErrorMessageResourceType=typeof({0}),", validation.ErrorMessageResourceType);
                        WriteLine("   ErrorMessageResourceName=\"{0}\")]", validation.ErrorMessageResourceName);
                    }
                    else 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.ErrorMessageResourceType) && !string.IsNullOrWhiteSpace(validation.ErrorMessageResourceName))
                    {
                        WriteLine("[Range({0}, {1},", (validation as RangeInt32).MinimumValue, (validation as RangeInt32).MaximumValue);
                        WriteLine("   ErrorMessageResourceType=typeof({0}),", validation.ErrorMessageResourceType);
                        WriteLine("   ErrorMessageResourceName=\"{0}\")]", validation.ErrorMessageResourceName);
                    }
                    else 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.ErrorMessageResourceType) && !string.IsNullOrWhiteSpace(validation.ErrorMessageResourceName))
                    {
                        WriteLine("[Range(typeof(Int64), \"{0}\", \"{1}\",", (validation as RangeInt64).MinimumValue, (validation as RangeInt64).MaximumValue);
                        WriteLine("   ErrorMessageResourceType=typeof({0}),", validation.ErrorMessageResourceType);
                        WriteLine("   ErrorMessageResourceName=\"{0}\")]", validation.ErrorMessageResourceName);
                    }
                    else 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.ErrorMessageResourceType) && !string.IsNullOrWhiteSpace(validation.ErrorMessageResourceName))
                    {
                        WriteLine("[Range(typeof(Single), \"{0}\", \"{1}\",", (validation as RangeSingle).MinimumValue, (validation as RangeSingle).MaximumValue);
                        WriteLine("   ErrorMessageResourceType=typeof({0}),", validation.ErrorMessageResourceType);
                        WriteLine("   ErrorMessageResourceName=\"{0}\")]", validation.ErrorMessageResourceName);
                    }
                    else 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 "RangeDouble":
                    if (!string.IsNullOrWhiteSpace(validation.ErrorMessageResourceType) && !string.IsNullOrWhiteSpace(validation.ErrorMessageResourceName))
                    {
                        WriteLine("[Range({0}, {1},", (validation as RangeDouble).MinimumValue, (validation as RangeDouble).MaximumValue);
                        WriteLine("   ErrorMessageResourceType=typeof({0}),", validation.ErrorMessageResourceType);
                        WriteLine("   ErrorMessageResourceName=\"{0}\")]", validation.ErrorMessageResourceName);
                    }
                    else 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 "RangeDecimal":
                    if (!string.IsNullOrWhiteSpace(validation.ErrorMessageResourceType) && !string.IsNullOrWhiteSpace(validation.ErrorMessageResourceName))
                    {
                        WriteLine("[Range(typeof(Decimal), \"{0}\", \"{1}\",", (validation as RangeDecimal).MinimumValue, (validation as RangeDecimal).MaximumValue);
                        WriteLine("   ErrorMessageResourceType=typeof({0}),", validation.ErrorMessageResourceType);
                        WriteLine("   ErrorMessageResourceName=\"{0}\")]", validation.ErrorMessageResourceName);
                    }
                    else 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 "RangeDateTime":
                    if (!string.IsNullOrWhiteSpace(validation.ErrorMessageResourceType) && !string.IsNullOrWhiteSpace(validation.ErrorMessageResourceName))
                    {
                        WriteLine("[Range(typeof(DateTime), \"{0}\", \"{1}\",", (validation as RangeDateTime).MinimumValue, (validation as RangeDateTime).MaximumValue);
                        WriteLine("   ErrorMessageResourceType=typeof({0}),", validation.ErrorMessageResourceType);
                        WriteLine("   ErrorMessageResourceName=\"{0}\")]", validation.ErrorMessageResourceName);
                    }
                    else 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 "RangeDateTimeOffset":
                    if (!string.IsNullOrWhiteSpace(validation.ErrorMessageResourceType) && !string.IsNullOrWhiteSpace(validation.ErrorMessageResourceName))
                    {
                        WriteLine("[Range(typeof(DateTimeOffset), \"{0}\", \"{1}\",", (validation as RangeDateTimeOffset).MinimumValue, (validation as RangeDateTimeOffset).MaximumValue);
                        WriteLine("   ErrorMessageResourceType=typeof({0}),", validation.ErrorMessageResourceType);
                        WriteLine("   ErrorMessageResourceName=\"{0}\")]", validation.ErrorMessageResourceName);
                    }
                    else if (!string.IsNullOrWhiteSpace(validation.ErrorMessage))
                    {
                        WriteLine("[Range(typeof(DateTimeOffset), \"{0}\", \"{1}\", ErrorMessage=\"{2}\")]", (validation as RangeDateTimeOffset).MinimumValue, (validation as RangeDateTimeOffset).MaximumValue, validation.ErrorMessage);
                    }
                    else
                    {
                        WriteLine("[Range(typeof(DateTimeOffset), \"{0}\", \"{1}\")]", (validation as RangeDateTimeOffset).MinimumValue, (validation as RangeDateTimeOffset).MaximumValue);
                    }
                    break;
                case "RangeTimeSpan":
                    if (!string.IsNullOrWhiteSpace(validation.ErrorMessageResourceType) && !string.IsNullOrWhiteSpace(validation.ErrorMessageResourceName))
                    {
                        WriteLine("[Range(typeof(TimeSpan), \"{0}\", \"{1}\",", (validation as RangeTimeSpan).MinimumValue, (validation as RangeTimeSpan).MaximumValue);
                        WriteLine("   ErrorMessageResourceType=typeof({0}),", validation.ErrorMessageResourceType);
                        WriteLine("   ErrorMessageResourceName=\"{0}\")]", validation.ErrorMessageResourceName);
                    }
                    else if (!string.IsNullOrWhiteSpace(validation.ErrorMessage))
                    {
                        WriteLine("[Range(typeof(TimeSpan), \"{0}\", \"{1}\", ErrorMessage=\"{2}\")]", (validation as RangeTimeSpan).MinimumValue, (validation as RangeTimeSpan).MaximumValue, validation.ErrorMessage);
                    }
                    else
                    {
                        WriteLine("[Range(typeof(TimeSpan), \"{0}\", \"{1}\")]", (validation as RangeTimeSpan).MinimumValue, (validation as RangeTimeSpan).MaximumValue);
                    }
                    break;
                case "RegularExpression":
                    if (!string.IsNullOrWhiteSpace(validation.ErrorMessageResourceType) && !string.IsNullOrWhiteSpace(validation.ErrorMessageResourceName))
                    {
                        WriteLine("[RegularExpression(\"{0}\",", (validation as RegularExpression).Pattern);
                        WriteLine("   ErrorMessageResourceType=typeof({0}),", validation.ErrorMessageResourceType);
                        WriteLine("   ErrorMessageResourceName=\"{0}\")]", validation.ErrorMessageResourceName);
                    }
                    else 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.ErrorMessageResourceType) && !string.IsNullOrWhiteSpace(validation.ErrorMessageResourceName))
                    {
                        WriteLine("[Required(ErrorMessageResourceType=typeof({0}),", validation.ErrorMessageResourceType);
                        WriteLine("   ErrorMessageResourceName=\"{0}\")]", validation.ErrorMessageResourceName);
                    }
                    else if (!string.IsNullOrWhiteSpace(validation.ErrorMessage))
                    {
                        WriteLine("[Required(ErrorMessage=\"{0}\")]", validation.ErrorMessage);
                    }
                    else
                    {
                        WriteLine("[Required]");
                    }
                    break;
                case "StringLength":
                    if (!string.IsNullOrWhiteSpace(validation.ErrorMessageResourceType) && !string.IsNullOrWhiteSpace(validation.ErrorMessageResourceName))
                    {
                        WriteLine("[StringLength({0}, MinimumLength={1},", (validation as StringLength).MaximumLength, (validation as StringLength).MinimumLength);
                        WriteLine("   ErrorMessageResourceType=typeof({0}),", validation.ErrorMessageResourceType);
                        WriteLine("   ErrorMessageResourceName=\"{0}\")]", validation.ErrorMessageResourceName);
                    }
                    else if (!string.IsNullOrWhiteSpace(validation.ErrorMessage))
                    {
                        WriteLine("[StringLength({0}, MinimumLength={1}, ErrorMessage=\"{2}\")]", (validation as StringLength).MaximumLength, (validation as StringLength).MinimumLength, validation.ErrorMessage);
                    }
                    else if ((validation as StringLength).MinimumLength != 0)
                    {
                        WriteLine("[StringLength({0}, MinimumLength={1})]", (validation as StringLength).MaximumLength, (validation as StringLength).MinimumLength);
                    }
                    else
                    {
                        WriteLine("[StringLength({0})]", (validation as StringLength).MaximumLength);
                    }
                    break;
                case "CustomValidation":
                    if (!string.IsNullOrWhiteSpace((validation as CustomValidation).ValidatorType) && !string.IsNullOrWhiteSpace((validation as CustomValidation).Method))
                    {
                        WriteLine("[CustomValidation(typeof({0}), \"{1}\")]", (validation as CustomValidation).ValidatorType, (validation as CustomValidation).Method);
                    }
                    else
                    {
                        WriteLine("//{1}[{0}]", validation.ToString(), validation.GetType().Name);
                    }
                    break;
                default:
                    WriteLine("//{1}[{0}]", validation.ToString(), validation.GetType().Name);
                    break;
            }                            
        }
        PopIndent();
    }
}

public void BuildEntityDataAnnotations(EntityType entity)
{
    MetadataProperty validationsMetadataProperty = entity.MetadataProperties.FirstOrDefault(mpr => mpr.Name == @"http://selftrackingentity.codeplex.com/EFExtension/2012/05:SteValidations");

    if (validationsMetadataProperty != null)
    {
        XElement modelElement = new XElement("Model", new object[] {validationsMetadataProperty.Value as XElement});
                
        EntityExtension entityExtension = new EntityExtension(modelElement, null);

        foreach(ValidationsBase validation in entityExtension.Validations)
        {
            switch (validation.GetType().Name)
            {
                case "CustomValidation":
                    if (!string.IsNullOrWhiteSpace((validation as CustomValidation).ValidatorType) && !string.IsNullOrWhiteSpace((validation as CustomValidation).Method))
                    {
                        WriteLine("[CustomValidation(typeof({0}), \"{1}\")]", (validation as CustomValidation).ValidatorType, (validation as CustomValidation).Method);
                    }
                    else
                    {
                        WriteLine("//{1}[{0}]", validation.ToString(), validation.GetType().Name);
                    }
                    break;
                default:
                    WriteLine("//{1}[{0}]", validation.ToString(), validation.GetType().Name);
                    break;
            }                            
        }
    }
}
#>

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

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

License

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


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

Comments and Discussions