背景
我们有一个类库,它有一个带有刷新功能的网格(从WPF DataGrid继承)。网格具有IQueryable Query
属性,可以进行刷新。每个网格的查询都不在类库中定义,而是在引用的最终项目中定义:
var dg = new RefreshableDataGrid();
dg.Query = () => new ProjectDbContext().Persons;
每个网格还有一个用于文本过滤的文本框。在筛选器中输入文本时,将生成一个表达式,该表达式检查是否有任何字符串属性或字符串可转换属性(使用SqlFunctions.StringConvert
)包含筛选字符串。然后将表达式作为Where
的参数附加到原始查询,因此只返回包含匹配字符串的记录。
//within the class library
//pseudo-code -- this is actually done via reflection, because at compile time the
//actual type of the grid is not known, and there is no generic placeholder
this.ItemsSource = this.Query.Where(filterExpression)
在某些情况下,过滤器逻辑在实体类型的最终项目中定义。例如:
public interface IFilterable {
public Expression<Func<String, Boolean>> TextSearchExpression();
}
public class Email {
public int ID {get;set;}
public int PersonID {get;set;}
public string Address {get;set;}
}
public class Person : IFilterable
public int ID {get;set;}
public string LastName {get;set;}
public string FirstName {get;set;}
public Expression<Func<String, Boolean>> TextSearchExpression() {
Dim ctx = new ProjectDbContext();
return phrase => LastName.Contains(phrase) || FirstName.Contains(phrase) ||
ctx.Emails.Where(x => x.PersonID = ID && x.Address.Contains(prase).Any();
}
}
此表达式树使用项目特定上下文的实例,该实例与原始查询的实例不同。查询不能使用来自多个上下文的组件(至少不在实体框架中)。我可以重写表达式树以使用特定的实例,但我需要从查询中提取原始实例。
很明显,查询包含对上下文实例的一些引用,否则查询将无法返回结果。
我不想将上下文实例传递给类库。
因此:
根据查询,如何获取用于创建查询的强类型DbContext
实例?
换句话说,这种方法的主体是什么:
DbContext GetDbContext<TSource>(IQueryable<TSource> qry) {
// ???
}
答案 0 :(得分:2)
很明显,查询包含对上下文实例的一些引用,否则查询将无法返回结果。
这是真的,但它是特定于实现的细节,并且在EF中封装在内部成员/类/接口中。
同时考虑到DbContext
是建立在ObjectContext
之上的,并不一定要保留对DbContext
的引用。幸运的是,情况并非如此:)
以下内容使用了最新版EF6.1.3的反映和实现细节(如果您不使用LinqKit之类的某些第三方扩展以及替换查询提供程序的类似扩展,则已经过测试和运行):
public static DbContext GetDbContext<TSource>(this IQueryable<TSource> query)
{
const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
var provider = query.Provider;
var internalContextProperty = provider.GetType().GetProperty("InternalContext", flags);
if (internalContextProperty == null) return null;
var internalContext = internalContextProperty.GetValue(provider, null);
if (internalContext == null) return null;
var ownerProperty = internalContext.GetType().GetProperty("Owner", flags);
if (ownerProperty == null) return null;
var dbContext = (DbContext)ownerProperty.GetValue(internalContext, null);
return dbContext;
}
答案 1 :(得分:0)
我建议将MyDataContext的实例传递给查询函数
public class DACPerson
{
public static IQueryable<Person> GetAllAsQueryable(MyDataContext db)
{
return db.People.AsQueryable();
}
}
这允许您在调用函数中执行以下操作:
public List<Person> UpdateListofPeople(string term)
{
using(DataContext db = new DataContext())
{
var people = DACPerson.GetAllAsQueryable(db);
var result = people.Where(x=>x.Username.Contains(term)).
//call other Data Access Component FUnctions here using same DB....
}
}
即。您可以将过滤带到数据访问类上方的层。
有些人可能不喜欢这样做。您可以获得最好的建议,即将所有entityframeowrk功能保留在同一层中,然后返回DTO。我喜欢上面的方法。这取决于将来必须维护您应用程序的每个部分。
希望这至少有帮助