2

I have a Repository class, which looks like this:

public class AppUserRepository : BaseRepositoryViewAsync<AppDbContext, AppUser, DTO.AppUsers.AppUser>, IAppUserRepository
{
    public AppUserRepository(AppDbContext dbContext, IBaseMapper<AppUser, DTO.AppUsers.AppUser> mapper) : base(dbContext, mapper)
    {
    }
}

The implementation of BaseRepositoryViewAsync looks like this:

public class BaseRepositoryViewAsync<TDbContext, TEntityIn, TEntityOut> :
    BaseRepositoryViewAsync<TDbContext, TEntityIn, TEntityOut, Guid>, IBaseRepositoryViewAsync<TEntityOut> 
    where TDbContext : BaseDbContext<BaseUser<Guid>, Guid> 
    where TEntityIn : class, IDomainEntityId
    where TEntityOut : class, IDomainEntityId
{
    public BaseRepositoryViewAsync(TDbContext dbContext, IBaseMapper<TEntityIn, TEntityOut> mapper) : base(dbContext, mapper)
    {
    }
}

Well there we can see, that the requirement for TDbContext is, that it is a BaseDbContext<BaseUser, Guid>

The AppDbContext:

public class AppDbContext : BaseDbContext<AppUser, Guid>
{
}

And the AppUser:

public class AppUser: BaseUser<Guid>, IDomainEntityId
{
}

The error that I get:

AppUserRepository.cs(8, 18): [CS0311] The type 'DAL.App.EF.AppDbContext' cannot be used as type parameter 'TDbContext' in the generic type or method 'BaseRepositoryViewAsync<TDbContext, TEntityIn, TEntityOut>'. There is no implicit reference conversion from 'DAL.App.EF.AppDbContext' to 'Base.DAL.EF.BaseDbContext<Base.Domain.Identity.BaseUser<System.Guid>, System.Guid>'.

From the error I understand, that the AppDbContext does not fit the requirements of being a BaseDbContext, but I can not figure out why it does not fit, since the AppDbContext directly Extends it.

It seems to me, that everything is implemented correctly, but I also might be wrong.

The not so liked fix is here:

public class AppDbContext : BaseDbContext<BaseUser<Guid>, Guid>
{
    public new DbSet<AppUser> Users { get; set; } = null!;
}

I don't like this, because then I must override the users dbSet and this will create a discriminator column in the db, which is not necessary, since all users will be AppUsers.

MD. RAKIB HASAN
  • 3,670
  • 4
  • 22
  • 35
  • A `BaseDbContext, Guid>` is a different type of object to a `BaseDbContext`. I would suggest you go read all about [covariance and contravariance](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance/) – DavidG Nov 09 '21 at 12:24

1 Answers1

3

To simplify, this will not compile:

BaseDbContext<BaseUser<Guid>, Guid>> x = new AppDbContext();

Even though AppDbContext inherits from BaseDbContext<AppUser, Guid>, and AppUser inherits BaseUser<Guid>.

The reason is covariance and contravariance in generics. It's easiest to explain with supposing that it's possible and see what can go wrong.

In this case, suppose assignment above works and you have something like this in base context:

public class BaseDbContext<TUser, IID>
{
    public DbSet<TUser> Users { get; set; }
}

Now, for AppDbContext this is DbSet<AppUser>, but if I was able to assign AppDbContext to BaseDbContext<BaseUser<Guid>, Guid>> - that would mean I can now add any BaseUser<Guid> to that DbSet, not just AppUser. I could add some other user which inherits from BaseUser<Guid> but is NOT AppUser. For reasons like this such assignment is not allowed.

Evk
  • 98,527
  • 8
  • 141
  • 191