Dbcontext Inheritance error while attempting to activate repository - c#

My application has two databases: MainDbContext which inherits from DbContext and IdentifiedDbContext which inherits from MainDbContext.
MainDbContext uses base classes like EntityBase with Id, createdAt, updatedAt. IdentifiedDbContext uses IdentifiedEntityBase classes that inherit from EntityBase with properties CreatorId, OwnerId.
My application also has a Repository, and IdentifiedRepository which inherits from Repository.
Code looks like this:
public class BeehouseContext:DbContext
{
public BeehouseContext(DbContextOptions<BeehouseContext> options) : base(options)
{
// -- Construtor
}
protected BeehouseContext(DbContextOptions options):base(options)
{
// -- Construtor fix
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
#region EntityBase
modelBuilder.Ignore<Beehouse.Framework.Domain.EntityBase>();
#endregion
}
}
{
public BeehouseIdentityContext(DbContextOptions<BeehouseIdentityContext> options) : base(options)
{
// -- Construtor
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
#region EntityBase
modelBuilder.Ignore<Beehouse.Framework.Identity.Domain.IdentifiedEntity>();
#endregion
}
}
public class Repository<TEntity> : IRepository<TEntity> where TEntity : EntityBase
{
protected readonly BeehouseContext Context;
public Repository(BeehouseContext context)
{
Context = context;
}
private DbSet<TEntity> _entities;
protected virtual DbSet<TEntity> Entities
{
get
{
if (_entities is null)
{
_entities = Context.Set<TEntity>();
}
return _entities;
}
}
// Other methods
}
public class IdentifiedRepository<TEntity>:Repository<TEntity>, IIdentifiedRepository<TEntity> where TEntity:IdentifiedEntity
{
protected readonly string UserId; // as Dealer
protected readonly string CreatorId; // as Retailer
protected readonly string CreatorAlias;
public IdentifiedRepository(IHttpContextAccessor httpContextAccessor, BeehouseIdentityContext context):base(context)
{
if (httpContextAccessor.HttpContext.User.FindFirst("sub") == null) return;
CreatorId = httpContextAccessor.HttpContext.User.FindFirst("sub").Value;
CreatorAlias = httpContextAccessor.HttpContext.User.FindFirst(SubscriptionClaimTypes.ProfileName).Value;
UserId = httpContextAccessor.HttpContext.User.FindFirst(SubscriptionClaimTypes.DealerUserId).Value;
}
In dependency injection I can create an instance of IdentifiedRepository, but in some cases I need to instantiate the Repository only, but I can't. The following error is thrown:
InvalidOperationException: Unable to resolve service for type
'Microsoft.EntityFrameworkCore.DbContextOptions`1[Beehouse.Framework.Identity.Data.BeehouseIdentityContext]'
while attempting to activate
'Beehouse.Framework.Identity.Data.BeehouseIdentityContext'.
I've tried to edit constructors like:
BeehouseContext(DbContextOptions options) : base(options)
BeehouseIdentityContext(DbContextOptions options) : base(options)
But this doesn't work either.
My dependency injection looks like this and doesn't work:
// Database
services.AddDbContext<IdentityServerContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("connstring")));
services.AddTransient(typeof(BeehouseContext), typeof(BeehouseIdentityContext));
I also tried to add DbContext like this:
services.AddDbContext<IdentityServerContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("auxiliarc-local")));
services.AddDbContext<BeehouseContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("auxiliarc-local")));
But that fails too with the following error:
InvalidOperationException: Cannot create a DbSet for 'Plan' because
this type is not included in the model for the context.
Plan are in namespace and Project Identified, but inherits from EntityBase only.
Can somebody help-me? Thanks.

The injector is looking to create an instance of type BeehouseIdentityContext.
The constructor requires an object of type DbContextOptions. The injector cannot instantiate this type for the activation of BeehouseIdentityContext so it throws an error.
Either tell the injector how to create instances of type DbContextOptions or create a constructor that uses defaults and does not need this type.

Related

MOQ SetupGet dbContext with constructor parameters

