Подтвердить что ты не робот

EF4 LINQ Include (строка) альтернатива жестко закодированной строке?

Есть ли альтернатива этому:

Organizations.Include("Assets").Where(o => o.Id == id).Single()

Я хотел бы увидеть что-то вроде:

Organizations.Include(o => o.Assets).Where(o => o.Id == id).Single()

чтобы избежать жестко закодированной строки "Активы".

4b9b3361

Ответ 1

Для Entity Framework 1.0 я создал несколько методов расширения для этого.

public static class EntityFrameworkIncludeExtension
{
    public static ObjectQuery<T> Include<T>(this ObjectQuery<T> src, Expression<Func<T, StructuralObject>> fetch)
    {
        return src.Include(CreateFetchingStrategyDescription(fetch));
    }

    public static ObjectQuery<T> Include<T>(this ObjectQuery<T> src, Expression<Func<T, RelatedEnd>> fetch)
    {
        return src.Include(CreateFetchingStrategyDescription(fetch));
    }

    public static ObjectQuery<T> Include<T, TFectchedCollection>(this ObjectQuery<T> src, Expression<Func<T, IEnumerable<TFectchedCollection>>> fetch)
    {
        return src.Include(CreateFetchingStrategyDescription(fetch));
    }

    public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, RelatedEnd>> fetch, Expression<Func<FetchedChild, Object>> secondFetch)
        where FetchedChild : StructuralObject
    {
        return src.Include(CombineFetchingStrategies(fetch, secondFetch));
    }

    public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, RelatedEnd>> fetch, Expression<Func<FetchedChild, RelatedEnd>> secondFetch)
        where FetchedChild : StructuralObject
    {
        return src.Include(CombineFetchingStrategies(fetch, secondFetch));
    }

    public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, RelatedEnd>> fetch, Expression<Func<FetchedChild, StructuralObject>> secondFetch)
        where FetchedChild : StructuralObject
    {
        return src.Include(CombineFetchingStrategies(fetch, secondFetch));
    }

    public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, IEnumerable<FetchedChild>>> fetch, Expression<Func<FetchedChild, Object>> secondFetch)
        where FetchedChild : StructuralObject
    {
        return src.Include(CombineFetchingStrategies(fetch, secondFetch));
    }

    public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, IEnumerable<FetchedChild>>> fetch, Expression<Func<FetchedChild, RelatedEnd>> secondFetch)
        where FetchedChild : StructuralObject
    {
        return src.Include(CombineFetchingStrategies(fetch, secondFetch));
    }

    public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, IEnumerable<FetchedChild>>> fetch, Expression<Func<FetchedChild, StructuralObject>> secondFetch)
        where FetchedChild : StructuralObject
    {
        return src.Include(CombineFetchingStrategies(fetch, secondFetch));
    }

    private static String CreateFetchingStrategyDescription<TFetchEntity, TFetchResult>(
        Expression<Func<TFetchEntity, TFetchResult>> fetch)
    {
        fetch = (Expression<Func<TFetchEntity, TFetchResult>>)FixedWrappedMemberAcces.ForExpression(fetch);
        if (fetch.Parameters.Count > 1)
            throw new ArgumentException("CreateFetchingStrategyDescription support only " +
                "one parameter in a dynamic expression!");

        int dot = fetch.Body.ToString().IndexOf(".") + 1;
        return fetch.Body.ToString().Remove(0, dot);
    }

    private static String CreateFetchingStrategyDescription<T>(Expression<Func<T, Object>> fetch)
    {
        return CreateFetchingStrategyDescription<T, Object>(fetch);
    }

    private static String CombineFetchingStrategies<T, TFetchedEntity>(
                Expression<Func<T, Object>> fetch, Expression<Func<TFetchedEntity, Object>> secondFetch)
    {
        return CombineFetchingStrategies<T, Object, TFetchedEntity, Object>(fetch, secondFetch);
    }

    private static String CombineFetchingStrategies<TFetchEntity, TFetchResult, TFetchedEntity, TSecondFetchResult>(
        Expression<Func<TFetchEntity, TFetchResult>> fetch, Expression<Func<TFetchedEntity, TSecondFetchResult>> secondFetch)
    {
        return CreateFetchingStrategyDescription<TFetchEntity, TFetchResult>(fetch) + "." +
            CreateFetchingStrategyDescription<TFetchedEntity, TSecondFetchResult>(secondFetch);
    }
}

