【问题标题】:EF object comparison with generic typesEF 对象与泛型类型的比较
【发布时间】:2012-05-11 05:39:16
【问题描述】:

我有以下问题:我有一个使用为 Int 或 Guid 实体键设计的 DbContext 的通用 EF 存储库,所以我有一个基实体类:

public class EntityBase<TKey> where TKey : struct, IComparable
{
    public virtual TKey Id { get; set; }
}
  • TKey 将在派生类中作为 Int 或 Guid 提供。

当我运行代码时

public virtual void LoadEntity()
{
    TEntity entity = Repository.Get<TEntity, TKey>(e => object.Equals(e.Id, EntityId));
}

public virtual void LoadEntity()
{
    TEntity entity = Repository.Get<TEntity, TKey>(e => e.Id.CompareTo(EntityId) == 0);
}

其中Entity是TKey类型,在派生类中设置为int,例如我得到如下错误:

无法将类型“System.Int32”转换为类型“System.Object”。 LINQ to Entities 仅支持强制转换实体数据模型基元类型

Repository.Get 只需将谓词参数作为过滤器传递给 DbSet 存储库的 Where 调用;

我了解错误 - EF 尝试转换为 SQL 语句并且不知道如何处理对象比较。但我不知道如何重写基类和/或 LoadEntity() 函数以允许 EF 使用原始类型进行操作。 有什么想法吗?

【问题讨论】:

    标签: entity-framework generics types comparison


    【解决方案1】:

    我认为有一个简单的方法可以解决它,但它是 hack。让我再次强调一下——这确实是一个hack。你可以试试这个:

    Repository.Get<TEntity, TKey>(e => (object)e.Id == (object)EntityId);
    

    上面的代码通常不应该工作。在 CLR 世界中,这些值将被装箱并通过引用进行比较。即使装箱的值相同,引用也会不同,因此结果将是错误的。但是 EF 查询不由 CLR 执行,而是转换为 SQL。结果,查询将被转换为:WHERE Id = {EntityId},这正是您所需要的。同样,使用它需要了解这些东西的工作原理和原因,并且可能有点冒险。但是,由于存在 hack,因此应该有更清洁的解决方案。事实上,干净的(这里不是简单的解决方案)是手动构建上述表达式。这是一个示例(抱歉,我没有完全使用您的实体):

        private static TEntity GetEntity<TEntity, TKey>(Expression<Func<TEntity, TKey>> property, TKey keyValue)
            where TKey : struct
            where TEntity : BaseEntity<TKey>
        {
            using (var ctx = new Context2())
            {
                var query = Filter(ctx.Set<TEntity>(), property, keyValue);
                return query.First();
            }
        }
    
    
        private static IQueryable<TEntity> Filter<TEntity, TProperty>(IQueryable<TEntity> dbSet,
                                                                      Expression<Func<TEntity, TProperty>> property,
                                                                      TProperty value)
            where TProperty : struct
        {
    
            var memberExpression = property.Body as MemberExpression;
            if (memberExpression == null || !(memberExpression.Member is PropertyInfo))
            {
                throw new ArgumentException("Property expected", "property");
            }
    
            Expression left = property.Body;
            Expression right = Expression.Constant(value, typeof (TProperty));
    
            Expression searchExpression = Expression.Equal(left, right);
            var lambda = Expression.Lambda<Func<TEntity, bool>>(Expression.Equal(left, right),
                                                                new ParameterExpression[] {property.Parameters.Single()});
    
            return dbSet.Where(lambda);
        }
    

    请注意,在过滤器方法中,我构建了一个过滤器表达式,我可以在上面进行组合。在这个例子中,有效的查询看起来像这样 DbSet().Where(e => e.Id == idValue).First() (看起来类似于上面的 hack)但是你可以在这个查询之上使用其他 linq 运算符(包括在Filter方法的结果上调用Filter方法进行多条件过滤)

    我将实体和上下文定义如下:

    public class BaseEntity<TKey> where TKey : struct
    {
        public TKey Id { get; set; }
    }
    
    public class EntityWithIntKey : BaseEntity<int>
    {
        public string Name { get; set; }
    }
    
    public class EntityWithGuidKey : BaseEntity<Guid>
    {
        public string Name { get; set; }
    }
    
    public class Context2 : DbContext
    {
        public DbSet<EntityWithIntKey> EntitiesWithIntKey { get; set; }
    
        public DbSet<EntityWithGuidKey> EntitiesWithGuidKey { get; set; }
    }
    

    您像这样调用 GetEntity 方法:var e2 = GetEntity(e =&gt; e.Id, guidKey);

    【讨论】:

    • 嗨 Pawel,我刚刚使用了 Repository.Get(e => (object)e.Id == (object)EntityId),它起作用了;这很奇怪,因为它也是对象的强制转换,但是,正确生成了 sql (SELECT .... WHERE Id = {EntityId})。谢谢!
    • 很高兴听到您解决了问题。请将回复标记为您的问题的答案。谢谢!
    • 我必须说明一点:虽然 Repository.Get(e => (object)e.Id == (object)EntityId) 在数据库上工作,但在-内存集合;所以我尝试了 Pawel 提出的第二种解决方案(使用 Filter(IQueryable dbSet, Expression> property, TProperty value),这是正确的解决方案
    • 这就是为什么我说这是一个 hack 并且它不应该工作(我也解释了原因)。这只是因为 Linq 查询被 Linq 转换为实体提供程序的方式。
    猜你喜欢
    • 1970-01-01
    • 2016-07-26
    • 2014-06-13
    • 1970-01-01
    • 1970-01-01
    • 2014-12-10
    • 2016-06-14
    • 1970-01-01
    • 2011-04-27
    相关资源
    最近更新 更多