【问题标题】:Linq extension methodLinq 扩展方法
【发布时间】:2013-01-08 17:55:07
【问题描述】:

我经常需要通过 publishStartpublishEndactive 等字段来限制 SELECT

我在几个不同的表中有这些字段。所以应该只选择行,即

a: active == true;
b: publishStart < now;
c: publishEnd > now;

所以,例如:

db.myTable.SingleOrDefault(a => (a.ID == _theID 
          //now the active and start-end part:            
                      && ((a.publishEnd > DateTime.Now) || (a.publishEnd == null))
                      && ((a.publishStart <= DateTime.Now) || (a.publishStart == null))
                      && a.active == true));

这有点冗长,所以我想知道是否可以创建一个(扩展?)方法,例如:

db.myTable.SingleOrDefault(a => (a.ID == _theID).isActive()

isActive() 提供了上述 sn-p 的 3 行。

我怎么能这样做? 有没有更好的清理代码的方法?

【问题讨论】:

  • 这些都在单独的表中,对吧?没有一个由上下文公开的表?我问是因为您在序言中指出属性位于单独的表中,而您在过滤器中仅使用一个上下文。如果他们都在同一张桌子上,这很容易,如果他们在不同的桌子上,它会极大地改变正确的答案。
  • 在这种情况下,您需要致电IsActive 之前 SingleOrDefault 以便它可以在您只取一个之前过滤活动项目,除非只有一个具有该 ID 的一项。

标签: c# linq linq-to-sql extension-methods


【解决方案1】:

要定义扩展,您需要一个静态类。你可以把它放在你喜欢的任何命名空间中,只要记住将它包含在你的 usings 中。

public static class Extensions
{
    public static IQueryable<T> Active<T>(this IQueryable<T> source)
        where T : YourEntityType
    {
        return source.Where(a => ((a.publishEnd > DateTime.Now) || (a.publishEnd == null))
                          && ((a.publishStart <= DateTime.Now) || (a.publishStart == null))
                          && a.active == true);
    }
}

通知YourEntityType 在那里。这用于确保该方法知道publishStartpublishEndactive 的存在。这应该是实现这些字段的类或定义它们的协定(接口)。

然后你可以这样称呼它:

var item = db.myTable.Active().SingleOrDefault(...);

更多关于扩展方法的信息:http://msdn.microsoft.com/en-us/library/bb383977.aspx


由于到处冒出来很多cmets,我这里再简单说明一下接口解决方案……

问题中不清楚是否存在三个过滤字段的通用实现或定义它们的接口。如果没有,为了上述工作,你也不会:

  1. 实现这些字段的基类。在这种情况下,您可以将 YourEntityType 替换为 YourBaseEntityType
  2. 定义字段的接口。在这种情况下,您需要让您的类实现这些字段。如果这些类是自动生成的(例如实体框架模型/db 首先),那么您可以实现部分类,让它们实现接口。在这种情况下,您可以将 YourEntityType 替换为 IYourContract

【讨论】:

  • 是否有理由使用IQueryable&lt;T&gt; 而不是IEnumerable&lt;T&gt;
  • 这在调用SingleOrDefault 时不起作用,这是 OP 要求的。
  • @默认值。我更喜欢使用IQueryable&lt;T&gt;,因为我在过去使用IEnumerable&lt;T&gt; 时遇到过多态性问题。鉴于 IEnumerable&lt;T&gt;IQueryable&lt;T&gt; 都存在扩展,返回 IEnumerable&lt;T&gt; 可能会导致在链中进一步调用错误的扩展,从而导致枚举而不是延迟查询。
  • @Default 这是一个问题,您是要对数据库执行查询,还是返回所有项目并在 C# 代码中进行过滤。在可能的情况下,在数据库中进行过滤几乎总是可取的,但并不总是可行的。
  • 没有必要使它成为一个泛型方法,然后将类型参数约束为一个类型,除非所有具有这三个属性的类型都使用继承建模——在大多数情况下我真的不建议这样做.
【解决方案2】:

只要定义一个这样的接口

public interface IHaveAActivityPeriod 
{
    Boolean active { get; }

    DateTime? publishStart { get; }

    DateTime? publishEnd { get; }
} 

并将其添加到所有相关类中。

public class Foo : IHaveAActivityPeriod { [...] }

public class Bar : IHaveAActivityPeriod { [...] }

现在你可以使用这个扩展方法了

public static class Extensions
{
    public static Boolean IsActive(this IHaveAActivityPeriod item)
    {
        var now = DateTime.Now;

        return item.active &&
               (item.publishStart <= now)
               (!item.publishEnd.HasValue || (item.publishEnd > now));
    }
}

在每个实现 IHaveAActivityPeriod 的实例上。

var foo = new Foo();

var isFooActive = foo.IsActive();

var bar = new Bar();

var isBarActive = bar.IsActive();

我完全错过了构建一个执行序列过滤而不是一次查看单个实体的扩展方法的可能性。只需将 flem 答案中的扩展方法作为类型约束放入接口中即可。

public static class Extensions
{
    public IQueryable<T> IsActive<T>(this IQueryable<T> sequence)
        where T : IHaveAActivityPeriod
    {
        return source.Where(item =>
                   item.active &&
                   (item.publishStart <= now) &&
                   (!item.publishEnd.HasValue || (item.publishEnd > now));

    }
}

【讨论】:

  • 这不会转换为 sql,因此需要对过滤进行全面评估。
  • 确实如此,但没有真正好的可转换为 SQL 的解决方案(没有自定义提供程序),因此决定提出一个 LINQ to Objects 解决方案。
【解决方案3】:
public static class Extensions
{
    public static IEnumerable<MyClass> isActive(this IEnumerable<MyClass> list)
    {
        return list.Where(a =>  
               ((a.publishEnd > DateTime.Now) || (a.publishEnd == null))
                 && ((a.publishStart <= DateTime.Now) || (a.publishStart == null))
                 && a.active == true);
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多