I have multiple tables in database and for each table the primary key is long and the id column name is not generic i.e. each table primary key column name is different.
For example:
Customer has primary key as 'CustomerId'Employee has primary key as 'EmployeeId' And same get represented in the domain model class
class Customer { public long CustomerId { get; set; }
class Employee { public long EmployeeId { get; set; }
How do I refer in primary key to get values based on different primary key name.
public async Task<TEntity> GetById(int id)
{
return await _dbContext.Set<TEntity>()
.AsNoTracking()
.FirstOrDefaultAsync(e => e.**Id** == id);
}
Id can be CustomerId or EmployeeId
Depending on what you actually want to do, I would consider renaming the properties in the class to Id, and then map them to the keys in the database in the model configuration instead:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MyEntityType>().Property(k => k.Id).HasColumnName("CustomerId");
// ...
}
However, if you really want to stick with those properties, and still want to use generic methods to get them (god knows why), you would probably have to expose a func on some interface that you later can use for the comparison.
Perhaps something like this:
// 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; }
}
With the simplest implementation of:
// 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;
}
And then use it with some minor changes to the GetById-method:
// 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));
}
I have a feeling that I've stumbled into some problems with Func and EF before, but this should technically work. Otherwise you'll have to dig in on the Expressions.
So to use this generic pattern your logic is slightly flawed. You will not be able to determine the name of the primary id field from the TEntity type without attributes and reflection. A better idea would be to have your entities implement an Interface and then place a constraint on your generic. Something along these lines
public interface IEntity {
int id {get;set;}
}
public class Customer : IEntity {
public int id {get; set;}
}
Then in your repository you can constrain the generics to the type of IEntity. This will allow you to just query based on the id field for any IEntity type.
where TEntity: IEntity
Then you method works by just looking for the Id field that is defined int he interface
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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With