File: System\Data\EntityModel\Emitters\ClientApiGenerator.cs
Project: ndp\fx\src\DataWeb\Design\System.Data.Services.Design.csproj (System.Data.Services.Design)
//---------------------------------------------------------------------
// <copyright file="ClientApiGenerator.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner       Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
 
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections;
using System.Collections.Generic;
using System.Data.EntityModel.Emitters;
using System.Data.Metadata.Edm;
using System.Data.Services.Design;
using System.Diagnostics;
using System.IO;
using System.Linq;
 
namespace System.Data.Metadata.Edm
{
    internal static partial class Helper
    {
        internal static bool IsCollectionType(GlobalItem item)
        {
            return (BuiltInTypeKind.CollectionType == item.BuiltInTypeKind);
        }
        internal static bool IsComplexType(EdmType type)
        {
            return (BuiltInTypeKind.ComplexType == type.BuiltInTypeKind);
        }
        internal static bool IsEntitySet(EntitySetBase entitySetBase)
        {
            return BuiltInTypeKind.EntitySet == entitySetBase.BuiltInTypeKind;
        }
        internal static bool IsPrimitiveType(EdmType type)
        {
            return (BuiltInTypeKind.PrimitiveType == type.BuiltInTypeKind);
        }
    }
 
    internal static class TypeSemantics
    {
        internal static bool IsComplexType(TypeUsage type)
        {
            return Helper.IsComplexType(type.EdmType);
        }
    }
 
    internal static class EdmProviderManifest
    {
        // System Facet Info
        /// <summary>
        /// Name of the MaxLength Facet
        /// </summary>
        internal const string MaxLengthFacetName = "MaxLength";
 
        /// <summary>
        /// Name of the Unicode Facet
        /// </summary>
        internal const string UnicodeFacetName = "Unicode";
 
        /// <summary>
        /// Name of the FixedLength Facet
        /// </summary>
        internal const string FixedLengthFacetName = "FixedLength";
 
        /// <summary>
        /// Name of the DateTimeKind Facet
        /// </summary>
        internal const string DateTimeKindFacetName = "DateTimeKind";
 
        /// <summary>
        /// Name of the PreserveSeconds Facet
        /// </summary>
        internal const string PreserveSecondsFacetName = "PreserveSeconds";
 
        /// <summary>
        /// Name of the Precision Facet
        /// </summary>
        internal const string PrecisionFacetName = "Precision";
 
        /// <summary>
        /// Name of the Scale Facet
        /// </summary>
        internal const string ScaleFacetName = "Scale";
 
        /// <summary>
        /// Name of the Nullable Facet
        /// </summary>
        internal const string NullableFacetName = "Nullable";
 
        /// <summary>
        /// Name of the DefaultValue Facet
        /// </summary>
        internal const string DefaultValueFacetName = "DefaultValue";
 
        ///// <summary>
        ///// Name of the DefaultType metadata property
        ///// </summary>
        //internal const string DefaultTypeMetadataPropertyName = "DefaultType";
 
        /// <summary>
        /// Name of the Collation Facet
        /// </summary>
        internal const string CollationFacetName = "Collation";
    }
 
    internal static class TypeSystem
    {
        private static readonly System.Reflection.MethodInfo s_getDefaultMethod = typeof(TypeSystem).GetMethod("GetDefault", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
        private static T GetDefault<T>() { return default(T); }
        
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining | System.Runtime.CompilerServices.MethodImplOptions.NoOptimization)]
        internal static object GetDefaultValue(Type type)
        {
            // null is always the default for non value types and Nullable<>
            if (!type.IsValueType ||
                (type.IsGenericType &&
                 typeof(Nullable<>) == type.GetGenericTypeDefinition()))
            {
                return null;
            }
            System.Reflection.MethodInfo getDefaultMethod = s_getDefaultMethod.MakeGenericMethod(type);
            object defaultValue = getDefaultMethod.Invoke(null, new object[] { });
            return defaultValue;
        }
    }
 
    internal static class Schema
    {
        public const string CodeGenerationSchemaNamespace = "http://schemas.microsoft.com/ado/2006/04/codegeneration";
    }
}
 
namespace System.Data.EntityModel
{
    /// <summary>
    /// Summary description for ClientApiGenerator.
    /// </summary>
    internal sealed class ClientApiGenerator : IDisposable
    {
        #region Instance Fields
        private CodeCompileUnit _compileUnit;
        private bool _isLanguageCaseSensitive = true;
 
