繁体   English   中英

如何使用匿名投影重构此方法以使其更通用?

How can I refactor this method using anonymous projection to be more generic?

提示:本站收集StackOverFlow近2千万问答,支持中英文搜索,鼠标放在语句上弹窗显示对应的参考中文或英文, 本站还提供   中文繁体   英文版本   中英对照 版本,有任何建议请联系yoyou2525@163.com。

我所拥有的是以下方法。 我使用匿名投影来过滤EF包含的内容。 我从此博客文章中了解了这种方法: http : //thedatafarm.com/data-access/use-projections-and-a-repository-to-fake-a-filtered-eager-load/

public IEnumerable<Entities.Nutrient> FindAllForSpecificLanguage(bool overridePossibleLogicalDelete)
{
    using (var context = CreateObjectContext())
    {
        context.ContextOptions.LazyLoadingEnabled = false;
        Entities.Nutrient[] result;

        var list = context.Nutrients
            .Select(nut => new
            {
                Entity = nut,
                Descriptions = nut.Descriptions.Where(desc => desc.LanguageCode.Equals(DataLanguageContext.Current.DataLanguageCode))
            }).ToList(); //perform query
        var resultList = list
            .Select(entity => entity.Entity);

        return resultList;
    }
}

此方法应内置到所有服务中(该API支持大约30种语言,此刻我们有很多数据库开销...)。 我试图以一种通用的方式来构建它,但是我对表达式树缺乏经验。 我以为我完全重新创建了该函数,但是由于它无法正常工作,我错过了一些东西。 这是我到目前为止的内容:

public virtual IEnumerable<TEntity> FindAllForSpecificLanguage(bool overridePossibleLogicalDelete, Expression<Func<TEntity, IEnumerable<object>>> selectEntityDescriptions)
{
    using (var context = CreateObjectContext())
    {
        context.ContextOptions.LazyLoadingEnabled = false;
        ObjectQuery<TEntity> queryObjectSet = GetObjectSet(context);
        TEntity[] result;

        Type anonType = new {Entity = default(TEntity), Descriptions = Enumerable.Empty<object>()}.GetType();

        // (entityManagerBaseEntity) => new { Entity = entityManagerBaseEntity, Descriptions = selectEntityDescriptions(entityManagerBaseEntity) }
        // 1) "(entityManagerBaseEntity) =>"
        var pe = Expression.Parameter(typeof(TEntity), "entityManagerBaseEntity");
        // 2) "selectEntityDescriptions(entityManagerBaseEntity)"
        var exprFunc = Expression.Invoke(selectEntityDescriptions, pe);
        // get constructor for anonymous type
        var constructorInfo = anonType.GetConstructor(new[] { typeof(TEntity), typeof(IEnumerable<object>) });
        // 3) "new AnonType(entityManagerBaseEntity, exprFunc(entityManagerBaseEntity))"
        var constructAnonType = Expression.New(constructorInfo, pe, exprFunc);
        // 4) combine all to a lambda
        // {entity => new <>f__AnonymousType0`2(entity, Invoke(entity => entity.Descriptions.Where(desc => desc.LanguageCode.Equals(DataLanguageContext.Current.DataLanguageCode)), entity))}
        var cooleExpression = Expression.Lambda<Func<TEntity, dynamic>>(constructAnonType, pe);
        //var bla = cooleExpression.Compile();
        //var list = queryObjectSet.AsQueryable().Provider.CreateQuery<dynamic>(cooleExpression).ToList();
        var list = queryObjectSet.Select(cooleExpression).ToList(); //perform query
        var resultList = list
            .Select(entity => entity.Entity as TEntity);

        return resultList;
    }
}

(注意:CreateObjectContext和GetObjectSet是完美的工作方法)

应该这样称呼:

_nutrientManager.FindAllForSpecificLanguage(true, (entity) => entity.Descriptions.Where(desc => desc.LanguageCode.Equals(DataLanguageContext.Current.DataLanguageCode)))

在注释中键入要构建的表达式。 我猜看起来不错,但是连接从未执行。 如果我调试,则会得到以下堆栈跟踪:

System.NotSupportedException:在LINQ to Entities中仅支持无参数构造函数和初始化程序。 在System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter父类,Expression)处LINQ)

2 个回复

所以..几件事:

  1. 您不能在e L2E表达式中调用函数
  2. 您只能使用没有参数的构造函数
  3. 您可以自己在表达式中构建整个where子句

整个解决方案可以在这里找到: https : //github.com/YoeriVD/entity-framework-filter-on-include/blob/master/ExpressionTrees/Program.cs

