【问题标题】:How to set generic properties with LINQ selector如何使用 LINQ 选择器设置通用属性
【发布时间】:2012-06-17 05:17:12
【问题描述】:

我有多个查询,如下所示:

var query = from a in EntityAs
            select new EntityASmall()
            {
                // Common Stuff:
                Id = a.Id,
                Name = a.Name,
                ShortName = a.ShortName,
                // Specific to EntityA:
                ASpecificProperty1 = a.1,
                ASpecificProperty2 = a.2
            };

var query = from b in EntityBs
            select new EntityBSmall()
            {
                // Common Stuff:
                Id = b.Id,
                Name = b.Name,
                ShortName = b.ShortName,
                // Specific to EntityB:
                BSpecificProperty1 = b.1,
                BSpecificProperty2 = b.2
            };

EntityA 和 EntityB 都派生自具有 IdNameShortName 属性的公共基类。 EntityASmallEntityBSmall 也是如此。我有很多看起来像这样的查询,所以我想做一些速记查询,先把常见的东西排除在外。我发现了一个有点前途的扩展方法,看起来像这样:

public static TSource SetCommonProperties<TSource>(this TSource input, EntityBaseClass entity, Action<TSource> updater) where TSource : EntitySmallBase
{
    input.Id = entity.Id;
    input.Name = entity.Name;
    input.ShortName = entity.Name;

    updater(input);

    return input;
}

我可以这样使用它:

var query = from a in EntityAs.AsEnumerable()
            select new EntityASmall().SetCommonProperties(a, x =>
            {
                ASpecificProperty1 = x.1;
                ASpecificProperty2 = x.2;
            });

注意AsEnumerable()。没有它,我会得到“无法将带有语句体的 lambda 表达式转换为表达式树”,我大致猜测这意味着它正在尝试将 Action 部分转换为 LINQ-to-SQL 的表达式。看起来AsEnumerable() 将集合带到了本地,它完全可以工作。很抱歉这篇冗长的帖子,但是有没有任何表达方式来编写这个可以与 LINQ-to-SQL 和实体框架一起使用的方法?提前致谢。

【问题讨论】:

  • EntityAsIQueryable&lt;&gt;?
  • 你不能写一个泛型方法ProjectOntoSmall 接受TEntity 类型的参数并将其转换为TEntitySmall 吗?如果你对这些必须从基类型派生的类型参数施加约束,我猜它会起作用。

标签: c# linq entity-framework linq-to-sql expression


【解决方案1】:

您想让您的代码 DRY,投入一些精力总是好的。也许您会通过一些辛勤劳动和一些Expression voodoo 使您的方法工作,但也许您会喜欢这个链接:Stop using AutoMapper in your Data Access Code。 (即使您不使用 AutoMapper)。

通过这项出色的工作,您将能够编写简洁的语句,例如:

context.EntityAs.Project().To<EntityASmall>();
context.EntityBs.Project().To<EntityBSmall>();

我自己用过这个,我真的很喜欢。

【讨论】:

  • 不错的链接。我喜欢它,但不幸的是,除了非常简单的映射之外,它没有任何帮助。我需要更强大的东西。
【解决方案2】:

你想出的扩展方法看起来不错。

只是您必须为每个派生实体创建它,这在您的情况下可能没问题,因为您似乎有几个派生实体。

其次,我认为您确实不需要在扩展方法中传递该动作委托。 如果可能的话,只需在其中调用该方法即可。我对设计了解不多。

所以你的扩展方法看起来像这样

public static TSource SetCommonProperties<TSource>(this TSource input, EntityBaseClass entity)     where TSource : EntitySmallBase 
{ 
   input.Id = entity.Id; 
   input.Name = entity.Name; 
   input.ShortName = entity.Name; 

   this.Update(input); // Or this method could exist in any other class or static class.

  return input; 

}

那么你也可以使用如下的扩展方法。

var query = from a in EntityAs
        select new EntityASmall
        {      
            ASpecificProperty1      
            ASpecificProperty2    
        }).SetCommonProperties(a,entity)    

这将消除您对 AsEnumerable 的使用。 如果您愿意,您还可以使用相同的方法从基础方法转换为衍生方法:

DerivceEntityObject SetCommonProperties(BaseEntity)

我希望这能让您了解我在这里要提出的建议。

【讨论】:

  • 嗯,适用于 LINQ-to-SQL,但不适用于 EF。它抱怨 SetCommonProperties 无法转换为 SQL。我猜它可以在 LINQ-to-SQL 中使用,因为 LINQ-to-SQL 和 LINQ-to-Objects 之间的界限更加模糊。
  • 是的,我同意我错过了 EF 的事情。它会抱怨因为它试图翻译 SQL 中的方法。在这种情况下,您可以做的是删除此键(不再使用扩展方法)并以我提到的相同方式将其用作普通方法。
  • 例如:对于 EntityAs 中的 a 选择 SetCommonProperties(a,entity),然后照常处理。
  • 也许我不明白,但如果我要这样做,我将不得不为每种类型的实体编写其中一种方法,这是我试图避免的。
  • public static TSource SetCommonProperties(TSource input, EntityBaseClass entity) where TSource : YouBaseClass
猜你喜欢
  • 2016-07-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多