        private EdmItemCollection _edmItemCollection;
        private FixUpCollection _fixUps;
        private AttributeEmitter _attributeEmitter;
        private EntityClassGenerator _generator;
        private List<EdmSchemaError> _errors;
        private TypeReference _typeReference = new TypeReference();
        private string _sourceEdmNamespaceName;
        private string _defaultContainerNamespace;
        private string _namespacePrefix;
        private Dictionary<string, string> _namespaceMap;
        private EventHandler<TypeGeneratedEventArgs> _onTypeGenerated;
        private EventHandler<PropertyGeneratedEventArgs> _onPropertyGenerated;
        #endregion
 
        #region Public Methods
        public ClientApiGenerator(object sourceSchema, EdmItemCollection edmItemCollection, EntityClassGenerator generator, List<EdmSchemaError> errors, string namespacePrefix)
        {
            Debug.Assert(edmItemCollection != null, "edmItemCollection is null");
            Debug.Assert(generator != null, "generator is null");
            Debug.Assert(errors != null, "errors is null");
 
            _edmItemCollection = edmItemCollection;
            _generator = generator;
            _errors = errors;
            _attributeEmitter = new AttributeEmitter(_typeReference);
            _namespacePrefix = namespacePrefix;
 
            // generate map for inherited types and prefixed types
            _namespaceMap = new Dictionary<string, string>();
 
            _onTypeGenerated = new EventHandler<TypeGeneratedEventArgs>(TypeGeneratedEventHandler);
            _onPropertyGenerated = new EventHandler<PropertyGeneratedEventArgs>(PropertyGeneratedEventHandler);
 
            //// This constructor can be called multiple times with the same EntityClassGenerator. That is, many instances
            //// of the ClientApiGenerator can add handlers to one instance of the EntityClassGenerator.
            //// Make sure the handlers are not left on the EntityClassGenerator after the ClientApiGenerator is no longer in use.
            //// See the Dispose method 
 
            _generator.OnTypeGenerated += _onTypeGenerated;
            _generator.OnPropertyGenerated += _onPropertyGenerated;
        }
 
        public void Dispose()
        {
            //// Make sure the handlers are not left on the EntityClassGenerator after the ClientApiGenerator is no longer in use.
 
            _generator.OnTypeGenerated -= _onTypeGenerated;
            _generator.OnPropertyGenerated -= _onPropertyGenerated;
        }
 
        internal EdmItemCollection EdmItemCollection
        {
            get { return this._edmItemCollection; }
        }
 
        public string NamespacePrefix
        {
            get { return this._namespacePrefix; }
        }
 
        public Dictionary<string, string> NamespaceMap
        {
            get { return this._namespaceMap; }
        }
 
        /// <summary>
        /// Does code generation emits collections inherited from DataServiceCollection
        /// </summary>
        internal bool UseDataServiceCollection
        {
            get { return _generator.UseDataServiceCollection; }
        }
 
        /// <summary>Version for which to generate code.</summary>
        internal DataServiceCodeVersion Version
        {
            get { return _generator.Version; }
        }
 
