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

Can I implement a PK Type argument for a generic repository?

我正在尝试为 DbEntity 的基础 class 实施通用存储库 - 其 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();
            }
        }
    }

您 运行 陷入此错误的原因是因为在编译时 == 运算符试图根据编译时有关 类 的已知信息进行绑定-时间,不多。

您可能想尝试使用 .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));