Использование:

 Orders.Include(o => o.Product); // generates .Include("Product")
 Orders.Include(o => o.Product.Category); // generates .Include("Product.Category")
 Orders.Include(o => o.History); // a 1-* reference => .Include("History")
 // fetch all the orders, and in the orders collection.
 // also include the user reference so: .Include("History.User")
 // but because history is an collection you cant write o => o.History.User, 
 // there is an overload which accepts a second parameter to describe the fetching 
 // inside the collection.
 Orders.Include(o => o.History, h => h.User); 

Я не тестировал это на EF4.0, но я ожидаю, что он сработает.

Ответ 2

В EF 4.1 для этого используется встроенный метод расширения.

У вас должна быть ссылка на сборку EntityFramework (где EF 4.1 живет) в вашем проекте и использовать System.Data.Entity.

using System.Data.Entity; 

Если вы хотите включить вложенные объекты, выполните следующее:

        db.Customers.Include(c => c.Orders.Select(o => o.LineItems))

Не уверен, что это работает для объектов EF4.0 (на основе ObjectContext).

Ответ 3

Это довольно легко сделать, используя выражения:

public static class ObjectQueryExtensions
{
    public static ObjectQuery<T> Include<T, TProperty>(this ObjectQuery<T> objectQuery, Expression<Func<T, TProperty>> selector)
    {
        MemberExpression memberExpr = selector.Body as MemberExpression;
        if (memberExpr != null)
        {
            return objectQuery.Include(memberExpr.Member.Name);
        }
        throw new ArgumentException("The expression must be a MemberExpression", "selector");
    }
}

Вы можете использовать его точно так же, как в примере в своем вопросе


ОБНОВЛЕНИЕ

Улучшенная версия, которая поддерживает свойства с несколькими цепями:

public static class ObjectQueryExtensions
{
    public static ObjectQuery<T> Include<T, TProperty>(this ObjectQuery<T> objectQuery, Expression<Func<T, TProperty>> selector)
    {
        string propertyPath = GetPropertyPath(selector);
        return objectQuery.Include(propertyPath);
    }

    public static string GetPropertyPath<T, TProperty>(Expression<Func<T, TProperty>> selector)
    {
        StringBuilder sb = new StringBuilder();
        MemberExpression memberExpr = selector.Body as MemberExpression;
        while (memberExpr != null)
        {
            string name = memberExpr.Member.Name;
            if (sb.Length > 0)
                name = name + ".";
            sb.Insert(0, name);
            if (memberExpr.Expression is ParameterExpression)
                return sb.ToString();
            memberExpr = memberExpr.Expression as MemberExpression;
        }
        throw new ArgumentException("The expression must be a MemberExpression", "selector");
    }
}

Пример:

var query = X.Include(x => x.Foo.Bar.Baz) // equivalent to X.Include("Foo.Bar.Baz")

Ответ 5

Другим решением является извлечение имени объекта с помощью EntitySet.Name.
Код будет выглядеть следующим образом:

var context = new DBContext();  
context.Organizations.Include(context.Assets.EntitySet.Name).Where(o => o.Id == id).Single()

Ответ 6

Хорошие новости о том, что EF4 CTP4 поддерживает эту функцию.

Ответ 7

Другим вариантом является включение внутреннего частичного класса внутри вашего класса с использованием шаблона TT.

Код T4:

<#
    region.Begin("Member Constants");
#>
   public partial class <#=code.Escape(entity)#>Members
   {
<#
    foreach (EdmProperty edmProperty in entity.Properties.Where(p => p.TypeUsage.EdmType is PrimitiveType && p.DeclaringType == entity))
    {
        bool isForeignKey = entity.NavigationProperties.Any(np=>np.GetDependentProperties().Contains(edmProperty));
        bool isDefaultValueDefinedInModel = (edmProperty.DefaultValue != null);
        bool generateAutomaticProperty = false;
        #>
        public const string <#=code.Escape(edmProperty)#> = "<#=code.Escape(edmProperty)#>";
        <#
    }
    #>
    }
    <#
    region.End();
#>

Что создаст что-то вроде:

    #region Member Constants
   public partial class ContactMembers
   {
        public const string ID = "ID";
                public const string OriginalSourceID = "OriginalSourceID";
                public const string EnabledInd = "EnabledInd";
                public const string EffectiveDTM = "EffectiveDTM";
                public const string EndDTM = "EndDTM";
                public const string EnterDTM = "EnterDTM";
                public const string EnterUserID = "EnterUserID";
                public const string LastChgDTM = "LastChgDTM";
                public const string LastChgUserID = "LastChgUserID";
            }

    #endregion