        /// <summary>
        /// Parses a source Schema and outputs client-side generated code to
        /// the output TextWriter.
        /// </summary>
        /// <param name="schema">The source Schema</param>
        /// <param name="output">The TextWriter in which to write the output</param>
        /// <param name="outputUri">The Uri for the output. Can be null.</param>
        /// <returns>A list of GeneratorErrors.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification="Reviewed")]
        internal void GenerateCode(LazyTextWriterCreator target)
        {
            Debug.Assert(target != null, "target parameter is null");
 
            IndentedTextWriter indentedTextWriter = null;
            System.IO.Stream tempFileStream = null;
            System.IO.StreamReader reader = null;
            System.IO.StreamWriter writer = null;
            TempFileCollection tempFiles = null;
            try
            {
                CodeDomProvider provider = null;
                switch (Language)
                {
                    case LanguageOption.GenerateCSharpCode:
                        provider = new Microsoft.CSharp.CSharpCodeProvider();
                        break;
 
                    case LanguageOption.GenerateVBCode:
                        provider = new Microsoft.VisualBasic.VBCodeProvider();
                        break;
                }
 
                _isLanguageCaseSensitive = (provider.LanguageOptions & LanguageOptions.CaseInsensitive) == 0;
 
                new NamespaceEmitter(this, this.NamespacePrefix, target.TargetFilePath).Emit();
 
                // if there were errors we don't need the output file
                if (RealErrorsExist)
                {
                    return;
                }
 
                if (FixUps.Count == 0 || !FixUpCollection.IsLanguageSupported(Language))
                {
                    indentedTextWriter = new IndentedTextWriter(target.GetOrCreateTextWriter(), "\t");
                }
                else
                {
                    // need to write to a temporary file so we can do fixups...
                    tempFiles = new TempFileCollection(Path.GetTempPath());
                    string filename = Path.Combine(tempFiles.TempDir, "EdmCodeGenFixup-" + Guid.NewGuid().ToString() + ".tmp");
                    tempFiles.AddFile(filename, false);
                    tempFileStream = new System.IO.FileStream(filename, System.IO.FileMode.CreateNew, System.IO.FileAccess.ReadWrite,
                        System.IO.FileShare.None);
                    indentedTextWriter = new IndentedTextWriter(new System.IO.StreamWriter(tempFileStream), "\t");
                }
 
                CodeGeneratorOptions styleOptions = new CodeGeneratorOptions();
                styleOptions.BracingStyle = "C";
                styleOptions.BlankLinesBetweenMembers = false;
                styleOptions.VerbatimOrder = true;
                provider.GenerateCodeFromCompileUnit(CompileUnit, indentedTextWriter, styleOptions);
 
                // if we wrote to a temp file need to post process the file...
                if (tempFileStream != null)
                {
                    indentedTextWriter.Flush();
                    tempFileStream.Seek(0, System.IO.SeekOrigin.Begin);
                    reader = new System.IO.StreamReader(tempFileStream);
                    FixUps.Do(reader, target.GetOrCreateTextWriter(), Language, !String.IsNullOrEmpty(SourceObjectNamespaceName));
                }
            }
            catch (System.UnauthorizedAccessException ex)
            {
                AddError(ModelBuilderErrorCode.SecurityError, EdmSchemaErrorSeverity.Error, ex);
            }
            catch (System.IO.FileNotFoundException ex)
            {
                AddError(ModelBuilderErrorCode.FileNotFound, EdmSchemaErrorSeverity.Error, ex);
            }
            catch (System.Security.SecurityException ex)
            {
                AddError(ModelBuilderErrorCode.SecurityError, EdmSchemaErrorSeverity.Error, ex);
            }
            catch (System.IO.DirectoryNotFoundException ex)
            {
                AddError(ModelBuilderErrorCode.DirectoryNotFound, EdmSchemaErrorSeverity.Error, ex);
            }
            catch (System.IO.IOException ex)
            {
                AddError(ModelBuilderErrorCode.IOException, EdmSchemaErrorSeverity.Error, ex);
            }
            finally
            {
                if (indentedTextWriter != null)
                {
                    indentedTextWriter.Close();
                }
                if (tempFileStream != null)
                {
                    tempFileStream.Close();
                }
                if (tempFiles != null)
                {
                    tempFiles.Delete();
                    ((IDisposable)tempFiles).Dispose();
                }
                if (reader != null)
                {
                    reader.Close();
                }
                if (writer != null)
                {
                    writer.Close();
                }
            }
        }
 
        /// <summary>
        /// Verification code invoked for types
        /// </summary>
        /// <param name="item">The type being generated</param>
        internal void VerifyLanguageCaseSensitiveCompatibilityForType(GlobalItem item)
        {
            if (_isLanguageCaseSensitive)
            {
                return; // no validation necessary
            }
 
            try
            {
                _edmItemCollection.GetItem<GlobalItem>(
                                                        Identity(item),
                                                        true   // ignore case
                                                    );
            }
            catch (InvalidOperationException ex)
            {
                AddError(ModelBuilderErrorCode.IncompatibleSettingForCaseSensitiveOption, EdmSchemaErrorSeverity.Error, ex);
            }
        }
 
