我有这个想法来创建一个执行不同操作的IQueryables的“列表”。
基本上是这样的:
var query1 = Enumerable.Empty<Person>().AsQueryable().Where(e => e.Name == "Ronald");
var query2 = Enumerable.Empty<Person>().AsQueryable().Where(e => e.Age == 43);
var query3 = Enumerable.Empty<Person>().AsQueryable().Select(e => e.EyeColor);
var listOfQueries = new List<IQueryable<Person>
{
query1,
query2,
query3
};
现在,我也有这个DbSet充满了“人”,我想“应用”我对DbSet的所有查询。我该怎么办?它甚至可能吗?
更新的例子:
var personQueryFactory = new PersonQueryFactory();
var personQueryByFirstname = personQueryFactory.CreateQueryByFirstname("Ronald"); //Query by Firstname.
var personQueryByAge = personQueryFactory.CreateQueryByAge(42); //Query by age.
var personQueryByHasChildWithAgeOver = personQueryFactory.CreateQueryByChildAgeOver(25); //Query using a "join" to the child-relationship.
var personQuerySkip = personQueryFactory.Take(5); //Only get the 5 first matching the queries.
var personQuery = personQueryFactory.AggregateQueries //Aggragate all the queries into one single query.
(
personQueryByFirstname,
personQueryByAge,
personQueryByHasChildWithAgeOver,
personQuerySkip
);
var personSurnames = personsService.Query(personQuery, e => new { Surname = e.Surname }); //Get only the surname of the first 5 persons with a firstname of "Ronald" with the age 42 and that has a child thats over 25 years old.
var personDomainObjects = personsService.Query<DomainPerson>(personQuery); //Get the first 5 persons as a domain-object (mapping behind the "scenes") with a firstname of "Ronald" with the age 42 and that has a child thats over 25 years old.
var personDaos = personsService.Query(personQuery); //Get the first 5 persons as a DAO-objects/entityframework-entities with a firstname of "Ronald" with the age 42 and that has a child thats over 25 years old.
这样做的原因是创建一种更“统一”的方式来创建和重用预定义查询,然后能够针对DbSet执行它们并将结果作为域对象而不是“实体框架模型/对象”
答案 0 :(得分:4)
这是可能的,但您需要稍微重新构建您的方法。
IQueryable
阅读您的意图,您实际上并不想处理Where
个对象,而是您提供给Select()
方法的参数。
编辑:我错过了第三个是Where()
,而不是Where()
。我调整了答案的其余部分,好像这也是Select()
。请参阅以下评论,了解我为何可以轻松地将Where()
与Func<Person,bool> filter1 = (e => e.Name == "Ronald");
Func<Person,bool> filter2 = (e => e.Age == 43);
Func<Person,bool> filter3 = (e => e.EyeColor == "Blue");
轻轻混合。
IQueryable
这会存储相同的信息(过滤条件),但它不会将每个过滤器包装在自己的Func<A,B>
中。
简短说明
注意
A
符号。在这种情况下,Person
是输入类型(B
),bool
是输出类型(Func<string,Person,bool>
)。这可以进一步扩展。
string
有两个输入参数(Person
,bool
)和一个输出参数(Func<string, Person, bool> filter = (inputString, inputPerson) => inputString == "TEST" && inputPerson.Age > 35;
)。用法示例:
Func<Person,bool>
始终有一个输出参数(最后一种类型)。每个其他提到的类型都是输入参数。
直观地说,List<Func<Person,bool>>
表示单个过滤器;您可以使用List<T>
来表示过滤器列表。
嵌套泛型类型有点难以阅读,但它确实像其他任何List<Func<Person,bool>> listOfFilters = new List<Func<Person,bool>>()
{
(e => e.Name == "Ronald"),
(e => e.Age == 43),
(e => e.EyeColor == "Blue")
};
一样工作。
var myFilteredData = myContext.Set<Person>()
.Where(filter1)
.Where(filter2)
.Where(filter3)
.ToList();
你很幸运。因为您想要应用所有过滤器(逻辑AND),所以可以通过堆叠它们来轻松完成:
List<Func<Person,bool>>
或者,如果您正在使用var myFilteredData = myContext.Set<Person>().AsQueryable();
foreach(var filter in listOfFilters)
{
myFilteredData = myFilteredData.Where(filter);
}
:
Func<Person, bool> filterCombined =
e => filter1(e) || filter2(e) || filter3(e);
var myFilteredData = myContext.Set<Person>()
.Where(filterCombined)
.ToList();
边缘案例
但是,如果您试图查找适合一个或多个过滤器的所有项目(逻辑OR),则会变得稍微困难。
The full answer is quite complicated.。你可以在这里查看。
但是,假设您已在已知变量中设置了过滤器there is a simpler method:
def get_lap(cmap):
return scipy.sparse.csc_matrix(csgraph.laplacian(cmap, normed=False))
def fromcmaptocoords(C):
L = get_lap(C)
eigw, eigv = LA.eigh(L.todense())
vecs = eigv[:,1:3]
guess_coords = pd.DataFrame(data=vecs, columns=["x", "y"])
return guess_coords
答案 1 :(得分:1)
其中一个问题是IQueryable的集合只有在您的DbSet有效时才有效。一旦您的DbContext被处置,您精心填充的集合就毫无价值。
因此,您必须考虑重建查询的另一种方法,而不是使用DbSet<Person>
的方法
虽然乍一看它们看起来是一样的,但IEnumerable
和IQueryable
之间存在差异。 Enumerable中包含所有内容以枚举结果序列。
另一方面,Queryable包含Expression和Provider。提供者知道可以获取数据的位置。这通常是一个数据库,但它也可以是CSV文件或其他可以获取序列的项目。提供程序的任务是解释Expression并将其转换为数据库可以理解的格式,通常是SQL。
在将linq语句连接成一个大的linq语句时,不访问数据库。只有表达式被更改。
一旦调用GetEnumerator()和MoveNext()(通常通过执行ForEach,ToList()或类似),Expression将被发送给提供者,提供者将其转换为数据库理解并执行的查询格式。查询。查询的结果是一个Enumerable序列,因此调用提供者的查询结果的Getenumerator()和MoveNext()。
因为您的IQueryable持有此提供者,所以在提供者被处置后您不能再枚举。
使用实体框架时,DbSet包含Provider。在Provider中是DbContext持有的数据库信息。一旦你处理了DbContext,就不能再使用IQueryable了:
IQueryable<Person> query = null;
using (var dbContext = new MyDbcontext())
{
query = dbContext.Persons.Where(person => person.Age > 20);
}
foreach (var person in query)
{
// expect exception: the DbContext is already Disposed
}
因此,您无法将提供者放入您的收藏或可能的查询中。但是,你可以记住表达式。您对Expression的唯一要求是它返回一个Person。你还需要一个带有这个Expression的函数和一个Personons的QueryaProvider来将它转换为IQueryable。
让我们为此创建一个通用函数,因此它可以用于任何类型,而不仅仅用于人:
static IQueryable<TSource> ToQueryable<TSource>(this IQueryProvider provider,
Expression expression)
{
return provider.CreateQuery(expression);
}
//好吧,我们还添加以下内容:
static IQueryable<Tsource> ToQueryable<TSource>(this DbContext dbContext,
Expression expression)
{
return dbContext.Set<TSource>.Provider.ToQueryable<TSource>(expression);
}
有关扩展功能的帮助,请参阅Extension Functions Demystified
现在只有在创建表达式集合时才会这样。对于快速查找,请将其设为字典:
enum PersonQuery
{
ByFirstname,
ByAge,
ByHasChildWithAgeOver,
Skip,
}
public IReadOnlyDictionary<PersonQuery, Expression> CreateExpressions()
{
Dictionary<PersonQuery, Expression> dict = new Dictionary<PersonQuery, Expression>();
using (var dbContext = new MyDbContext())
{
IQueryable<Person> queryByFirstName = dbContext.Persons
.Where(...);
dict.Add(PersonQuery.ByfirstName, queryByFirstName.Expression);
... // etc for the other queries
}
return dict.
}
用法:
IReadOnlyCollection<Person> PerformQuery(PersonQuery queryId)
{
using (var dbContext = new MyDbContext())
{
// get the Expression from the dictionary:
var expression = this.QueryDictionary[queryId];
// translate to IQueryable:
var query = dbContext.ToQueryable<Person>(expression);
// perform the query:
return query.ToList();
// because all items are fetched by now, the DbContext may be Disposed
}
}