受到这篇文章的启发dynamic-repositories-in-lightspeed我正在努力建立自己的这个。
我有一个像这样的摘要GenericRepository
。为简单起见,我省略了大部分代码(它只是普通的添加/更新/过滤方法)。
public abstract class GenericRepository<TEntity, TContext> :
DynamicObject,
IDataRepository<TEntity>
where TEntity : class, new()
where TContext : DbContext, new()
{
protected TContext context;
protected DbSet<TEntity> DbSet;
}
如您所见,我的摘要GenericRepository
从DynamicObject
扩展到支持动态存储库。
我还有一个抽象的UnitOfWork实现,它在运行时为这个给定的实体生成一个存储库。同样,基类和其他细节与问题无关,但如果您需要,我很乐意提供它们。
public abstract class UnitOfWorkBase<TContext> : IUnitOfWork
where TContext : DbContext, new()
{
public abstract IDataRepository<T> Repository<T>()
where T : class, IIdentifiableEntity, new();
// Code
}
以下类实现上述类的抽象方法。
public class MyUnitOfWorkBase : UnitOfWorkBase<MyDataContext>
{
public override IDataRepository<T> Repository<T>()
{
if (Repositories == null)
Repositories = new Hashtable();
var type = typeof(T).Name;
if (!Repositories.ContainsKey(type))
{
var repositoryType = typeof(GenericRepositoryImpl<,>);
var genericType = repositoryType.MakeGenericType(typeof(T), typeof(InTeleBillContext));
var repositoryInstance = Activator.CreateInstance(genericType);
Repositories.Add(type, repositoryInstance);
}
return (IDataRepository<T>)Repositories[type];
}
}
现在,每当我想为基本的CRUD函数创建动态存储库时,我就可以这样做。
var uow = new MyUnitOfWorkBase();
var settingsRepo = uow.Repository<Settings>();
var settingsList = settingsRepo.Get().ToList();
现在,我想要做的就是这样。
dynamic settingsRepo = uow.Repository<Settings>();
var result = settingsRepo.FindSettingsByCustomerNumber(774278L);
此处,FindSettingsByCustomerNumber()
是一种动态方法。我使用此代码解析此方法。
public class GenericRepositoryImpl<TEntity, TContext> :
GenericRepository<TEntity, TContext>
where TEntity : class, IIdentifiableEntity, new()
where TContext : DbContext, new()
{
public override bool TryInvokeMember(InvokeMemberBinder binder,
object[] args, out object result)
{
// Crude parsing for simplicity
if (binder.Name.StartsWith("Find"))
{
int byIndex = binder.Name.IndexOf("By");
if (byIndex >= 0)
{
string collectionName = binder.Name.Substring(4, byIndex - 4);
string[] attributes = binder.Name.Substring(byIndex + 2)
.Split(new[] { "And" }, StringSplitOptions.None);
var items = DbSet.ToList();
Func<TEntity, bool> predicate = entity => entity.GetType().GetProperty(attributes[0]).GetValue(entity).Equals(args[0]);
result = items.Where(predicate).ToList();
return true;
}
}
return base.TryInvokeMember(binder, args, out result);
}
}
这是我遇到的问题。
使用此行var items = DbSet.ToList();
效果很好,但如果我要查询包含1000个数据的大型表,则会出现性能问题。
如果我直接尝试使用IQueryble
界面并像这样调用它
Func predicate = entity =&gt; 。entity.GetType()的getProperty(属性[0])的GetValue(实体).Equals(参数[0]);
result = DbSet.Where(predicate).ToList();
它给出了一个错误,说LINQ to Entities中没有方法GetProperty()。
是否可以使用LINQ to Entities使其工作?
答案 0 :(得分:1)
您需要知道LINQ-to-Entities需要将表达式(由谓词指定)转换为SQL查询。 entity
由数据库列替换。此外,LINQ2Entities支持各种表达式(例如EqualExpression等)。但是它不能支持整个.NET Framework。特别是:数据库列上的GetType()应该返回什么?
因此,您需要使用Expresson API生成谓词,并仅使用LINQ2Entities支持的表达式。例如:使用MemberAccess
表达式访问属性(LINQ2Entities能够将其映射到SQL查询)。
提示:我们正在为Entity Framework进行谓词生成,并且必须克服一些我们可以使用库LinqKit
解决的其他问题。
如果您还不了解.NET Expression API,则需要先收集该领域的技能,然后才能恢复动态存储库的想法。
顺便说一句:我认为进行这种自动呼叫并不是一个好主意。它们不是重构安全的(即如果重命名DB列会怎样?所有方法调用都会遇到问题,并且编译器无法检测到它。)我只会用它来为类似过滤器的DTO类型中的Where()子句生成谓词。
答案 1 :(得分:1)
异常模式 - 存储库模式的动态方法。但这是另一个主题。
动态调用您拥有的存储库。 所以现在你需要更多地了解Linq to Entities。
Linq to Entities language reference你可以用linq对实体做什么。 鉴于表达式树必须转换为DB指令, 有限制并不令人惊讶。 如果您有兴趣The EF provider specs and links to samples
因此,如果你想要动态EF,你有几个选择。 我专注于动态,但你可以申请其他EF方法。
结帐
public virtual IQueryable<TPoco> DynamicWhere(string predicate, params object[] values) {
return Context.Set<TPoco>().Where(predicate, values);
}
这是一个接受字符串的IQueryable扩展... Samples of using this string based predicate parser
LinqKit甚至PM&gt; Install-Package LinqKit
Linqkit将动态EF提升到新的水平,
提供诸如
的惊人功能
public IQueryable<TPoco> AsExpandable() {
return Context.Set<TPoco>().AsExpandable();
}
允许您逐步构建AND和OR。
Expression Building API是支持您的最强大的工具。 学习API很难。更难使用工具。 例如,非常难以处理串联。但是,如果您能理解API以及表达式如何工作。 有可能的。 这是一个简单的例子。 (想象一些复杂的事情)
public static Expression<Func<TPoco, bool>> GetContainsPredicate<TPoco>(string propertyName,
string containsValue)
{
// (tpoco t) => t.propertyName.Contains(value ) is built
var parameterExp = Expression.Parameter(typeof(TPoco), @"t");
var propertyExp = Expression.Property(parameterExp, propertyName);
MethodInfo method = typeof(string).GetMethod(@"Contains", new[] { typeof(string) });
var someValue = Expression.Constant(containsValue, typeof(string));
var containsMethodExp = Expression.Call(propertyExp, method, someValue);
return Expression.Lambda<Func<TPoco, bool>>(containsMethodExp, parameterExp);
}