        /// <summary>
        /// Verification code invoked for properties
        /// </summary>
        /// <param name="item">The property or navigation property being generated</param>
        internal void VerifyLanguageCaseSensitiveCompatibilityForProperty(EdmMember item)
        {
            if (_isLanguageCaseSensitive)
            {
                return; // no validation necessary
            }
 
            Debug.Assert(item != null);
 
            ReadOnlyMetadataCollection<EdmMember> members = item.DeclaringType.Members;
            HashSet<string> set = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
 
            foreach (EdmMember member in members)
            {
                if (set.Contains(Identity(member)) &&
                    String.Equals(Identity(item), Identity(member), StringComparison.OrdinalIgnoreCase))
                {
                    AddError(ModelBuilderErrorCode.IncompatibleSettingForCaseSensitiveOption, EdmSchemaErrorSeverity.Error,
                                new InvalidOperationException(Strings.PropertyExistsWithDifferentCase(Identity(item))));
                }
                else
                {
                    set.Add(Identity(member));
                }
            }
        }
 
        /// <summary>
        /// Verification code invoked for entity sets
        /// </summary>
        /// <param name="item">The entity container being generated</param>
        internal void VerifyLanguageCaseSensitiveCompatibilityForEntitySet(System.Data.Metadata.Edm.EntityContainer item)
        {
            if (_isLanguageCaseSensitive)
            {
                return; // no validation necessary
            }
 
            Debug.Assert(item != null);
 
            HashSet<string> set = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
 
            foreach (EntitySetBase entitySetBase in item.BaseEntitySets)
            {
                if (Helper.IsEntitySet(entitySetBase))
                {
                    EntitySet entitySet = (EntitySet)entitySetBase;
                    if (set.Contains(Identity(entitySet)))
                    {
                        AddError(ModelBuilderErrorCode.IncompatibleSettingForCaseSensitiveOption, EdmSchemaErrorSeverity.Error,
                                    new InvalidOperationException(Strings.EntitySetExistsWithDifferentCase(Identity(entitySet))));
                    }
                    else
                    {
                        set.Add(Identity(entitySet));
                    }
                }
            }
        }
 
        #endregion
 
        #region Internal Properties
 
        internal LanguageOption Language
        {
            get
            {
                return _generator.LanguageOption;
            }
        }
 
        internal TypeReference TypeReference
        {
            get { return _typeReference; }
        }
 
        internal CodeCompileUnit CompileUnit
        {
            get
            {
                if (_compileUnit == null)
                    _compileUnit = new CodeCompileUnit();
 
                return _compileUnit;
            }
        }
 
        public void AddError(string message, ModelBuilderErrorCode errorCode, EdmSchemaErrorSeverity severity)
        {
        }
 
        public void AddError(ModelBuilderErrorCode errorCode, EdmSchemaErrorSeverity severity, Exception ex)
        {
        }
 
        internal void AddError(string message, ModelBuilderErrorCode errorCode, EdmSchemaErrorSeverity severity, Exception ex)
        {
        }
 
        /// <summary>
        /// Check collection for any real errors (Severity != Warning)
        /// </summary>
        public bool RealErrorsExist
        {
            get
            {
                foreach (EdmSchemaError error in _errors)
                {
                    if (error.Severity != EdmSchemaErrorSeverity.Warning)
                        return true;
                }
                return false;
            }
        }
 
        public IEnumerable<GlobalItem> GetSourceTypes()
        {
            foreach (EntityContainer container in _edmItemCollection.GetItems<EntityContainer>())
            {
                BuiltInTypeKind kind = container.BuiltInTypeKind;
                yield return container;
            }
 
            foreach (EdmType item in _edmItemCollection.GetItems<EdmType>())
            {
                switch (item.BuiltInTypeKind)
                {
                    case BuiltInTypeKind.AssociationType:
                    case BuiltInTypeKind.ComplexType:
                    case BuiltInTypeKind.EntityType:
                        yield return item;
                        break;
                    case BuiltInTypeKind.EdmFunction:
                    case BuiltInTypeKind.PrimitiveType:
                        break;
                    default:
                        Debug.Assert(false, item.BuiltInTypeKind.ToString());
                        break;
                }
            }
        }
 
        public string GetClientTypeNamespace(string serviceTypeNamespace)
        {
            if (string.IsNullOrEmpty(this.NamespacePrefix)) return serviceTypeNamespace;
 
            if (string.IsNullOrEmpty(serviceTypeNamespace) ||
                ((this.DefaultContainerNamespace != null) && (this.DefaultContainerNamespace == serviceTypeNamespace)))
            {
                return this.NamespacePrefix;
            }
            else
            {
                return this.NamespacePrefix + "." + serviceTypeNamespace;
            }
        }
 
