How to have a property on ObjectContext which returns the derived entities?

With EF v1, we have an ObjectContext which has one property per EntitySet. The “problem” is that if you have an entity type Person and an entity type Employee which inherits Person, you will have only one property Persons because there is only one EntitySet Persons.

If you want to add a property Employees, you can do this:

public IQueryable<Employee> Employees

{

    get

    {

        return Persons.OfType<Employee>();

    }

}


Note that your ObjectContext generated class is partial.

Very easy!

As I said yesterday in this msdn forum thread, it is possible to customize the code generator in order to have this property directly on the generated code.

I use the SampleEdmxCodeGen project to do this:

public class SampleEdmxCodeGenerator : BaseCodeGeneratorWithSite

{

    private EntityContainer _objectContext;

    private Dictionary<string, EntitySetBase> _entitySetNames;

    private Dictionary<string, List<EntityType>> _typesHierarchyToAddInObjectContext;

    private bool _first = true;

 

    public Dictionary<string, EntitySetBase> EntitySetNames

    {

        get

        {

            if (_entitySetNames == null)

                _entitySetNames = new Dictionary<string, EntitySetBase>();

            return _entitySetNames;

        }

    }

    private Dictionary<string, List<EntityType>> TypesHierarchyToAddInObjectContext

    {

        get

        {

            if (_typesHierarchyToAddInObjectContext == null)

                _typesHierarchyToAddInObjectContext = new Dictionary<string, List<EntityType>>();

            return _typesHierarchyToAddInObjectContext;

        }

    }

    private IEnumerable<EntityType> GetSubEntitiesOf(EntityType baseType)

    {

        var empty = Enumerable.Empty<EntityType>();

        if (_typesHierarchyToAddInObjectContext == null)

            return empty;

        string baseTypeName = baseType.Name;

        if (!_typesHierarchyToAddInObjectContext.ContainsKey(baseTypeName))

            return empty;

        return TypesHierarchyToAddInObjectContext[baseTypeName];

    }

    private void AddSubEntities(EntityType baseType, EntityType typeToAdd)

    {

        string baseTypeName = baseType.Name;

        if (!TypesHierarchyToAddInObjectContext.ContainsKey(baseTypeName))

            TypesHierarchyToAddInObjectContext.Add(baseTypeName, new List<EntityType>());

        TypesHierarchyToAddInObjectContext[baseTypeName].Add(typeToAdd);

    }

 

    protected override string GetDefaultExtension()

    {

        return (“.Designer” + base.GetDefaultExtension());

    }

 

    protected override byte[] GenerateCode(string inputFileContent)

