Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Abonnements

Avoir une propriété sur l'object context qui renvoit les sous-entités

Dans l'entity framework, on a dans l'ObjectContext, une propriété par EntitySet. Le "problème" c'est que si on a une entité Person dérivée par une entité Employee, on n'a qu'un seul EntitySet : Persons.

Pour rajouter une propriété sur notre ObjectContext qui permettent de renvoyer uniquement les Employees, on peut rajouter à notre ObjectContext (partial) une propriété qui utilise la méthode OfType:

public IQueryable<Employee> Employees

{

    get

    {

        return Persons.OfType<Employee>();

    }

}

Jusque là facile.

Comme je l'ai dit sur ce thread du forum msdn hier, il est possible de le faire générer directement par l'outil de génération de code en le customisant.

En partant du code fournit par Elisa, voilà ce que j'ai fait:

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;

 

            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)

                {

                    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;

    }

}

La grosse différence par rapport à mon précédent générateur custom vient du fait que, le contexte étant généré avant les entity types, je suis obligé ici de faire deux passes de génération. Une pour identifier tous les entity types dérivés et une autre pour réellement générer le code.

Enjoy Big Smile

Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :

Publié jeudi 21 août 2008 13:15 par Matthieu MEZIL

Commentaires

# re: Avoir une propriété sur l'object context qui renvoit les sous-entités @ vendredi 22 août 2008 01:22

Vous trouverez ici une version plus performante.

Matthieu MEZIL

Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- [SharePoint] Les sessions TechDays 2012… par Le blog de Patrick [MVP SharePoint] le il y a 6 heures et 44 minutes

- TechDays Paris 2012 : Session pleinière jour 3 par Blog Technique de Romelard Fabrice le 02-09-2012, 11:01

- Mishra Reader : un lecteur RSS très Zune Style en Open Source ! par Cyril Sansus le 02-09-2012, 08:28

- [framework 4] Les Tasks et le Thread UI par Fathi Bellahcene le 02-09-2012, 00:33

- Workflow Foundation 3 a un pied dans la tombe par Blog de Jérémy Jeanson le 02-08-2012, 22:15

- TechDays Paris 2012 : Nouvelles tendances du poste de travail - Bring Your own PC par Blog Technique de Romelard Fabrice le 02-08-2012, 19:42

- TechDays Paris 2012 : System Center Service Manager 2012 Vue d’ensemble par Blog Technique de Romelard Fabrice le 02-08-2012, 17:32

- TechDays Paris 2012 : Pleinière second jour par Blog Technique de Romelard Fabrice le 02-08-2012, 16:23

- TechDays Paris 2012 : Retour d'expérience sur la mise en place d'un Cloud Privé par Blog Technique de Romelard Fabrice le 02-08-2012, 16:04

- TechDays Paris 2012 : Comment SharePoint a sauvé mes TechDays par Blog Technique de Romelard Fabrice le 02-07-2012, 23:59