【问题标题】:Using an extension method on a base class in a LINQ query在 LINQ 查询中对基类使用扩展方法
【发布时间】:2013-10-10 12:23:00
【问题描述】:

为我的幼稚提前道歉。

我正在使用实体框架来持久化我在域模型中定义的实体。我的域模型实体都继承自我的 EntityBase 类。这具有我希望所有实体共有的属性:

public class EntityBase
{
    public string CreatedBy { get; set; }
    public DateTime? Created { get; set; }

    public int ModifiedBy { get; set; }
    public DateTime? Modified { get; set; }

    public bool Enabled { get; set; }
    public bool Deleted { get; set; }
}

现在,当我想使用 LINQ 查询 EF 时,如果我不必包含元素来检查特定实体是否已启用或已删除,那就太好了。每个查询都会涉及代码,例如:

var messages = _db.Memberships.Where(m => m.UserId.Equals(userId))
                              .SelectMany(m => m.Group.Messages)
                              .Include(m => m.Group.Category)
                              .Select(m => m.Enabled && !m.Deleted) 
                              .ToList();

与其每次都这样做,我想我会编写一个可作用于 IQueryable 的扩展方法

public static IQueryable<EntityBase> Active(this IQueryable<EntityBase> entityCollection)
    {
        return entityCollection.Where(e => e.Enabled && !e.Deleted);
    }

然后我天真地认为我可以将它包含在任何 LINQ 查询中,该查询返回我从 EntityBase 类继承的实体 - 就像这样:

var messages = _db.Memberships.Where(m => m.UserId.Equals(userId))
            .SelectMany(m => m.Group.Messages)
            .Include(m => m.Group.Category)
            .Active() <============================= Extension Methd
            .ToList();

        return Mapper.Map<List<Message>,List<MessageDto>>(messages);

但是,编译器现在抱怨:

Error 2 Argument 1: cannot convert from
          'System.Collections.Generic.List<Diffusr.Business.Entities.EntityBase>' to
          'System.Collections.Generic.List<Diffusr.Business.Entities.Message>'  

问题:我能否实现我想要实现的目标,即我的所有实体的通用方法只返回启用而不是删除?如果有,怎么做?

【问题讨论】:

  • 您的错误不在查询中,而是在返回点。将鼠标悬停在messagesvar 上,查看类型是否符合您的预期。
  • 使用 .NET 3.5 我敢打赌?

标签: c# linq entity-framework extension-methods covariance


【解决方案1】:

不要指定具体的类,而是使用泛型,就像大多数扩展方法所做的那样:

public static IQueryable<T> Active<T>(this IQueryable<T> entityCollection) where T:EntityBase
{
    return entityCollection.Where(e => e.Enabled && !e.Deleted);
}

我假设您使用的 .NET 版本早于 4.0。 Generic covariance 在 4.0 之前是不允许的(即,当需要基类型的可枚举时,传递子类型的可枚举)。

即使在 4.0 之后,使用协方差也不是绝对最好的主意,因为当您尝试将一些新值存储到 List 时,编译器最终会进行大量额外检查以确保类型安全。 Jon Skeet 有一个nice article about this

【讨论】:

  • 非常感谢您说得有道理。但是,当我尝试使用通用版本 IQueryable 时,Visual Studio 一直抱怨它无法解析 。我到底想念什么——感觉好蠢!顺便说一句,该项目是一个面向 .NET 4.5 的类库?非常感谢任何帮助。
  • 糟糕,声明中缺少
  • Sorry Panagiotis - 代码与上面的代码完全相同,但已更改为 并且按照您的建议添加了 T:EntityBase 位。编辑器中的错误是“无法解析符号 T”。我是否需要添加 using 指令才能使用泛型类型?
  • 您好,错过了您的评论。太棒了——现在都在工作。非常感谢。
【解决方案2】:

您可以通过更改扩展方法:

public static IQueryable<T> Active(this IQueryable<T> entityCollection)
    where T : EntityBase
{
    return entityCollection.Where(e => e.Enabled && !e.Deleted);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-12
    • 1970-01-01
    • 2016-02-05
    • 2018-12-24
    • 2011-10-02
    • 1970-01-01
    相关资源
    最近更新 更多