Linq扩展方法

7

我经常需要通过像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()方法提供了上述代码片段中的3行代码。

我该如何做呢? 有没有更好的方法来清理代码?


这些都在不同的表中,对吧? 不是一个由上下文揭示的单一表格? 我之所以问,是因为你在前言中指示属性在不同的表中,而在筛选器中只使用了一个上下文。 如果它们都在同一个表格中,那很容易解决;但如果它们在不同的表格中,那么答案就会有很大的变化。 - casperOne
在这种情况下,您需要在调用 SingleOrDefault 之前调用 IsActive,这样它就可以在您只取一个项目之前过滤活动项目,除非仅有一个具有该ID的项目。 - Servy
3个回答

13
为了定义一个扩展,你需要一个静态类。你可以把它放在任何namespace中,只需要记得在你的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


由于各种评论随处可见,我在这里添加一个简要的接口解决方案说明...

问题中不清楚是否有三个过滤字段的公共实现或定义它们的接口。如果没有,则需要以下操作才能使上述内容生效:

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

1
@Default。我更喜欢使用IQueryable<T>,因为我以前在使用IEnumerable<T>时遇到了多态性问题。鉴于IEnumerable<T>IQueryable<T>都存在扩展,返回IEnumerable<T>可能会导致在链中进一步调用错误的扩展,从而导致枚举而不是延迟查询。 - Paul Fleming
2
@Default 这取决于您是想在数据库上执行查询,还是返回所有项目并在 C# 代码中进行筛选。如果可能的话,在数据库中进行过滤通常是更可取的,但并非总是可行的。 - Servy
没有必要将这个方法变成通用方法,然后将类型参数限制为一种类型,除非所有具有这三个属性的类型都使用继承进行建模——在大多数情况下,我真的不建议这样做。 - Daniel Brückner
扩展方法示例在哪里?你刚刚展示了冗长的Lambda语法。 - PositiveGuy
@CoffeeAddict 方法内的代码与扩展方法无关(实际上是 OP 的代码)。扩展方法是 Active 方法本身。请注意,在第一个参数前加上 this 关键字前缀。在静态类中的 static 方法可以通过将 this 添加到第一个参数来成为扩展方法。这意味着您可以调用该 静态方法,就像它是第一个参数的 实例方法 一样。我已经在我的答案中添加了一个链接以供进一步阅读。 - Paul Fleming
显示剩余5条评论

4

只需要像这样定义一个接口

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,因此需要完全评估以进行过滤。 - Paul Fleming
这是正确的,但是没有真正好的解决方案可以转换为SQL(没有自定义提供程序),因此决定提供一个LINQ to Objects的解决方案。 - Daniel Brückner

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);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接