        public string GetContainerNamespace(EntityContainer container)
        {
            if (container == null) return null;
 
            string namespaceName = null;
            EntitySetBase baseEntitySet = container.BaseEntitySets.FirstOrDefault();
            if (null != baseEntitySet)
            {
                namespaceName = baseEntitySet.ElementType.NamespaceName;
            }
 
            return namespaceName;
        }
 
        public CodeTypeReference GetLeastPossibleQualifiedTypeReference(EdmType type)
        {
            string typeRef;
            string clientNamespace = GetClientTypeNamespace(type.NamespaceName);
            if (clientNamespace == SourceEdmNamespaceName)
            {
                // we are already generating in this namespace, no need to qualify it
                typeRef = type.Name;
            }
            else
            {
                typeRef = GetObjectNamespace(clientNamespace) + "." + type.Name;
            }
 
            return TypeReference.FromString(typeRef);
        }
 
        public string SourceEdmNamespaceName
        {
            get
            {
                if (null != _sourceEdmNamespaceName)
                {
                    return _sourceEdmNamespaceName;
                }
 
                // 
                foreach (GlobalItem item in GetSourceTypes())
                {
                    EdmType edm = item as EdmType;
                    if (null != edm)
                    {
                        return edm.NamespaceName;
                    }
                }
 
                return null;
            }
            set
            {
                _sourceEdmNamespaceName = value;
            }
        }
 
        public string DefaultContainerNamespace
        {
            get { return _defaultContainerNamespace; }
            set { _defaultContainerNamespace = value; }
        }
 
        public string SourceObjectNamespaceName
        {
            get
            {
                string sourceEdmNamespaceName = SourceEdmNamespaceName;
                if (!String.IsNullOrEmpty(sourceEdmNamespaceName))
                {
                    return GetObjectNamespace(sourceEdmNamespaceName);
                }
 
                return null;
            }
        }
 
        private string GetObjectNamespace(string csdlNamespaceName)
        {
            Debug.Assert(csdlNamespaceName != null, "csdlNamespaceName is null");
 
            string objectNamespace;
            if (_generator.EdmToObjectNamespaceMap.TryGetObjectNamespace(csdlNamespaceName, out objectNamespace))
            {
                return objectNamespace;
            }
 
            return csdlNamespaceName;
        }
 
 
        /// <summary>
        /// 
        /// </summary>
        /// <value></value>
        internal FixUpCollection FixUps
        {
            get
            {
                if (_fixUps == null)
                    _fixUps = new FixUpCollection();
 
                return _fixUps;
            }
        }
 
        internal AttributeEmitter AttributeEmitter
        {
            get { return _attributeEmitter; }
        }
 
        internal bool IsLanguageCaseSensitive
        {
            get { return _isLanguageCaseSensitive; }
        }
 
        internal StringComparison LanguageAppropriateStringComparer
        {
            get
            {
                if (IsLanguageCaseSensitive)
                {
                    return StringComparison.Ordinal;
                }
                else
                {
                    return StringComparison.OrdinalIgnoreCase;
                }
            }
        }
 
        /// <summary>
        /// Helper method that raises the TypeGenerated event
        /// </summary>
        /// <param name="eventArgs">The event arguments passed to the subscriber</param>
        internal void RaiseTypeGeneratedEvent(TypeGeneratedEventArgs eventArgs)
        {
            _generator.RaiseTypeGeneratedEvent(eventArgs);
        }
 
        /// <summary>
        /// Helper method that raises the PropertyGenerated event
        /// </summary>
        /// <param name="eventArgs">The event arguments passed to the subscriber</param>
        internal void RaisePropertyGeneratedEvent(PropertyGeneratedEventArgs eventArgs)
        {
            _generator.RaisePropertyGeneratedEvent(eventArgs);
        }
 
        /// <summary>
        /// Gets the collection type to be returned for a multi-valued navigation property.
        /// </summary>
        /// <returns>Type name which is decided based on UseDataServiceCollection setting.</returns>
        internal string GetRelationshipMultiplicityManyCollectionTypeName()
        {
            return this.UseDataServiceCollection ? "System.Data.Services.Client.DataServiceCollection" : "System.Collections.ObjectModel.Collection";
        }
 
        #endregion
 
        private static string Identity(EdmMember member)
        {
            return member.ToString();
        }
        private static string Identity(EntitySetBase entitySet)
        {
            return entitySet.ToString();
        }
        private static string Identity(MetadataItem item)
        {
            return item.ToString();
        }
 