    {

        byte[] generatedCodeAsBytes = null;

 

        try

        {

            XElement csdlContent = ExtractCsdlContent(inputFileContent);

            if (csdlContent == null)

            {

                throw new InvalidOperationException(“No CSDL content in input file”);

            }

 

            _objectContext = null;

            _entitySetNames = null;

            _typesHierarchyToAddInObjectContext = null;

 

            _first = true;

 

            // Determine language for generated code

            LanguageOption languageOption = LanguageOption.GenerateCSharpCode;

            string fileExtension = base.GetCodeProvider().FileExtension;

            if (fileExtension != null && fileExtension.Length > 0)

            {

                fileExtension = “.” + fileExtension.TrimStart(“.”.ToCharArray());

            }

            if (fileExtension.EndsWith(“.vb”, StringComparison.InvariantCultureIgnoreCase))

            {

                languageOption = LanguageOption.GenerateVBCode;

            }

            else if (fileExtension.EndsWith(“.cs”, StringComparison.InvariantCultureIgnoreCase))

            {

                languageOption = LanguageOption.GenerateCSharpCode;

            }

            else

            {

                throw new InvalidOperationException(“Unsupported project language. Only C# and VB are supported.”);

            }

 

            if (base.CodeGeneratorProgress != null)

            {

                base.CodeGeneratorProgress.Progress(33, 100);

            }

 

            EntityClassGenerator classGenerator;

            IList<EdmSchemaError> errors = null;

            StringWriter codeWriter = new StringWriter(CultureInfo.InvariantCulture);

            for (int i = 0; i < 2; i++)

            {

                using (XmlReader csdlReader = csdlContent.CreateReader())

                {

                    classGenerator = new EntityClassGenerator(languageOption);

                    classGenerator.OnTypeGenerated += new TypeGeneratedEventHandler(OnTypeGenerated);

                    classGenerator.OnPropertyGenerated += new PropertyGeneratedEventHandler(OnPropertyGenerated);

 

                    errors = classGenerator.GenerateCode(csdlReader, codeWriter);

                }

                if (! _first)

                    break;

                _first = false;

                if (base.CodeGeneratorProgress != null)

                {

                    base.CodeGeneratorProgress.Progress(66, 100);

                }

                codeWriter.Dispose();

                codeWriter = new StringWriter(CultureInfo.InvariantCulture);

            }

            if (errors != null)

            {

                foreach (EdmSchemaError error in errors)

                {

                    int line = (error.Line == 0) ? 0 : (error.Line – 1);

                    int column = (error.Column == 0) ? 0 : (error.Column – 1);

 

                    if (error.Severity == EdmSchemaErrorSeverity.Warning)

                    {

                        base.GeneratorWarning(0, error.Message, (uint)line, (uint)column);

                    }

                    else

                    {

                        base.GeneratorError(4, error.Message, (uint)line, (uint)column);

                    }

                }

            }

 

            generatedCodeAsBytes = Encoding.UTF8.GetBytes(codeWriter.ToString());

            codeWriter.Dispose();

            if (base.CodeGeneratorProgress != null)

            {

                base.CodeGeneratorProgress.Progress(100, 100);

            }

        }

        catch (Exception e)

        {

            base.GeneratorError(4, e.Message, 1, 1);

 

            generatedCodeAsBytes = null;

        }

 

        return generatedCodeAsBytes;

    }

 

    private void OnTypeGenerated(object sender, TypeGeneratedEventArgs eventArgs)

    {

        var entityType = eventArgs.TypeSource as System.Data.Metadata.Edm.EntityType;

        if (_first)

        {

            if (entityType != null)

            {

                EdmType baseType;

                if ((baseType = entityType.BaseType) != null)

                {

                    //System.Windows.Forms.MessageBox.Show(“”);

                    while (baseType.BaseType != null)

                        baseType = baseType.BaseType;

                    var baseEntityType = baseType as EntityType;

                    AddSubEntities(baseEntityType, entityType);

                }

            }

        }

        else

        {

            eventArgs.AdditionalAttributes.AddRange(CreateCodeAttributes(eventArgs.TypeSource));

            var objectContext = eventArgs.TypeSource as EntityContainer;

            if (objectContext != null)

            {

                _objectContext = objectContext;

                var baseEntitySets = _objectContext.MetadataProperties.FirstOrDefault(mp => mp.Name == “BaseEntitySets”);

                if (baseEntitySets != null)

                {

                    foreach (var entitySet in (ReadOnlyMetadataCollection<EntitySetBase>)baseEntitySets.Value)

                    {

                        var derivedBaseEntityType = entitySet.ElementType as EntityType;

                        if (derivedBaseEntityType != null)

                        {

                            EntitySetNames.Add(derivedBaseEntityType.Name, entitySet);

                            if (_typesHierarchyToAddInObjectContext != null)

                                foreach (var derivedEntityType in GetSubEntitiesOf(derivedBaseEntityType))

                                {

                                    string derivedEntityTypeName = derivedEntityType.Name;

                                    var newProp = new CodeMemberProperty { Name = derivedEntityTypeName + “s”, Attributes = MemberAttributes.Public | MemberAttributes.Final, Type = new CodeTypeReference(“global::System.Linq.IQueryable<“ + derivedEntityTypeName + “>”) };

                                    newProp.GetStatements.Add(new CodeMethodReturnStatement(new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), _entitySetNames[derivedBaseEntityType.Name].Name), “OfType”, new CodeTypeReference(derivedEntityTypeName)))));

                                    eventArgs.AdditionalMembers.Add(newProp);

                                }

                        }

                    }

                }

            }

        }

    }

 

    private void OnPropertyGenerated(object sender, PropertyGeneratedEventArgs eventArgs)

    {

        if (!_first)

            eventArgs.AdditionalAttributes.AddRange(CreateCodeAttributes(eventArgs.PropertySource));

    }

 

    private IList<CodeAttributeDeclaration> CreateCodeAttributes(MetadataItem item)

    {

        string xmlns = “http://tempuri.org/AttributeAnnotations”;

 

        List<CodeAttributeDeclaration> codeAttributeDeclarations = new List<CodeAttributeDeclaration>();

        if (item != null)

        {

            IEnumerable<MetadataProperty> metadataProperties = item.MetadataProperties.Where(prop => prop.Name.StartsWith(xmlns));

            foreach (MetadataProperty metadataProperty in metadataProperties)

            {

                string metadataPropertyValue = (string)metadataProperty.Value;

                if (!String.IsNullOrEmpty(metadataPropertyValue))

                {

                    string[] attributes = metadataPropertyValue.Split(new char[] { ‘;’ }, StringSplitOptions.RemoveEmptyEntries);

                    foreach (string attribute in attributes)

                    {

                        string attributeName = attribute;

                        string[] attributeParams = new string[1];

 

                        if (attribute.Contains(‘(‘))

                        {

                            attributeParams = attribute.Split(new char[] { ‘(‘, ‘)’ }, StringSplitOptions.RemoveEmptyEntries);

                            attributeName = attributeParams[0];

                        }

 

                        CodeAttributeDeclaration codeAttributeDeclaration = new CodeAttributeDeclaration(attributeName);

 

                        foreach (string attributeParam in attributeParams.Skip(1))

                        {

                            object attributeParamObj = null;

                            bool attributeParamBool = false;

                            if (bool.TryParse(attributeParam, out attributeParamBool))

                            {

                                attributeParamObj = attributeParamBool;

                            }

                            else

                            {

                                attributeParamObj = attributeParam;

                            }

                            codeAttributeDeclaration.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(attributeParamObj)));

                        }

                        codeAttributeDeclarations.Add(codeAttributeDeclaration);

                    }

                }

            }

        }

        return codeAttributeDeclarations;

    }

 

    private XElement ExtractCsdlContent(string inputFileContent)

    {

        XElement csdlContent = null;

        XNamespace edmxns = “http://schemas.microsoft.com/ado/2007/06/edmx”;

        XNamespace edmns = “http://schemas.microsoft.com/ado/2006/04/edm”;

 

        XDocument edmxDoc = XDocument.Load(new StringReader(inputFileContent));

        if (edmxDoc != null)

        {

            XElement edmxNode = edmxDoc.Element(edmxns + “Edmx”);

            if (edmxNode != null)

            {

                XElement runtimeNode = edmxNode.Element(edmxns + “Runtime”);

                if (runtimeNode != null)

                {

                    XElement conceptualModelsNode = runtimeNode.Element(edmxns + “ConceptualModels”);

                    if (conceptualModelsNode != null)

                    {

                        csdlContent = conceptualModelsNode.Element(edmns + “Schema”);

                    }

                }

            }

        }

        return csdlContent;

    }

}


The big difference between this and my previous custom generator is that, as the context is generated before the entity types, I must run the generation twice. The first to identify the sub entity types and the second to actually generate the code. 

This entry was posted in 7671, 7674, 7675, 8606. Bookmark the permalink.

3 Responses to How to have a property on ObjectContext which returns the derived entities?

  1. Matthieu MEZIL says:

    There is a better way to do this with only one code generation as I do here.

  2. Robert Gustafson says:

    1. WHERE does one paste this code?
    2. Is there a VB (2010) version of it?
    3. Is any text simply placeholder for one’s own objects, or is all to be entered LITERALLY as shown?
    4. What if One has already constructed a model?

  3. Robert Gustafson says:

    I have Visual Studio 2010 and .NET Framework 4.0. I converted the above class code from C# to Visual Basic.
    Now I need to know:

    1. Will this work on my version of VS/.NET/Entity Framework?
    2. Where do I place this code in VS/my project and how do I run it?
    3. Will it work for all derived classes of all base classes in ANY project?
    4. Is there anything else I should know?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>