我可以为通用存储库实现PK Type参数吗?

时间:2016-03-09 15:32:51

标签: c# generics struct repository primary-key

我正在尝试为我的DbEntity基类实现通用存储库 - 其ID可以是intguid

我几乎让它工作了,但我被困在了存储库的SelectByID()方法中。

语法table.Find(id)工作得很好。

但语法SingleOrDefault(i => i.ID == id)没有。它给了我operator '=='cannot be applied to operants of type PKT and PKT

但我想使用第二个版本(因为它允许我使用Include()加载相关实体。)

我尝试过不对PKT使用约束 我尝试过使用struct约束但我仍然遇到同样的错误。

我应该如何实施?

更新

经过@StriplingWarrior的大量帮助后,我提出了以下建议:

public virtual T SelectByID(PKT id, params Expression<Func<T, object>>[] includeExpressions)
{
    if (includeExpressions.Any())
    {
        var set = includeExpressions.Aggregate<Expression<Func<T, object>>, IQueryable<T>>
                    (table, (current, expression) => current.Include(expression));

        ParameterExpression parameter = Expression.Parameter(typeof(T), "s");
        PropertyInfo propertyInfo = typeof(T).GetProperty("ID");
        MemberExpression memberExpression = Expression.MakeMemberAccess(parameter, propertyInfo);
        ConstantExpression constantExpression = Expression.Constant(id, typeof(PKT));
        BinaryExpression binaryExpression = Expression.Equal(memberExpression, constantExpression);
        Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(binaryExpression, parameter);

        return set.SingleOrDefault(lambda);

    }
    return table.Find(id);
}

基本实体

    public interface IDBEntity<PKT>
        where PKT : struct
    {
        PKT ID { get; set; }
        string Title { get; set; }
    }

    public class DBEntity<PKT> : IDBEntity<PKT>
        where PKT : struct 
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public PKT ID { get; set; }
        public virtual string Title { get; set; }
    }

用法

    public class Car : DBEntity<Guid>
    {
        public string Colour { get; set; }
    }
    public class Pet : DBEntity<int>
    {
        public bool FurryOrNot { get; set; }
    }

通用存储库

    public class GenericRepository<T, PKT> : IGenericRepository<T, PKT>
        where T : DBEntity<PKT>
        where PKT : struct
    {
        private ApplicationDbContext db = null;
        private DbSet<T> table = null;


        public GenericRepository()
        {
            db = new ApplicationDbContext();
            table = db.Set<T>();
        }

        public virtual T SelectByID(PKT id, params Expression<Func<T, object>>[] includeExpressions)
        {
            if (includeExpressions.Any())
            {
                var set = includeExpressions.Aggregate<Expression<Func<T, object>>, IQueryable<T>>
                         (table, (current, expression) => current.Include(expression));

                return set.SingleOrDefault(s => s.ID == id);

            }
            return table.Find(id);
        }


        public virtual IQueryable<T> AllAsQueryable(params Expression<Func<T, object>>[] includeExpressions)
        {
            return includeExpressions.Aggregate<Expression<Func<T, object>>, IQueryable<T>>
                (table, (current, expression) => current.Include(expression));
        }



        public virtual IQueryable<T> AllWhereAsQueryable(Expression<Func<T, bool>> wherePredicate, params Expression<Func<T, object>>[] includeExpressions)
        {
            return AllAsQueryable(includeExpressions).Where(wherePredicate);
        }


        public virtual void Create(T obj)
        {
            table.Add(obj);
            Save();
        }



        public virtual void Edit(T obj)
        {
            table.Attach(obj);
            db.Entry(obj).State = EntityState.Modified;
            Save();
        }



        public void Delete(object id)
        {
            T existing = table.Find(id);
            table.Remove(existing);
            Save();
        }



        public virtual void Save()
        {
            db.SaveChanges();
        }



        public virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                db.Dispose();
            }
        }
    }

1 个答案:

答案 0 :(得分:1)

您遇到此错误的原因是因为在编译时==运算符正在尝试根据编译时类的已知信息进行绑定,这不是“得多。

您可能想尝试使用.Equals()。我不确定这是否有效。

我过去对此的看法是在运行时生成一个表达式树,它使用泛型参数的实际已知运行时类型。所以不是这样:

return set.SingleOrDefault(s => s.ID == id);

......会是这样的:

var parameter = Expression.Parameter("s", typeof(T));
return set.SingleOrDefault(
    Expression.Lambda<Func<T, bool>>(
        Expression.Equal(
            /*make an expression that gets the ID property from parameter*/,
            Expression.Constant(id, typeof(PKT)),
        parameter));