如何使用可查询的linq有条件地从hashset中选择项目?

时间:2017-10-20 06:17:25

标签: c# linq

您好我是C#的新手,并从互联网上找到了这段代码:

public class InMemoryObjectSet<T>
    : IObjectSet<T> where T : class
{
    readonly HashSet<T> _set;
    readonly IQueryable<T> _queryableSet;

    public InMemoryObjectSet(IEnumerable<T> entities)
    {
        _set = new HashSet<T>();
        foreach (var entity in entities)
        {
            _set.Add(entity);
        }
        _queryableSet = _set.AsQueryable();
    }

    public Expression Expression
    {
        get { return _queryableSet.Expression; }
    }

    public IQueryProvider Provider
    {
        get { return _queryableSet.Provider; }
    }

    public IEnumerator<T> GetEnumerator()
    {
        return _set.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    /* Ignore Add, Delete operations */
}

我试图在InMemoryObjectSet中找到一个元素,条件名是&#39; xx&#39;。当然我可以使用IEnumerator循环但我只是想知道是否可以使用linq-ish方式:

var inMemoryItems = new InMemoryObjectSet<Customer>();
System.Linq.Expressions.Expression<Customer> exp = (x => x.Name == "xx");
var findItem = inMemoryItems.Provider.CreateQuery<Customer>(exp);

错误是:

Cannot convert lambda expression to type 'Customer' because it is not a delegate type

有人可以帮忙吗?

1 个答案:

答案 0 :(得分:1)

您想创建一个Expression,可用于过滤您的Customers集合。

您的错误是表达式的签名不正确。

我花了一段时间才看到你InMemoryObjectSet<T>的目的。 HashSet already implements IEnumerable`,因此可以处理你想要的所有LINQ语句。

但显然你想要一个实现IQueryable<T>的类,所以你可以处理你的InMemorySet对象AsQueryable

如果要从IQueryable中过滤出TSource类型的某些元素,则需要以下类型的表达式:

Expression<Func<TSource, bool>> expr = ...

在您创建表达式后,您可以将IObjectSet<TSource>过滤为仅包含与您的表达式匹配的元素的IQueryable<TSource>

IObjectSet<TSource>实现IQueryable<TSource>. An IQueryable`隐藏了您要查询的集合所在的位置,以及如何访问集合中的元素。

该集合可以位于数据库,文件,Internet或InMemoryObjectSet中。

由于隐藏了这些信息,您不必知道查询所在的项目集合以及访问方式(SQL?其他方法?)

每个IQueryable都包含ExpressionProviderExpression通常由您使用LINQ语句填充。这是&#39;提供商的任务。将Expression转换为基础集合理解的格式并将其发送到此基础集合。

对于数据库,Provider会将Expression转换为SQL,对于InMemorySet,转换将更简单,它将转换IEnumerable中的表达式以访问基础HashSet。

看到这一点,您不应该访问Provider。您应该只创建Expression并使用ToList()First()Any()Count()等执行方法来执行Expression并获取结果

回到你的问题。正确使用将是: (在婴儿步骤中,所以你可以看到所有潜在的类型)

InMemorySet<Customer> customerCollection = new InMemorySet<Customer>();
IQueryable<Customer> customers = customerCollection;

从这里,您不再了解您的客户是否在数据库中 一个文件,InMemorySet或其他什么。 由于此信息隐藏,以下代码适用于任何这些集合

使用正确的LINQ获取名称为XX的所有客户:

IQueryable<Cusomter> xxCustomers = customers.Where(customer => customer.Name == XX);

使用ToList()或类似内容执行查询

或者:创建一个Expression并在过滤函数中使用它:

string XX = ...
Expression<Func<Customer, bool>> expr = customer => customer.Name == XX;
IQueryable<Customer> result1 = customers.Where(expr);

ToList()将命令result1的Provider执行表达式。

真正低级别的方法是告诉提供商访问

// (2) Use the Provider
IQueryProvider provider = customers.Provider;
object query2 = provider.Execute(customers.Expression)

但是再一次:不要这样做,你将丢失隐藏的信息,导致你的代码不再适用于任何IQueryable