Situation
Here I am, trying to write some unit tests for my GroupService with the use of MOQ.
To create an instance of my GroupService, I mocked 4 interfaces that needed to be passed through the constructor. Now on one of the mocks (IGroupRepository) a property called Context is called and my idea was to SetupGet this property and just simply return a fake list of GroupUser. But I keep getting errors, whatever I try.
Code
public class GroupServiceTests
{
private readonly GroupService _groupService;
private readonly Mock<AppDbContext> _dbContext;
private readonly Mock<IGroupRepository> _groupRepository;
private readonly Mock<IComponentService> _componentService;
private readonly Mock<IUserContextService> _userContextService;
private readonly Mock<IModelEntityMapper<Group, Core.DbContexts.Entities.Group>> _mapper;
public GroupServiceTests()
{
var groupUsersMock = CreateDbSetMock(GetFakeListOfGroupUsers());
_dbContext = new Mock<AppDbContext>(new DbContextOptions<AppDbContext>());
_dbContext.SetupGet(x => x.GroupUser).Returns(groupUsersMock.Object);
_groupRepository = new Mock<IGroupRepository>();
_groupRepository.SetupGet(repo => repo.Context).Returns(_dbContext.Object);
_componentService = new Mock<IComponentService>();
_userContextService = new Mock<IUserContextService>();
_mapper = new Mock<IModelEntityMapper<Group, Core.DbContexts.Entities.Group>>();
_groupService = new GroupService(_groupRepository.Object, _componentService.Object, _userContextService.Object, _mapper.Object);
}
}
In the GroupService this line is called:
// _repository reffers to IGroupRepository
userIdsForContextReset.AddRange(_repository.Context.GroupUser.Where(x => groupIds.Contains(x.GroupId)).Select(x => x.UserId));
And the GroupRepository and EntityRepository look like this:
public interface IGroupRepository : IEntityRepository<AppDbContext, Group>
{
List<GroupPermission> GetInheritedGroupPermissions(int groupId);
}
public class GroupRepository : EntityRepository<AppDbContext, Group>, IGroupRepository
{
public GroupRepository(AppDbContext dbContext) : base(dbContext)
{
}
public List<GroupPermission> GetInheritedGroupPermissions(int groupId)
{
// Removed for brevity
}
}
public class EntityRepository<TDbContext, TEntity> : EntityRepository<TDbContext, TEntity, int>, IEntityRepository<TDbContext, TEntity>
where TDbContext : DbContext
where TEntity : class, IEntity<int>
{
public EntityRepository(TDbContext dbContext) : base(dbContext)
{
}
}
public class EntityRepository<TDbContext, TEntity, TId> : IEntityRepository<TDbContext, TEntity, TId>
where TDbContext : DbContext
where TEntity : class, IEntity<TId>
where TId : IComparable
{
public EntityRepository(TDbContext context)
{
Context = context;
}
public TDbContext Context { get; }
}
And last but not least, the AppDbContext and SqlDbContext:
public class AppDbContext : Shared.DbContexts.SqlDbContext
{
public virtual DbSet<GroupUser> GroupUser { get; set; }
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
}
public class SqlDbContext : DbContext
{
public SqlDbContext(DbContextOptions options) : base(options)
{
ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
ChangeTracker.StateChanged += ChangeTracker_StateChanged;
}
}
Error
The error that I am getting is inside the SqlDbContext on the 1st line inside the constructor and says the following:
System.InvalidOperationException: 'No database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the application service provider. If AddDbContext is used, then also ensure that your DbContext type accepts a DbContextOptions object in its constructor and passes it to the base constructor for DbContext.'
What am I doing wrong?
When you mock an implementation it creates the object using the constructor matching the parameters provided; it runs that code.
Additionally, anything not able to be mocked (not virtual or abstract) will run as is. In this case, you're passing in DbContextOptions and you haven't specified a provider, and something needs that.
This can be an opinionated topic, however to solve your problem there are a number of ways you could do it:
Add a parameterless constructor to your DbContext for testing. I wouldn't recommend this as I follow the mantra of not changing your SUT for a test.
Use an in-memory provider; EF Core In-Memory Database Provider or SQLite EF Core Database Provider are two that I have used. They do have limitations but for the OP usage would probably be fine and addresses Microsofts notes about how you shouldn't mock the DbContext.
Use an existing library such as EntityFrameworkCore.Testing (disclaimer, I am the author) which will extend in-memory providers to address their limitations.

Registering multiple DBContexts in .net core with same base class