        private void TypeGeneratedEventHandler(object sender, TypeGeneratedEventArgs eventArgs)
        {
            if (!this.UseDataServiceCollection)
            {
                return;
            }
 
            if (eventArgs.TypeSource.BuiltInTypeKind != BuiltInTypeKind.EntityType && 
                eventArgs.TypeSource.BuiltInTypeKind != BuiltInTypeKind.ComplexType)
            {
                return;
            }
 
            if (eventArgs.TypeSource.BuiltInTypeKind == BuiltInTypeKind.EntityType)
            {
                // Generate EntitySetAttribute only if there is exactly one entity set associated 
                // with the entity type. The DataServiceEntitySetAttribute is not generated for ComplexType(s).
                EntitySetBase entitySet = this.GetUniqueEntitySetForType((EntityType)eventArgs.TypeSource);
                if (entitySet != null)
                {
                    List<CodeAttributeDeclaration> additionalAttributes = eventArgs.AdditionalAttributes;
                    CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(
                        new CodeTypeReference(typeof(System.Data.Services.Common.EntitySetAttribute),
                            CodeTypeReferenceOptions.GlobalReference), new CodeAttributeArgument(new CodePrimitiveExpression(entitySet.Name)));
                    additionalAttributes.Add(attribute);
                }
            }
 
            //// Determine if type being generated has a base type
            if (eventArgs.BaseType != null && !String.IsNullOrEmpty(eventArgs.BaseType.BaseType))
            {
                if (this.GetSourceTypes().Where(x => x.BuiltInTypeKind == BuiltInTypeKind.EntityType).Where(x => ((EntityType)x).Name == eventArgs.BaseType.BaseType).Count() != 0)
                {
                    //// Don't generate the PropertyChanged event and OnPropertyChanged method for derived type classes
                    return;
                }
            }
 
            //// Add the INotifyPropertyChanged interface
 
            List<Type> additionalInterfaces = eventArgs.AdditionalInterfaces;
 
            additionalInterfaces.Add(typeof(System.ComponentModel.INotifyPropertyChanged));
            
            //// Add the implementation of the INotifyPropertyChanged interface
 
            //// Generate this code:
            ////
            //// CSharp:
            //// 
            //// public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
            //// 
            //// protected virtual void OnPropertyChanged(string property) {
            ////     if ((this.PropertyChanged != null)) {
            ////         this.PropertyChanged(this, new global::System.ComponentModel.PropertyChangedEventArgs(property));
            ////     }
            //// }
            //// 
            //// Visual Basic:
            ////
            //// Public Event PropertyChanged As Global.System.ComponentModel.PropertyChangedEventHandler Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
            //// Protected Overridable Sub OnPropertyChanged(ByVal [property] As String)
            ////     If (Not (Me.PropertyChangedEvent) Is Nothing) Then
            ////         RaiseEvent PropertyChanged(Me, New Global.System.ComponentModel.PropertyChangedEventArgs([property]))
            ////     End If
            //// End Sub
 
 
            CodeMemberEvent propertyChangedEvent = new CodeMemberEvent();
            propertyChangedEvent.Type = new CodeTypeReference(typeof(System.ComponentModel.PropertyChangedEventHandler), CodeTypeReferenceOptions.GlobalReference);
            propertyChangedEvent.Name = "PropertyChanged";
            propertyChangedEvent.Attributes = MemberAttributes.Public;
            propertyChangedEvent.ImplementationTypes.Add(typeof(System.ComponentModel.INotifyPropertyChanged));
 
            AttributeEmitter.AddGeneratedCodeAttribute(propertyChangedEvent);
            
            eventArgs.AdditionalMembers.Add(propertyChangedEvent);
 
            CodeMemberMethod propertyChangedMethod = new CodeMemberMethod();
            propertyChangedMethod.Name = "OnPropertyChanged";
            propertyChangedMethod.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference(typeof(System.String), CodeTypeReferenceOptions.GlobalReference), "property"));
            propertyChangedMethod.ReturnType = new CodeTypeReference(typeof(void));
 
            AttributeEmitter.AddGeneratedCodeAttribute(propertyChangedMethod);
 