重要的是:

    private static void Main(string[] args)
    {
        FilterOnInclude<Car, Wheel>(car => car.Wheels, wheel => wheel.SizeInInches == 14)
            .ForEach(car => Console.WriteLine($"car : {car.Name} wheels: {car.Wheels.Count}"));
    }

    private static IEnumerable<TEntity> FilterOnInclude<TEntity, TChildEntity>(
        Expression<Func<TEntity, IEnumerable<TChildEntity>>> propertyExpression,
        Expression<Func<TChildEntity, bool>> predicateExpression)
        where TEntity : class
    {
        using (var context = new CarContext())
        {
            context.Configuration.LazyLoadingEnabled = false;

            var selector = CreateSelector(propertyExpression, predicateExpression);
            return
                context.Set<TEntity>().Select(
                    selector
                    ).ToList().Select(e => e.Entity).ToArray();
        }
    }

    private static Expression<Func<TEntity, EntityWithFilteredChildren<TEntity, TChildEntity>>> CreateSelector
        <TEntity, TChildEntity>(
        Expression<Func<TEntity, IEnumerable<TChildEntity>>> propertyExpression,
        Expression<Func<TChildEntity, bool>> predicateExpression)
    {
        var selectType = typeof (EntityWithFilteredChildren<TEntity, TChildEntity>);

        //bind entity
        var entityValueParam = Expression.Parameter(typeof (TEntity), "entityValue");
        var entityProp = selectType.GetProperty("Entity");
        var entityValueAssignment = Expression.Bind(
            entityProp, entityValueParam);
        //bind collection
        var childrenProp = selectType.GetProperty("Children");
        var descriptionsMemberExpression = (propertyExpression.Body as MemberExpression);
        var descriptionsPropertyInfo = (PropertyInfo) descriptionsMemberExpression.Member;
        var descriptionsProperty = Expression.Property(entityValueParam, descriptionsPropertyInfo);
        //perform where call
        var whereCall = Expression.Call(typeof (Enumerable), "Where", new[] {typeof (TChildEntity)}, descriptionsProperty,
            predicateExpression);

        var descriptionValueAssignment = Expression.Bind(
            childrenProp, whereCall);

        var ctor = Expression.New(selectType);
        var memberInit = Expression.MemberInit(ctor, entityValueAssignment, descriptionValueAssignment);
        var selector = Expression.Lambda<Func<TEntity, EntityWithFilteredChildren<TEntity, TChildEntity>>>(memberInit,
            entityValueParam);

        return selector;
    }

    public class EntityWithFilteredChildren<T, TChild>
    {
        public T Entity { get; set; }
        public IEnumerable<TChild> Children { get; set; }
    }

显然,我不知道您的项目的更大结构,但是您可以通过用TEntity替换Entities.Nutrient使第一个示例更通用吗? 这是我正在考虑的事情:

public IEnumerable<TEntity> FindAllForSpecificLanguage<TEntity>(
    Func<DbContext, IEnumerable<TEntity>> selectEntities,
    Func<TEntity, IEnumerable<object>> selectEntityDescriptions)
{
    using (var context = CreateObjectContext())
    {
        context.ContextOptions.LazyLoadingEnabled = false;

        var list = selectEntities(context)
            .Select(nut => new
            {
                Entity = nut,
                Descriptions = selectEntityDescriptions
            }).ToList(); //perform query
        var resultList = list
            .Select(entity => entity.Entity);

        return resultList;
    }
}
1 重构代码以使用更通用的方法

当我在理解异步方面有一些困难时,我想尝试更好地重构此方法,我有一个Web api2项目,该项目使用我的数据访问层共享给电话应用程序。 我不确定我的语法是否正确,是否在xamrian表单应用程序中使用xamrian共享库。 我将使用各种方法来链接获取客户端,这些客户端将具有端点API /客 ...

2 重构方法以使其更快?

我有一个存储过程,该存储过程能够从数据库(通常为2500+)中检索行,该数据库的执行和完成速度相当快。 如下收集信息并使用DBReader读取: 这也运行得相当快,瓶颈位于GraphingService.GetAssociatedAlertTypes() ; 这将接受一个列表( al ...

3 如何重构此代码以使其更通用? [关闭]

我有以下模板存储在文本文件中: 您好,感谢您购买{year} {make} {model}。 year, make, and model come存储在template configuration table ,实际值存储在Car table 。 目前,我获取了模板中使用 ...

6 如何重构该Ruby方法以使其运行更快?

以下方法应采用数组a并返回其第二索引值为最低的重复整数。 该数组将仅包含1到a.length之间的整数。 在这个例子中 该方法返回2 。 该代码可以工作,但是看起来比必要时间更长,并且运行速度不够快。 我正在寻找重构的方法。 ...

8 如何重构方法以使其更易于测试

下面是我很难弄清楚如何使用JUnit进行测试的方法。 此方法很难测试,因为它取决于其他方法的结果(例如getClosestDcoumentCode)。 根据对JUnit的阅读,这表明我应该重构该方法。 但是如何? 如果不需要重构,您如何测试依赖于其他方法的方法? 谢谢, ...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2022 STACKOOM.COM