So just curiosity here, I have an app that has an internal site and an external site. The db for them is identical. Basically the internal app is for users to change data that is shown on the external app. So for this, I decided to create a single db context for the app for the internal db connection. I then created another context that inherited from the internal one for the external connection I did this because I thought I could add them to the service and set each up to it's own database. When I inject them into my promoter class, both db connections are pointing to the same db. Why is this? So what I have for my contexts is:
public class AppContext : DbContext
{
public AppContext() { }
public AppContext(DbContextOptions<AppContext> options): base(options)
{ }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Folder.Folder>().ToTable("Folders");
modelBuilder.Entity<File.File>().ToTable("Files");
modelBuilder.Entity<DocumentView>().ToTable("DocumentViews")
.HasKey(c=> new { c.PersonId, c.DocId });
modelBuilder.Entity<AppInfo>().ToTable("AppInfo");
}
public DbSet<Folder.Folder> Folders { get; set; }
public DbSet<File.File> Files { get; set; }
public DbSet<DocumentView> DocumentViews { get; set; }
public DbSet<AppInfo> AppInfos { get; set; }
}
public class ExternalAppContext : AppContext
{
public ExternalAppContext(DbContextOptions<AppContext> options) : base(options)
{ }
}
When I registered them in my startup, I registered them as such.
services.AddDbContext<AppContext>(options => options.UseSqlite(#"Data Source=" + Configuration["SqlConnection:adminDbLocation"]));
services.AddDbContext<ExternalAppContext>(options => options.UseSqlite(#"Data Source=" + Configuration["SqlConnection:externalDbLocation"]))
Promoter class injection is:
public Promoter(ExternalAppContext externalContext, AppContext adminContext)
{
_externalContext = externalContext;
_adminContext = adminContext;
}
So the real answer here was nested in a github answer from three years ago that can be found https://github.com/dotnet/efcore/issues/7533. The issue comes down to how the services are being rendered for pooling, which is why when registering multiple DbContexts that you have to specify the type on the DbContextOptions. To get around this on the base class you can have a protected constructor for the options. This will allow the pooling to work correctly and allow you to inherit the context. The example given by greggbjensen on Dec 2017 is below and was exactly what I was looking for.
public class MainDbContext : DbContext
{
public MainDbContext(DbContextOptions<MainDbContext> options)
: base(options)
{
}
protected MainDbContext(DbContextOptions options)
: base(options)
{
}
}
public class SubDbContext : MainDbContext
{
public SubDbContext (DbContextOptions<SubDbContext> options)
: base(options)
{
}
}
This allows for two services to be set up in the .net core services code as such.
services.AddDbContext<MainDbContext >(options => [[SomeOptionsHere]]);
services.AddDbContext<SubDbContext >(options => [[SomeOptionsHere]]);

Abstract Entity Framework

I want to create an abstraction layer between Entity Framework and the rest of my application. But I am having a few problems with Entity Framework.
Basically (I don't show you all the interface layers that I've created too), I've split my application into several projects like this :
Domain
Contains my domain object, an abstraction of my datastorage object
DAL
Creates a link between my datastorage and my business layer. Contains two types of elements :
Private ones : my EDMX, my database object, and some other generated objects providing me some useful methods like ToDomain/ToEntity
Public ones : my Data Access Object, providing CRUD methods
Business
Contains the logic of my application. Only knows about the public elements of the DAL and the Domain Layer.
Presentation
Presents the domain objects for the user. Only knows about the business layer.
As I said, I want to create an abstraction of my datastorage objects (in my case Database object, but I want a solution that works also for file or WCF storage for example) so that my business layer don't know anything about my DAL implementation.
Here is a glimpse of what I've done in my DAL :
public abstract class GenericDao<TEntity, TDomain, TDbContext> : IGenericDao<TDomain>
where TDbContext : DbContext, new()
where TEntity : class
where TDomain : class
{
protected TDbContext _context;
protected DbSet<TEntity> _dbSet;
public GenericDao(TDbContext dbContext)
{
this._context = dbContext;
this._dbSet = dbContext.Set<TEntity>();
}
public TDomain Create()
{
return this.ToDomain(this._dbSet.Create());
}
public IList<TDomain> GetAll()
{
return this._dbSet.ToList().Select(entity => this.ToDomain(entity)).ToList();
}
public void Update(TDomain domain)
{
var entity = this.ToEntity(domain);
var entry = this._context.Entry(entity);
entry.State = EntityState.Modified;
}
public void Remove(TDomain domain)
{
_dbSet.Remove(this.ToEntity(domain));
}
protected abstract TDomain ToDomain(TEntity entity);
protected abstract TEntity ToEntity(TDomain domain);
}
You will probably see what's wrong with my code by reading it: when I try to delete or update an entity, I am not manipulating an entity attached to Entity Framework. If I try to attach my entity to the dbContext, it fails because there is already an entity in the context with the same id.
I already thought about several solutions, but none of them please me.
Maybe am I doing something wrong in my approach? I am a little bit confused about the Repository and DAO pattern (I read anything and the very opposite about that difference on the internet).
You have two options:
initialize new dbcontext for each operation and dispose it when operation is ended:
public abstract class GenericDao<TEntity, TDomain, TDbContext> : IGenericDao<TDomain>
where TDbContext : DbContext, new()
where TEntity : class
where TDomain : class
{
protected Func<TDbContext> _contextFactory;
public GenericDao(Func<TDbContext> contextFactory)
{
_contextFactory = contextFactory;
}
public TDomain Create()
{
using(var context = _contextFactory())
{
return context.Set<TEntity>().Create();
}
}
public IList<TDomain> GetAll()
{
using(var context = _contextFactory())
{
return context.Set<TEntity>().ToList()
.Select(entity => this.ToDomain(entity)).ToList();
}
}
public void Update(TDomain domain)
{
using(var context = _contextFactory())
{
var entity = this.ToEntity(domain);
context.Attach(entity);
var entry = this._context.Entry(entity);
entry.State = EntityState.Modified;
context.SaveChanges();
}
}
public void Remove(TDomain domain)
{
using(var context = _contextFactory())
{
var entity = this.ToEntity(domain);
context.Attach(entity);
context.Set<TEntity>.Remove(entity);
context.SaveChanges();
}
}
protected abstract TDomain ToDomain(TEntity entity);
protected abstract TEntity ToEntity(TDomain domain);
}
or you can try to find entity in your instance of dbcontext using property Local of DbSet:
var contextEntity = context.Set<TEntity>().Local
.Where(c=>c.Id == entity.Id).FirstOrDefault();
You seem to be getting stuck coding to an implementation within your abstraction. If you injected an interface to your generic rather than a concrete type (like EF) then your GenericDao becomes much more flexible. You can inject whatever implementation you choose providing it implements the required interface. In your case, WCF, File, Whatever. For example;
protected IDbContext _context;
public GenericDao(IDbContext dbContext)
{
this._context = dbContext;
}
public void Remove(TDomain domain)
{
_context.Remove(this.ToEntity(domain));
}
//abstraction
public interface IDbContext
{
void Remove(Entity entity);
}
//EF Implementation
public MyEfClass : IDbContext
{
public void Remove(Entity entity)
{
//code to remove for EF example
context.Attach(entity);
context.State = EntityState.Modified;
context.Set<TEntity>.Remove(entity);
context.SaveChanges();
}
}
//WCF Implementation
public MyWCFClass : IDbContext
{
public void Remove(Entity entity)
{
//Wcf implementation here
}
}
//File example
public FileWriter : IDbContext
{
public void Remove(Entity entity)
{
LoadFile();
FindEntry(entity);
WriteFile(entity);
SaveFile();
}
public void LoadFile()
{
//use app settings for file directory
}
}

How to write custom methods when using generic repository

I am still struggling to make good data access layer for asp mvc application and as always something is missing :)
I have created separate assembly for DAL and I am using repository pattern and ninject for IOC.
Problem is that now I don't know how to write custom methods (methods out of generic CRUD methods).
This is implementation:
Context class:
public class MainContext : IdentityDbContext<ApplicationUser, ApplicationRole, int, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>, IMainContext
{
public MainContext()
: base("DefaultConnection")
{
}
...
public DbSet<Country> Countries { get; set; }
...
public new IDbSet<TEntity> Set<TEntity>() where TEntity : class
{
return base.Set<TEntity>();
}
Repository:
public interface ICountryRepository : IGenericRepository<Country>...
Generic Repository:
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
private readonly IMainContext _context;
private readonly IDbSet<TEntity> _dbSet;
public GenericRepository(IMainContext context)
{
this._context = context;
this._dbSet = context.Set<TEntity>();
}
...
Generic Repository Interface:
public interface IGenericRepository<TEntity> where TEntity : class
{
IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "");
TEntity GetByID(object id);
void Insert(TEntity entity);
void Delete(object id);
void Delete(TEntity entityToDelete);
void Update(TEntity entityToUpdate);
}
And this is try to add custom method in repository class:
public virtual IEnumerable<Country> GetByLocation(float location)
{
var data = from c in _context...
return data;
}
But I don't have a context.
I don't know how to implement getting data now.
Should I inject it somehow or make instance by new keyword (but I guess this is wrong)
How to implement custom method now?
Your implementation of ICountryRepository should inherit the GenericRepository. The GenericRepository has a reference to the db context which you can use for your custom queries. For your Generic Repository constructor is will be much easier to take MainContext instead of an IMainContext which is ok if you keep injecting it down through you layers. With Ninject you will want to bind your MainContext like this:
kernel.Bind<MainContext>().ToSelf().InRequestScope();
and the rest of your interfaces to the concrete implementation. So each of your repositories will have the context through the GenericRepository which has a reference to your db context which you can make custom queries off of in the repository. If you had a service layer, inject the interface of the repository:
private readonly ICountryRepository _repository;
public SomeServie(ICountryRepository repository){
_repository = repository;
}
public void DoSomething(float locationId){
_repository.GetByLocation(locationId);
}
HERE IS THE OTHER CODE YOU NEED:
public class CountryRepository : GenericRepository<Country>, ICountryRepository
{
public CountryRepository(MainContext mainContext) : base(mainContext) { }
public IEnumerable<Country> GetByLocation(float location)
{
return this.Context.Countries.ToList();
}
.....
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
public MainContext Context { get; set; }
public IDbSet<TEntity> DbSet { get; set; }
public GenericRepository(MainContext context)
{
Context = context;
DbSet = context.Set<TEntity>();
}
.......
Also, your GetCountries does not need to be virtual.
The main changes here are that you need to pass the context through the country repository constructor to base, and the context and countries db set need to be public, even when inherited. See this article: Are private members inherited in C#?

How to write generic Entity name in constructor in EF4?

i try to write Generic Repository in EF 4. But my codes is not looking good. Because ExpressionEntities is not GENERIC.
I want to convert this codes:
public class DataRepository<T> : IRepository<T> where T : class
{
private ExpressionsEntities _context;
public DataRepository()
{
}
public DataRepository(ExpressionsEntities context)
{
_context = context;
}
}
to the following:
public class DataRepository<T> : IRepository<T> where T : class
{
private GetGenericEntityCONTEXT _context;
public DataRepository()
{
}
public DataRepository(GetGenericEntityCONTEXT context)
{
_context = context;
}
}
because ExpressionsEntities not global my entities : ExpressionsEntities1, ExpressionsEntities 2, ExpressionsEntities 3 etx... i need to write get take entity for example:
public class DataRepository<T> : IRepository<T> where T : class
{
private Entity _context;
public DataRepository()
{
}
public DataRepository(Entity context)
{
_context = context;
}
public class Main
{
main()
{
new DataRepository(ExpressionEntities)
}
}
}
Not 100% sure what your asking - from what i can understand, you wan't a way to dynamically create the entity set based on T.
Well that's easy enough:
public class DataRepository<T> : IRepository<T> where T : class
{
private ObjectContext _ctx;
public DataRepository<T>(ObjectContext ctx)
{
this._ctx = ctx;
}
public IObjectSet<T> CurrentEntitySet<T>()
{
get
{
var entityName = _plularizer.Pluralize(typeof(T).Name);
string entitySetName = string.Format("{0}.{1}", EntityContainerName, entityName);
return _ctx.CreateObjectSet<T>(entitySetName );
}
}
}
Then your specific Repository could do this (for example):
public class AppleRepository : DataRepository<Apple>
{
public AppleRepository(IObjectContext ctx) : base(ctx) {}
public ICollection<Apple> FindApples(Func<Apple,bool> predicate)
{
return CurrentEntitySet.Where(predicate).ToList();
}
}
And when your creating your repository, pass through the object context - preferably by DI:
var repository = new AppleRepository(new ExpressionEntities()); // should be DI'ed
Basically, we're making use of Pluralization (the same code used by Entity Framework to pluralize entity set names), and CreateObjectSet<T>.
So if you created a DataRepository<Apple>, that would translate to an entity set name of Apples (which should match the entity set on your model), and we create an entity set based on that.
The EntityContainerName property is what's on your EDMX - you should pass this through the ctor (via DI preferably).
Does that answer your question?
I prefer to get the entityName like this.
EntityContainer container = context.MetadataWorkspace.GetEntityContainer(context.DefaultContainerName, DataSpace.CSpace);
EntitySetBase entitySet = container.BaseEntitySets.Where(item => item.ElementType.Name.Equals(typeof(T).Name)).FirstOrDefault();
var entityName = entitySet.Name
This deals with some cases if you have some classes pluralized, and some classes not.

Categories