动态库的动态库

时间:2014-03-18 08:21:44

标签: linq entity-framework

受到这篇文章的启发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;
}

如您所见,我的摘要GenericRepositoryDynamicObject扩展到支持动态存储库。

我还有一个抽象的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使其工作?

2 个答案:

答案 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方法。

结帐

  • Dynamic Linq on codeplex
    允许像

    这样的东西

    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 trees

    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);
    }