Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Primary Key attribute name is different in Generic Repository Pattern

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:

  • Table Customer has primary key as 'CustomerId'
  • Table 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

like image 575
Hafeez Khan Avatar asked Nov 02 '25 04:11

Hafeez Khan


2 Answers

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.

like image 58
Classe Avatar answered Nov 03 '25 20:11

Classe


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);
}
like image 23
hawkstrider Avatar answered Nov 03 '25 20:11

hawkstrider