2

I have .net core rest api, which contains hybrid structure in which it only contains repositories and not the service layer.

Now, there is one issue that I am facing with base repository and main structure. let me explain the issue first.

So, consider one entity. let's say Product and below is the definition for that entity. this entity has one base class called FullAuditedEntity.

[Table(name: "Products")]
public class Product : FullAuditedEntity
{
    public string Name { get; set; }
}

public class FullAuditedEntity: IFullAuditedEntity
{
    public FullAuditedEntity() { }

    [Key]
    public virtual int Id { get; set; }
}

public interface IFullAuditedEntity
{
    int Id { get; set; }
}

The Base repository and it's interfaces are as below.

public class EntityBaseRepository<T> : IEntityBaseRepository<T> where T : class, IFullAuditedEntity, new()
{
    private readonly ApplicationContext context;

    public EntityBaseRepository(ApplicationContext context)
    {
        this.context = context;
    }

    public virtual IEnumerable<T> items => context.Set<T>().AsEnumerable().OrderByDescending(m => m.Id);

    public virtual T GetSingle(int id) => context.Set<T>().FirstOrDefault(x => x.Id == id);
}

public interface IEntityBaseRepository<T> where T : class, new()
{
    IEnumerable<T> items { get; }
    T GetSingle(int id);
}

So, my Product repository will be like this.

public interface IProductRepository : IEntityBaseRepository<Product> { }

public class ProductRepository : EntityBaseRepository<Product>, IProductRepository
{
    private readonly ApplicationContext context;

    public ProductRepository(ApplicationContext context) : base(context: context)
    {
        this.context = context;
    }
}

Now, up-to here everything is good, I can access this repository in controllers and can perform the actions that are listed in base class.

Issue I am facing : So with this structure, If I tries to add any new entity without FullAuditedEntity (see Product entity above, I have base class full audited entity over there), my structure of repository fails and it gives error.

let's say if I tries to add new entity Implementation, and this new entity has a random Id, so I do not want to inherit the FullAuditedEnitity base class. now in this case most of the thing will work fine but when I will try to create repository for Implementation entity, it will give generic error. see below snap of that.

enter image description here

What I tried so far...

I was thinking to create a parallel Base repository which does not inherit FullAuditedEntity as a generic class but I am not sure if it's best practice or not. also my concern is that what if I am doing any mistake in my current structure of repository pattern and Dependency injection?

Any help world be best and really appreciated.

Thank you in advance for your time.

Bharat
  • 5,869
  • 4
  • 38
  • 58

1 Answers1

2

Repositories are usually mapped to database tables. Database table should always have some column which can uniquely identify the row in table and it is common practice to call this column as Id. So you correctly implemented your FullAuditedEntity as there is Id property. However, your Id has always type of int. I suggest you to use the following construction and then your Id would be any type of struct such as int, decimal, Guid, and etc:

/// <summary>
/// Abstraction of the Entity
/// </summary>
public interface IEntity
{
    object Id { get; set; }        
}


/// <summary>
/// Base class for IDs
/// </summary>
public abstract class Entity<T>: IEntity where T: struct
{        
    public T Id { get; set; }

    object IEntity.Id
    {
        get { return Id; }
        set {                
            Id = (T)value;
        }
    }
}

public class EntityBaseRepository<T> : IEntityBaseRepository<T> where T : class, IEntity, new()
{ 
    // The code is omitted for the brevity
}

In addition, try to avoid entities without Id like Implementation as in future you will have to figure out how to find rows without Id in your database table.

UPDATE:

If you do not want to inherit FullAuditedEntity, then you can create BaseRepository<T> and then derive it in the EntityBaseRepository.

public abstract class BaseRepository<T> : IEntityBaseRepository<T> where T : class, new()
{
    public virtual IEnumerable<T> items => throw new NotImplementedException();

    public virtual T GetSingle(int id)
    {            
        throw new NotImplementedException();
    }
}

public class EntityBaseRepository<T> : BaseRepository<T> where T : class
                                             , IFullAuditedEntity, new()
{
    public override IEnumerable<T> items => base.items;

    public override T GetSingle(int id)
    {
        return base.GetSingle(id);            
    }
}

and then your Implementation repository:

public interface IImplementationRepository : IEntityBaseRepository<Implementation> { }


public class ImplementationRepository: BaseRepository<Implementation>
    , IImplementationRepository
{
    public override Implementation GetSingle(int id)
    {

        return base.GetSingle(id);
    }
}

UPDATE 1:

In my view, it is better to use services(Service layer) which consume ITRepository<T>. Because it gives to you new abilities such as:

  1. Add some calculations on data which got by Repository

  2. Remap your entities which are pulled by the Repository

  3. It is one additional layer of decoupling. So when you edit your service layer, then you don't need to edit Repository layer and then recompile your assembly

StepUp
  • 36,391
  • 15
  • 88
  • 148
  • I agree with your view on key. eventually Implementation class will also have the Guid as key but let's say, I don't want to inherit this FullAuditedEntity class at all for some of my Entity, in that case this structure is not working for me and giving error (see added snap of this error in question). can you please guide me on that point?? and what are your views on this service less structure? – Bharat Nov 11 '19 at 09:52
  • @Bharat try to avoid entities without Id because it could be hard to figure out how to update this entity. Could you write an example of an entity where you don't need to have `Id` property? – StepUp Nov 11 '19 at 10:07
  • I think you didn't get me, look I will have a Id property but inside entity class, and I do not want to inherit this FullAuditedEntity. that's all. now currently I am not able to achieve this and getting error. – Bharat Nov 11 '19 at 10:34
  • Thank you @StepUp. I came across this idea once and tried to implement it but didn't work at that time. Now, I have created this new BaseRepository and it's working fine. anything specific you would like to update me regarding this repository pattern? what are your views on this service less structure? I will accept this answer and will aword the bounty in next 20 hours as per SO rule. – Bharat Nov 11 '19 at 11:45
  • @Bharat [Repository/Service Design Pattern](https://codereview.stackexchange.com/questions/33109/repository-service-design-pattern) In addition, you can improve your code `Repository` pattern with `UnitOfWork` pattern. [Read more about UnitOfWork more](https://medium.com/@utterbbq/c-unitofwork-and-repository-pattern-305cd8ecfa7a) – StepUp Nov 11 '19 at 12:29
  • 1
    @Bharat Oh, great, I am glad that it helped to you! [Repository with Service Design Pattern](https://codereview.stackexchange.com/questions/33109/repository-service-design-pattern) In addition, you can improve your code Repository pattern with UnitOfWork pattern. [Read more about UnitOfWork more](https://medium.com/@utterbbq/c-unitofwork-and-repository-pattern-305cd8ecfa7a). However, there are so many opinions about Repository pattern and UnitOfWork pattern. – StepUp Nov 11 '19 at 12:41
  • I am not able to create a string Id as a primary key. it gives me error like "the type 'string' must be a non-nullable value type in order to..." can you please help me with that? I already wasted more then 3 hours on it.. – Bharat Nov 18 '19 at 06:19
  • 1
    @Bharat could you create another question to simplify the future search of other users? It is good practice to make one question per post. – StepUp Nov 18 '19 at 07:12
  • 1
    sure. here is the question for same and thank you for quick response... https://stackoverflow.com/questions/58909782/c-sharp-generics-error-the-type-string-must-be-non-nullable – Bharat Nov 18 '19 at 07:31