[英]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.