我在数据库中有多个表,并且每个表的主键很长,并且id列名称不是通用的,即每个表的主键列名称都不同。
例如:
git remote set-url heroku <your_url>
的主键为“ CustomerId” Customer
的主键为'EmployeeId'同样在域模型类中表示
Employee
如何引用主键来获取基于不同主键名称的值。
class Customer { public long CustomerId { get; set; }
class Employee { public long EmployeeId { get; set; }
public async Task<TEntity> GetById(int id)
{
return await _dbContext.Set<TEntity>()
.AsNoTracking()
.FirstOrDefaultAsync(e => e.**Id** == id);
}
可以是Id
或CustomerId
答案 0 :(得分:1)
根据您实际要执行的操作,我会考虑将类中的属性重命名为Id,然后将它们映射到模型配置中数据库中的键:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MyEntityType>().Property(k => k.Id).HasColumnName("CustomerId");
// ...
}
但是,如果您确实想保留这些属性,但仍然想使用通用方法来获取它们(上帝知道原因),则可能必须在某个接口上公开一个func,以便以后用于比较。
也许是这样的:
// An interface that requires a func to match it's id
public interface IExpressionKeyEntity
{
// A func that takes an int and returns true if it's a match
// against the entity id
Func<int, bool> HasMatchingId { get; }
}
最简单的实现:
// A simple customer class that implements the interface
public class Customer : IExpressionKeyEntity
{
// An id property that we don't know anything about from the outside
public int CustomerId { get; set; }
// A func that takes an id and returns true if it's a match against this entities id
public Func<int, bool> HasMatchingId => comparisonEntityId => comparisonEntityId == CustomerId;
}
然后在对GetById方法进行一些细微更改的情况下使用它:
// GetById now requires an IExpressionKeyEntity to be retrieved
public async Task<TEntity> GetById<TEntity>(int id)
where TEntity : IExpressionKeyEntity
{
// Return the first match based on our func
return await _dbContext.Set<TEntity>()
.AsNoTracking()
.FirstOrDefault(q => q.HasMatchingId(id));
}
我觉得之前我曾偶然遇到过Func和EF的一些问题,但这在技术上应该是可行的。否则,您将不得不深入研究表达式。
答案 1 :(得分:0)
因此,使用这种通用模式时,您的逻辑会有些瑕疵。没有属性和反射,您将无法从TEntity类型确定主ID字段的名称。一个更好的主意是让您的实体实现一个接口,然后对您的泛型施加约束。遵循这些思路
public interface IEntity {
int id {get;set;}
}
public class Customer : IEntity {
public int id {get; set;}
}
然后在存储库中,可以将泛型约束为IEntity的类型。 这样,您就可以根据ID字段查询任何IEntity类型。
where TEntity: IEntity
然后,您的方法只需要在界面中定义的ID字段中起作用
public async Task<TEntity> GetById(int id)
{
//Since TEntity is an IEntity, it is guaranteed to have an id property
return await _dbContext.Set<TEntity>()
.AsNoTracking()
.FirstOrDefaultAsync(e => e.Id == id);
}
答案 2 :(得分:0)
首先,每当看到有人通过EF实现存储库模式时,我都会感到不寒而栗
但是,除了我的内心平静。如果您具有标准的ID和标准化的ID命名,则可以使用接口
public interface IId
{
public long Id {get;set;}
}
// ...
class Customer : IId { public long ID{get;set;} ...}
class Employee : IId { public long ID{get;set;} ...}
public async Task<TEntity> GetById<TEntity>(int id) where TEntity : IId
{
return await _dbContext.Set<TEntity>()
.AsNoTracking()
.FirstOrDefaultAsync(e => e.Id == id);
}
或者您可以使用表达式
public static async Task<T> GetById<T>(this IQueryable<T> entities, Expression<Func<T, long>> propertySelector, long id)
{
var member = propertySelector.Body as MemberExpression;
var property = member.Member as PropertyInfo;
var parameter = Expression.Parameter(typeof(T));
var expression = Expression.Lambda<Func<T, bool>>(Expression.Equal(Expression.Constant(id), Expression.Property(parameter, property)), parameter);
return await entities.FirstOrDefaultAsync(expression);
}
免责声明,所有这些都未经测试,也没有错误检查