我可以为通用存储库实现 PK 类型参数吗?
Can I implement a PK Type argument for a generic repository?
我正在尝试为 DbEntity
的基础 class 实施通用存储库 - 其 ID 可能是 int
或 guid
。
我几乎可以正常工作,但我被存储库的 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));
我正在尝试为 DbEntity
的基础 class 实施通用存储库 - 其 ID 可能是 int
或 guid
。
我几乎可以正常工作,但我被存储库的 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));