简体   繁体   English

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

[英]How to conditionally select item from hashset with queryable linq?

Hi I am new to C# and found this code from Internet:嗨,我是 C# 的新手,从 Internet 上找到了此代码:

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 */
}

I am trying to find an element in InMemoryObjectSet with condition name is 'xx'.我试图在 InMemoryObjectSet 中查找条件名称为“xx”的元素。 Of course I can use IEnumerator to loop through but I just wonder if it is possible to use linq-ish way:当然我可以使用 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);

Error was:错误是:

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

Can anybody please help?有人可以帮忙吗?

You want to create an Expression that can be used to filter your collection of Customers .您想创建一个可用于过滤Customers集合的Expression

Your error is that the signature of your expression is not correct.您的错误是您的表达式签名不正确。

It took me a while to see the purpose of your InMemoryObjectSet<T> .我花了一段时间才看到您的InMemoryObjectSet<T>的用途。 HashSet already implements IEnumerable`, and thus could already handle all LINQ statements you want. HashSet already implements IEnumerable`,因此已经可以处理您想要的所有 LINQ 语句。

But apparently you want a class that implements IQueryable<T> , so you could handle your InMemorySet object AsQueryable但显然你想要一个实现IQueryable<T> ,这样你就可以处理你的 InMemorySet 对象AsQueryable

If you want to filter out certain elements of type TSource from an IQueryable you need an expression of the following type:如果要从 IQueryable 中过滤掉 TSource 类型的某些元素,则需要以下类型的表达式:

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

Once you've created your expression, you can filter your IObjectSet<TSource> to an IQueryable<TSource> that contains only the elements that match your expression.创建表达式后,您可以将IObjectSet<TSource>过滤为仅包含与您的表达式匹配的元素的IQueryable<TSource>

An IObjectSet<TSource> implements IQueryable<TSource>. An IObjectSet<TSource>实现IQueryable<TSource>. An IQueryable<TSource>. An IQueryable` hides where the collection you are about to query is located, and how the elements in the collection are accessed. IQueryable<TSource>. An IQueryable`皮,你是要查询所在的收集,以及如何集合中的元素进行访问。

The collection could be in a database, a file, the internet, or your InMemoryObjectSet.该集合可以位于数据库、文件、互联网或您的 InMemoryObjectSet 中。

Because of this information hiding, you don't really have to know where your collection of items your query is located, and how it is accessed (SQL? other methods?)由于这种信息隐藏,您实际上不必知道您查询的项目集合位于何处,以及如何访问它(SQL?其他方法?)

Every IQueryable contains an Expression and a Provider .每个IQueryable包含一个Expression和一个Provider The Expression is usually filled by you using LINQ statements. Expression通常由您使用 LINQ 语句填充。 It is the task of the 'Provider' to translate the Expression into a format the underlying collection understands and send it to this underlying collection. 'Provider' 的任务是将Expression转换为底层集合理解的格式并将其发送到该底层集合。

For a database the Provider will translate the Expression into SQL, for an InMemorySet the translation will be simpler, it will translate the expression in an IEnumerable to access the underlying HashSet.对于数据库, Provider会将Expression转换为 SQL,对于InMemorySet ,转换会更简单,它将转换 IEnumerable 中的表达式以访问底层 HashSet。

Seeing this, you should not access the Provider .看到这一点,您不应该访问Provider You should only create the Expression and use executioner methods like ToList() , First() , Any() , Count() , to execute the Expression and get the result.您应该只创建Expression并使用诸如ToList()First()Any()Count()类的执行程序方法来执行Expression并获得结果。

Back to your question.回到你的问题。 Proper usage would be: (in baby steps, so you can see all underlying types)正确的用法是:(在婴儿阶段,所以你可以看到所有底层类型)

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

From here you don't know anymore whether your customers are in a database a file, an InMemorySet , or whatever.从这里开始,您不再知道您的客户是在数据库中的一个文件、一个InMemorySet还是其他任何东西。 Because of this information hiding the following code works on any of these collections由于此信息隐藏以下代码适用于任何这些集合

Get all customers with name XX, using proper LINQ:使用适当的 LINQ 获取名称为 XX 的所有客户:

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

Use ToList() or similar to execute the query使用ToList()或类似方法执行查询

Alternatively: create an Expression and use this in your filter function:或者:创建一个Expression并在您的过滤器函数中使用它:

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

ToList() will order the Provider of result1 to execute the expression. ToList()将命令 result1 的 Provider 执行表达式。

A really low level method would be to tell the Provider to access一个非常低级的方法是告诉提供者访问

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

But once again: don't do this, you'll loose the information hiding, causing that it isn't guaranteed anymore that your code works on any IQueryable但是再一次:不要这样做,你会丢失信息隐藏,导致不再保证你的代码可以在任何 IQueryable 上工作

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM