Entity framework - avoir un include typé

Contenu du snippet

Entity Framework propose une méhtode Include permettant d'inclure des objets connexes lors du chargement de certaines entités. Cette méthode prend un String en paramètre, ce qui ne permet pas d'avoir une vérification lors de la compilation.

Le bout de code ci-dessous permet de générer différentes méthodes Include permettant d'avoir des includes typés. Il existe de nombreuses solutions permettant d'attendre cette objectif, cette solution permet d'inclure plusieurs profondeurs d'objets connexes, y compris lorsqu'un de ces objets est une collection.

Il est ainsi possible de faire :

var q = entities.Orders.Include(o => o.Customer.Addresses, a => a.Country).Where(o => o.CustomerId == 3);

La source est composé de 2 fichiers, un fichier ExpressionExtensions contenant quelques méthodes d'extension permettant de connaitre le nom d'une propriété à partir d'une lambda et d'un fichier T4 générant les différentes méthodes Include.

Source / Exemple :


// ExpressionExtensions.cs

    public static class ExpressionExtensions
    {
        public static String GetPropertyName<T, TProperty>(this Expression<Func<T, TProperty>> expression)
        {
            return GetPropertyName(expression);
        }
        public static String GetPropertyName(this Expression expression)
        {
            LambdaExpression lambdaExpression = expression as LambdaExpression;
            if (lambdaExpression != null)
            {
                expression = lambdaExpression.Body;
            }

            MemberExpression memberExpression = expression as MemberExpression;
            if (memberExpression != null)
            {
                String parentPropertyName = String.Empty;
                if (memberExpression.Expression is MemberExpression)
                {
                    parentPropertyName = ExpressionExtensions.GetPropertyName(memberExpression.Expression) + ".";
                }
                return parentPropertyName + memberExpression.Member.Name;
            }

            throw new NotImplementedException();
        }
    }

// ObjectQueryExtensions.Include.tt

<#@ template debug="true" hostSpecific="true" #>
<#@ output extension=".cs" #>
<#@ Assembly Name="System.Core.dll" #>
<#@ Assembly Name="System.Windows.Forms.dll" #>
<#@ import namespace="System" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Diagnostics" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Collections.Generic" #> 
<#
	Byte maxDepth = 6;
#>

using System;
using System.Collections.Generic;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
using System.Linq;
using System.Linq.Expressions;

namespace Magelia.WebStore.Data.Entities
{
	public static partial class ObjectQueryExtensions
  	{
	    internal static String GetPath(params LambdaExpression[] lambdas)
        {
            return String.Join(".",
                lambdas
                .Select(l => l.GetPropertyName())
                .ToArray()
            );
        }

<#
	for(Byte i = 1; i < maxDepth; i++)
	{
        int combinationCount = (int)Math.Pow(2, i);
        for (int j = 0; j < combinationCount; j++)
        {
            WriteMethod(i, j); 
        }
	}
#>
  	}
}
 
<#+
  	void WriteMethod(Byte depth, int combinationValue)
	{
        String[] genericsType = new String[depth+1];
		genericsType[0] = "TEntity"; 
		int i = depth-1;
        while (i >= 0)
        {
			String genericType = "T" + i.ToString();
            if ((combinationValue & (1 << i)) != 0)
            {
                genericType = "IEnumerable<" + genericType + ">";
            }
			genericsType[i+1] = genericType; 
            i--;
        }
		String[] parametersType = new String[depth]; 
		for(i = 0; i < depth; i++)
		{
			parametersType[i] = String.Format("Expression<Func<T{0}, {1}>> selector{2}", (i == 0) ? "Entity" : (i-1).ToString(), genericsType[i+1], i); 
		}
#>
		public static ObjectQuery<TEntity> Include<TEntity, <#= String.Join(", ", Enumerable.Range(0, depth).Select(j => "T" + j.ToString()).ToArray()) #>>(this ObjectQuery<TEntity> objectSet, <#= String.Join(", ", parametersType) #>)
			where TEntity : class
		{
			return objectSet.Include(GetPath(<#= String.Join(", ", Enumerable.Range(0, depth).Select(j => "selector" + j.ToString()).ToArray()) #>));
		}
<#+
	}
#>

Conclusion :


Plus d'information sur mon blog : http://blogs.developpeur.org/cyril/archive/2012/01/17/include-typ-et-entity-framework.aspx

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.