            propertyChangedMethod.Statements.Add(
                    new CodeConditionStatement(
                        new CodeBinaryOperatorExpression(
                            new CodeEventReferenceExpression(new CodeThisReferenceExpression(), "PropertyChanged"),
                                CodeBinaryOperatorType.IdentityInequality,
                                new CodePrimitiveExpression(null)),
                            new CodeExpressionStatement(
                            new CodeDelegateInvokeExpression(
                                new CodeEventReferenceExpression(new CodeThisReferenceExpression(), "PropertyChanged"),
                                    new CodeExpression[] { 
                                    new CodeThisReferenceExpression(), 
                                    new CodeObjectCreateExpression(new CodeTypeReference(typeof(System.ComponentModel.PropertyChangedEventArgs), CodeTypeReferenceOptions.GlobalReference), new CodeArgumentReferenceExpression("property"))
                                }))));
            propertyChangedMethod.Attributes = MemberAttributes.Family;
            eventArgs.AdditionalMembers.Add(propertyChangedMethod);
        }
 
        private void PropertyGeneratedEventHandler(object sender, PropertyGeneratedEventArgs eventArgs)
        {
            if (!this.UseDataServiceCollection)
            {
                return;
            }
 
            if (eventArgs.PropertySource.BuiltInTypeKind != BuiltInTypeKind.EdmProperty &&
                eventArgs.PropertySource.BuiltInTypeKind != BuiltInTypeKind.NavigationProperty)
            {
                return;
            }
 
            if (((EdmMember)eventArgs.PropertySource).DeclaringType.BuiltInTypeKind != BuiltInTypeKind.EntityType &&
                ((EdmMember)eventArgs.PropertySource).DeclaringType.BuiltInTypeKind != BuiltInTypeKind.ComplexType)
            {
                return;
            }
 
            string name = eventArgs.PropertySource.BuiltInTypeKind == BuiltInTypeKind.EdmProperty ? ((EdmProperty)eventArgs.PropertySource).Name : ((NavigationProperty)eventArgs.PropertySource).Name;
            
            // Add call to the OnPropertyChanged method
            eventArgs.AdditionalAfterSetStatements.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(
                new CodeThisReferenceExpression(), "OnPropertyChanged",
                new CodeExpression[] { new CodePrimitiveExpression(name) }
                )));
        }
 
        /// <summary>Given an entity type, returns the corresponding entity set if it's unique.</summary>
        /// <param name="entityType">Given entity type.</param>
        /// <returns>Corresponding entity set if it's unique, null otherwise.</returns>
        private EntitySetBase GetUniqueEntitySetForType(EntityType entityType)
        {
            HashSet<EntitySetBase> entitySets = new HashSet<EntitySetBase>(EqualityComparerEntitySet.Default);
 
            foreach (EntityContainer container in this.EdmItemCollection.GetItems<EntityContainer>())
            {
                bool alreadyAdded = false;
                foreach (EntitySetBase es in container.BaseEntitySets
                                                      .Where(x => x.BuiltInTypeKind == BuiltInTypeKind.EntitySet &&
                                                                  x.ElementType == entityType))
                {
                    if (alreadyAdded == true) 
                    { 
                        return null; 
                    }
 
                    alreadyAdded = true;
                    entitySets.Add(es);
                }
            }
 
            if (entitySets.Count == 1)
            {
                return entitySets.Single();
            }
            else
            {
                return null;
            }
        }
 
        /// <summary>Equality comparer used for deciding whether to add EntitySet attribute to an entity type.</summary>
        public class EqualityComparerEntitySet : IEqualityComparer<EntitySetBase>
        {
            /// <summary>Cached singleton comparer.</summary>
            private static EqualityComparerEntitySet _comparer = new EqualityComparerEntitySet();
 
            /// <summary>Gets the singleton comparer.</summary>
            public static EqualityComparerEntitySet Default
            {
                get
                {
                    return _comparer;
                }
            }
 
            #region IEqualityComparer<EntitySetBase> Members
 
            /// <summary>Equality check.</summary>
            /// <param name="x">Left.</param>
            /// <param name="y">Right.</param>
            /// <returns>true if names are same, false otherwise.</returns>
            public bool Equals(EntitySetBase x, EntitySetBase y)
            {
                return (x == null && y == null) ||
                       (x != null && y != null && x.Name == y.Name);
            }
 
            /// <summary>Gets hash code for EntitySetBase.</summary>
            /// <param name="obj">Object for which to get hash code.</param>
            /// <returns>Hash code for the name.</returns>
            public int GetHashCode(EntitySetBase obj)
            {
                return (null != obj) ? obj.Name.GetHashCode() : 0;
            }
 
            #endregion
        }
    }
}