I am trying to add Windsor Castle 3.3.0 IoC with my MVC 5 project dbcontext (EF 6) and have had a look at many articles, but I can't figure out how to do it and keep running into the problem of getting the following error:
The operation cannot be completed because the DbContext has been disposed.
This error only seems to occur once the first page has loaded and then I try to load subsequent pages
This is my code:
Context Interface
public interface IMyContext
{
IDbSet<DBProduct> Products { get; set; }
}
Context
public class MyContext : DbContext, IMyContext
{
public MyContext ()
: base("Name=MyDB")
{
Database.SetInitializer<MyContext>(null);
this.Configuration.ProxyCreationEnabled = false;
}
public IDbSet<DBProduct> Products { get; set; }
}
Usage
public sealed class CatalogueDAL : ICatalogueDAL
{
private IMyContext _dbContext;
public CatalogueDAL(IMyContext dbContext)
{
_dbContext= dbContext;
}
public ICollection<DBProduct> GetAllProducts()
{
return _dbContext.Products
.OrderBy(x => x.Order)
.ThenBy(x => x.Name)
.ToList();
}
}
public class CatalogueHelper : ICatalogueHelper
{
private ICategoryDAL _categoryDAL;
private IMapper _mapper;
public CatalogueHelper(ICatalogueDAL catalogueDAL)
{
_catalogueDAL = catalogueDAL;
CreateMapper();
}
public ICollection<Product> GetAllProducts()
{
return _mapper.Map<ICollection<DBProduct>, ICollection<Product>>(_catalogueDAL.GetAllProducts());
}
private void CreateMapper()
{
MapperConfiguration config = new MapperConfiguration(cfg =>
{
cfg.CreateProductMap();
});
_mapper = config.CreateMapper();
}
}
public class LandingPageController : Controller
{
private ICatalogueHelper _helper;
public LandingPageController(ICatalogueHelper helper)
{
_helper = helper;
}
public ActionResult AllProducts()
{
return View(_helper.GetAllProducts());
}
}
Castle Initialiser
container.Register(Component.For<IMyContext>().ImplementedBy<MyContext>().LifeStyle.PerWebRequest);
container.Register(Component.For<ICatalogueDAL>().ImplementedBy<CatalogueDAL>().LifeStyle.PerWebRequest);
container.Register(Component.For<ICatalogueHelper>().ImplementedBy<CatalogueHelper>().LifeStyle.PerWebRequest);
If I remove the IoC and put a using(var context = new MyContext()) in the GetAllProducts, it will work fine but that seems to against the point?
How do I change this to use IoC on the dbContext.
I have tried just using the MyContext (instead of the interface) and registering that with
container.Register(Component.For<MyContext>().ImplementedBy<MyContext>().LifeStyle.PerWebRequest);
And this seems to work better, but when I load two pages at the same time, the one that starts second will always get the following error message:
There is already an open DataReader associated with this Command which must be closed first
Having searched both these errors, I cannot figure out how to apply it to my solution. Any help would be greatly appreciated
Related
We are developing windows service, and i want to change dbcontext class dynamically in repositories.
bellow is the scenario.
I have three db context classes
public abstract class Context : DbContext, IUnitOfWork
{
protected Context(string connectionString) : base(connectionString)
{
}
}
public class PlatformContext : Context
{
private readonly string _connectionString;
public PlatformContext(string connectionString)
: base(connectionString)
{
_connectionString = connectionString;
}
}
public class PlatformReplicaContext : Context
{
private readonly string _connectionString;
public PlatformReplicaContext(string connectionString)
: base(connectionString)
{
_connectionString = connectionString;
}
}
public class TempContext : Context
{
private readonly string _connectionString;
public TempContext(string connectionString)
: base(connectionString)
{
_connectionString = connectionString;
}
}
and i have repository
public interface ICategoryRepository : IRepository<Category>
{
}
public class CategoryRepository :Repository<Category>, ICategoryRepository
{
public CategoryRepository(Context context) : base(context)
{
}
}
hence im using CQRS i have another three classes
public class CategoryBasicQuery:IRequest<BaseQueryResponse>
{
public int CategoryId { get; set; }
}
public class CategoryBasicQueryHandler : IRequestHandler<CategoryBasicQuery, BaseQueryResponse>
{
private readonly ICategoryRepository _categoryRepository;
private readonly IMapper _mapper;
public CategoryBasicQueryHandler(ICategoryRepository categoryRepository, IMapper mapper)
{
_categoryRepository = categoryRepository;
_mapper = mapper;
}
public async Task<BaseQueryResponse> Handle(CategoryBasicQuery request, CancellationToken cancellationToken)
{
var entry = await _categoryRepository.FindAsync(request.CategoryId);
if (entry == null)
{
return new NotFoundResponse();
}
var response = _mapper.Map<CategoryBasicResponse>(entry);
return response;
}
}
Now here is the issue
Here category repository should be able to execute queries in all 3 types of contexts.
but how should i register classes in using autofac?
then i came up with a solution generating repositories in run time as below
public class RepositoryFactory
{
public static TRepository GetRepositoryInstance<T, TRepository>(
params object[] args)
where TRepository : IRepository<T>
{
return (TRepository)Activator.CreateInstance(typeof(TRepository), args);
}
}
im calling this method inside CategoryBasicQueryHandler class like this
var categoryRepo = RepositoryFactory.GetRepositoryInstance<Category, CategoryRepository>(new PlatformReplicaContext("connectionString"));
but when calling from CQRS
var categoty = new Category();
var command = new CategoryBasicQuery {CategoryId = categoryId};
var result = _mediator.Send(command);
VS give me following error
and my autofac registration as follows
builder.RegisterType<CategoryService>().AsSelf();
builder.RegisterType<ActionRepository>().As<IActionRepository>();
builder.RegisterType<CategoryRepository>().As<ICategoryRepository>();
builder.RegisterType<Mapper>().As<IMapper>();
can anyone help me resolve this or suggest good method to handle this situation.
thanks.
This may give you a good starting point for a possible solution: http://autofaccn.readthedocs.io/en/latest/resolve/relationships.html#keyed-service-lookup-iindex-x-b
builder.RegisterType<PlatformContext>().Keyed<Context>("platform");
builder.RegisterType<PlatformReplicaContext>().Keyed<Context>("replica");
builder.RegisterType<TempContext>().Keyed<Context>("temp");
You mentioned in a comment that there is a variable named action somewhere that will indicate which implementation to use:
public class Class1
{
private readonly IIndex<string, Context> contexts;
public Class1(IIndex<string, Context> contexts)
{
this.contexts = contexts;
}
public void Whatever()
{
string action = ...; // platform, replica or temp
Context context = this.contexts[action];
...
}
}
Of course this needs to be adapted so that it will fit in the rest of your application design. A possible example could be:
Context context = this.contexts[action];
using(ILifetimeScope scope = container.BeginLifetimeScope(builder =>
{
builder.RegisterInstance(context).As<Context>();
}))
{
// Because we are resolving IMediator from the scope, the selected Context will be used in all dependencies
var mediator = scope.Resolve<IMediator>();
mediator.Send(...);
}
I have an ASP.Net Core 2 Web application.
I'm trying to create a custom routing Middleware, so I can get the routes from a database.
In ConfigureServices() I have:
services.AddDbContext<DbContext>(options =>
options.UseMySQL(configuration.GetConnectionString("ConnectionClient")));
services.AddScoped<IServiceConfig, ServiceConfig>();
In Configure():
app.UseMvc(routes =>
{
routes.Routes.Add(new RouteCustom(routes.DefaultHandler);
routes.MapRoute(name: "default", template: "{controller=Home}/{action=Index}/{id?}");
});
In the RouteCustom
public class RouteCustom : IRouteCustom
{
private readonly IRouter _innerRouter;
private IServiceConfig _serviceConfig;
public RouteCustom(IRouter innerRouter)
{
_innerRouter = innerRouter ?? throw new ArgumentNullException(nameof(innerRouter));
}
public async Task RouteAsync(RouteContext context)
{
_serviceConfig = context.HttpContext
.RequestServices.GetRequiredService<IServiceConfig>();
/// ...
// Operations inside _serviceConfig to get the route
}
public VirtualPathData GetVirtualPath(VirtualPathContext context)
{
_serviceConfig = context.HttpContext
.RequestServices.GetRequiredService<IServiceConfig>();
// ...
// Operations inside _serviceConfig to get the route
}
}
The IServiceConfig it is just a class where I access the database to get data, in this case the routes, but also other configuration data I need for the application.
public interface IServiceConfig
{
Config GetConfig();
List<RouteWeb> SelRoutesWeb();
}
public class ServiceConfig : IServiceConfig
{
private readonly IMemoryCache _memoryCache;
private readonly IUnitOfWork _unitOfWork;
private readonly IServiceTenant _serviceTenant;
public ServiceConfig(IMemoryCache memoryCache,
IUnitOfWork unitOfWork,
IServiceTenant serviceTenant)
{
_memoryCache = memoryCache;
_unitOfWork = unitOfWork;
_serviceTenant = serviceTenant;
}
public Config GetConfig()
{
var cacheConfigTenant = Names.CacheConfig + _serviceTenant.GetId();
var config = _memoryCache.Get<Config>(cacheConfigTenant);
if (config != null)
return config;
config = _unitOfWork.Config.Get();
_memoryCache.Set(cacheConfigTenant, config,
new MemoryCacheEntryOptions()
{
SlidingExpiration = Names.CacheExpiration
});
return config;
}
public List<RouteWeb> SelRoutesWeb()
{
var cacheRoutesWebTenant = Names.CacheRoutesWeb + _serviceTenant.GetId();
var routesWebList = _memoryCache.Get<List<RouteWeb>>(cacheRoutesWebTenant);
if (routesWebList != null)
return routesWebList;
routesWebList = _unitOfWork.PageWeb.SelRoutesWeb();
_memoryCache.Set(cacheRoutesWebTenant, routesWebList,
new MemoryCacheEntryOptions()
{
SlidingExpiration = Names.CacheExpiration
});
return routesWebList;
}
}
The problem is I'm getting this message when I test with multiple tabs opened and try to refresh all at the same time:
"A second operation started on this context before a previous operation completed"
I'm sure there is something I'm doing wrong, but I don't know what. It has to be a better way to access the db inside the custom route middleware or even a better way for doing this.
For example, on a regular Middleware (not the routing one) I can inject the dependencies to the Invoke function, but I can't inject dependencies here to the RouteAsync or the GetVirtualPath().
What can be happening here?
Thanks in advance.
UPDATE
These are the exceptions I'm getting.
An unhandled exception occurred while processing the request.
InvalidOperationException: A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.
And this one:
An unhandled exception occurred while processing the request.
MySqlException: There is already an open DataReader associated with this Connection which must be closed first.
This is the UnitOfWork
public interface IUnitOfWork : IDisposable
{
ICompanyRepository Company { get; }
IConfigRepository Config { get; }
// ...
void Complete();
}
public class UnitOfWork : IUnitOfWork
{
private readonly DbContext _context;
public UnitOfWork(DbContext context)
{
_context = context;
Company = new CompanyRepository(_context);
Config = new ConfigRepository(_context);
// ...
}
public ICompanyRepository Company { get; private set; }
public IConfigRepository Config { get; private set; }
// ...
public void Complete()
{
_context.SaveChanges();
}
public void Dispose()
{
_context.Dispose();
}
}
UPDATE 2
After reviewing the comments and making a lot of tests, the best clue I have is when I remove the CustomRoute line the problem disappear. Removing this line from Configure function on Startup.cs
routes.Routes.Add(new RouteCustom(routes.DefaultHandler));
Also I have tried removing, first the RouteAsync and then the GetVirtualPath() methods, but if one of those is present I get an error, so it is clear that the problem is in this CustomRoute class.
In the TenantMiddleware, which is called first for any request, I'm injecting the UnitOfWork and I have no problem. This Middleware is create in the Configure function:
app.UseMiddleware<TenantMiddleware>();
And inside, I'm injecting the UnitOfWork, and using it on every request, like this:
public async Task Invoke(HttpContext httpContext, IServiceTenant serviceTenant)
{
// ...performing DB operations to retrieve the tenent's data.
}
public class ServiceTenant : IServiceTenant
{
public ServiceTenant(IHttpContextAccessor contextAccessor,
IMemoryCache memoryCache,
IUnitOfWorkMaster unitOfWorkMaster)
{
_unitOfWorkMaster = unitOfWorkMaster;
}
// ...performing DB operations
}
SO, the problem with the CustomRoute is I can't inject the dependencies by adding to the Invoke function like this:
public async Task Invoke(HttpContext httpContext, IServiceTenant serviceTenant)
So I have to call the corresponding Service (Inside that service I inject the UnitOfWork and perform the DB operations) like this, and I think this can be the thing that is causing problems:
public async Task RouteAsync(RouteContext context)
{
_serviceConfig = context.HttpContext
.RequestServices.GetRequiredService<IServiceConfig>();
// ....
}
because this is the only way I know to "inject" the IServiceConfig into the RouteAsync and GetVirtualPath()...
Also, I'm doing that in every controller since I'm using a BaseCOntroller, so I decide which os the injection services I use...
public class BaseWebController : Controller
{
private readonly IMemoryCache _memoryCache;
private readonly IUnitOfWork _unitOfWork;
private readonly IUnitOfWorkMaster _unitOfWorkMaster;
private readonly IServiceConfig _serviceConfig;
private readonly IServiceFiles _serviceFiles;
private readonly IServiceFilesData _serviceFilesData;
private readonly IServiceTenant _serviceTenant;
public BaseWebController(IServiceProvider serviceProvider)
{
_memoryCache = serviceProvider.GetRequiredService<IMemoryCache>();
_unitOfWork = serviceProvider.GetRequiredService<IUnitOfWork>();
_unitOfWorkMaster = serviceProvider.GetRequiredService<IUnitOfWorkMaster>();
_serviceConfig = serviceProvider.GetRequiredService<IServiceConfig>();
_serviceFiles = serviceProvider.GetRequiredService<IServiceFiles>();
_serviceFilesData = serviceProvider.GetRequiredService<IServiceFilesData>();
_serviceTenant = serviceProvider.GetRequiredService<IServiceTenant>();
}
}
And then in every controller, instead of referencing all of the injected services, I can do it only for those I need, like this:
public class HomeController : BaseWebController
{
private readonly IUnitOfWork _unitOfWork;
public HomeController(IServiceProvider serviceProvider) : base(serviceProvider)
{
_unitOfWork = serviceProvider.GetRequiredService<IUnitOfWork>();
}
public IActionResult Index()
{
// ...
}
}
I don't know if this has something to do with my problem, but I'm just showing you what I think can be the problem, so you can have more information.
Thanks.
UPDATE 3
This is the code of the db to retrieve the routes:
public class PageWebRepository : Repository<PageWeb>, IPageWebRepository
{
public PageWebRepository(DbContext context) : base(context) { }
public List<RouteWeb> SelRoutesWeb()
{
return Context.PagesWebTrs
.Include(p => p.PageWeb)
.Where(p => p.PageWeb.Active)
.Select(p => new RouteWeb
{
PageWebId = p.PageWebId,
LanguageCode = p.LanguageCode,
Route = p.Route,
Regex = p.PageWeb.Regex.Replace("<route>", p.Route),
Params = p.PageWeb.Params,
Area = p.PageWeb.Area,
Controller = p.PageWeb.Controller,
Action = p.PageWeb.Action,
Type = p.PageWeb.Type,
Sidebar = p.PageWeb.Sidebar,
BannerIsScript = p.PageWeb.BannerIsScript,
Title = p.Title,
Description = p.Description,
Keywords = p.Keywords,
ScriptHead = p.ScriptHead,
ScriptBody = p.ScriptBody,
BannerScript = p.BannerScript,
BannerUrl = p.BannerUrl,
})
.ToList();
}
}
Where PagesWebTrs are the translations of the pages (multi language) and PagesWeb is the main table.
This issue is indeed within the route middleware.
Per definition, a middleware is a singleton, so a single instance handles all requests. This results into the instance state (the IServiceConfigwith hooked up DbContext) being accessed and changed by multiple simultaneous requests; it's a well disguished classical concurrency issue.
An example.
Request A executes RouteAsync, sets the _serviceConfig and executes a query on the DbContext. Nano seconds (or less :)) later, request B does the same. While request B's query is being executed, request A executes GetVirtualPath, but this time on the DbContext set by request B. This results in a second query being executed on the DbContext of request B which still has one running and you get the mentionned error.
The solution is to prevent shared state, by retrieving the IServiceConfig at the start of each method.
As you already said, getting such a dependency injected via the Invoke method does not work; the Invokemethod does not get executed.
Here below is the reworked RouteCustom.
public class RouteCustom : IRouteCustom
{
private readonly IRouter _innerRouter;
public RouteCustom(IRouter innerRouter)
{
_innerRouter = innerRouter ?? throw new ArgumentNullException(nameof(innerRouter));
}
public async Task RouteAsync(RouteContext context)
{
var serviceConfig = context.HttpContext.RequestServices.GetRequiredService<IServiceConfig>();
// ...
}
public VirtualPathData GetVirtualPath(VirtualPathContext context)
{
var serviceConfig = context.HttpContext.RequestServices.GetRequiredService<IServiceConfig>();
// ...
}
}
As an answer to my own question:
What would be the most elegant way to use Entity Framework with Generic Repository, in Service Stack, and to write Integration \ Unit Tests for service?
At the moment, this is how my structure looks like:
Generic repository layer:
public class GenericRepository<TEntity> where TEntity : class
{
internal DbContext Context;
//...
//CRUD Operations, etc.
}
Unit of work layer:
public class UnitOfWork : IDisposable
{
private readonly DbContext _context;
public UnitOfWork(DbContext ctx)
{
_context = ctx;
}
private bool _disposed;
private GenericRepository<User> _userRepository;
public GenericRepository<User> UserRepository
{
get { return _userRepository ?? (_userRepository = new GenericRepository<User>(_context)); }
}
//...
}
Business layer:
public class UserBusiness
{
public UnitOfWork UoW { get; set; }
public void AddUser(Models.User user)
{
//Map from domain model to entity model
var u = Mapper.Map<Models.User, DAL.Repository.User>(user);
UoW.UserRepository.Insert(u);
UoW.Save();
}
}
API project:
public class AppHost : AppHostBase
{
public AppHost() : base("Users Service", typeof(UsersService).Assembly) { }
//...
public override void Configure(Funq.Container container)
{
//...other configuration
//UoW registration
container.Register(c => new UnitOfWork(new DbContext("my-DB-connection"))).ReusedWithin(Funq.ReuseScope.Hierarchy);
//Business layer class registration
container.Register<UserBusiness>(c=>new UserBusiness {
UoW = c.Resolve<UnitOfWork>()
}).ReuseWithin(Funq.ReuseScope.Hierarchy);
}
}
public class UsersService : Service
{
public UserBusiness UB { get; set; }
public object Post(User u)
{
UB.AddUser(u);
//...
}
}
So when it comes to integration testing, I can just do something like this:
Declare _appHost
private ServiceStackHost _appHost;
And configure funq container like this:
public override void Configure(Funq.Container container)
{
//...create mocked context
var mockedContext = new Mock<IDbContext>();
mockedContext.Setup(x => x.Set<User>()).Returns(new List<User>
{
new User { ID = 1, FirstName = "John", LastName = "Doe" }
});
//(or use effort or any other way to create it)
container.Register(c => new UnitOfWork(mockedContext)).ReusedWithin(Funq.ReuseScope.Hierarchy);
}
And test as usual:
[Test]
public void Get_User_By_Id()
{
//...generate client instance (JsonServiceClient) etc.
var customer = client.Get(new GetCustomer { Id = 1 });
Assert.AreEqual("John", customer.FirstName);
///...
}
In addition to have all layers available for DI, and mocking, I also created IDbContext, IGenericRepository, IUnitOfWork, etc. interfaces.
I didn't include it here in order to keep this as simple as I could.
But I would like to hear if there's a better (more elegant way) to do it.
I am starting to use Ninject in my MVC5 code-first app. Here's my NinjectWebCommon.cs:
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
try
{
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
kernel.Bind<CMSContext>()
.ToSelf()
//.InSingletonScope();
.InRequestScope();
kernel.Bind<IExecutiveRepository>()
.To<ExecutiveRepository>();
kernel.Bind<IExecutiveSectionRepository>()
.To<ExecutiveSectionRepository>();
kernel.Bind<IExecutiveSectionMappingRepository>()
.To<ExecutiveSectionMappingRepository>();
kernel.Bind<IUserRepository>()
.To<UserRepository>();
kernel.Bind<IContentRepository>()
.To<ContentRepository>();
RegisterServices(kernel);
return kernel;
}
catch
{
kernel.Dispose();
throw;
}
}
I tried .InSingletonScope() as well as .InRequestScope() but I still get the 'entity object cannot be referenced by multiple instances of IEntityChangeTracker' error.
Here is my Interface:
public interface IExecutiveRepository : IDisposable
{
IEnumerable<Executive> GetExecutives();
Executive GetExecutiveById(int executiveId);
void InsertExecutive(Executive executive);
void UpdateExecutive(Executive executive);
void DeleteExecutive(int executiveId);
void Save();
}
Here is my concrete:
public class ExecutiveRepository : IExecutiveRepository, IDisposable
{
private CMSContext context;
public ExecutiveRepository(CMSContext context)
{
this.context = context;
}
public IEnumerable<Executive> GetExecutives()
{
return context.Executives.ToList();
}
public Executive GetExecutiveById(int id)
{
return context.Executives.Find(id);
}
public void InsertExecutive(Executive executive)
{
context.Executives.Add(executive);
}
public void DeleteExecutive(int executiveId)
{
Executive executive = context.Executives.Find(executiveId);
context.Executives.Remove(executive);
}
public void UpdateExecutive(Executive executive)
{
context.Entry(executive).State = EntityState.Modified;
}
public void Save()
{
context.SaveChanges();
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
context.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
Here is the controller(top pertinent part):
public class ExecutiveController : Controller
{
private IExecutiveRepository executiveRepository;
private IUserRepository userRepository;
private IExecutiveSectionRepository executiveSectionRepository;
private IExecutiveSectionMappingRepository executiveSectionMappingRepository;
private IContentRepository contentRepository;
private Ninject.IKernel _kernel = new StandardKernel();
//[Inject]
public ExecutiveController()
{
executiveRepository = _kernel.Get<ExecutiveRepository>();
userRepository = _kernel.Get<UserRepository>();
executiveSectionRepository = _kernel.Get<ExecutiveSectionRepository>();
executiveSectionMappingRepository = _kernel.Get<ExecutiveSectionMappingRepository>();
contentRepository = _kernel.Get<ContentRepository>();
}
...
Not sure what I am doing wrong but upon adding a new 'Executive' it bombs... I do understand it's trying to use separate contexts and that's the problem, but I 'm just not sure how to fix it. Apparently, the line in the NinjectWebCommon.cs class:
kernel.Bind<CMSContext>()
.ToSelf()
//.InSingletonScope();
.InRequestScope();
Is supposed to be the fix, but it isn't...
any ideas/suggestions?
You should be using NUGET package Ninject.Web.Mvc if you aren't already. This configures your application ready to use Ninject, other than your bindings. It looks like you are reasonably familiar with the bindings side of things already from what I can see in your CreateKernel() method.
Once your bindings are in place, you should not be creating Kernels in your controllers, this is because the Ninject.Web.Mvc library configures Ninject to create your controllers for you under the hood. Therefore any dependencies that you add to them should be automatically resolved.
So, you can use constructor injection to resolve your dependencies:
public class ExecutiveController : Controller
{
private IExecutiveRepository ExecutiveRepository;
private IUserRepository UserRepository;
private IExecutiveSectionRepository ExecutiveSectionRepository;
private IExecutiveSectionMappingRepository ExecutiveSectionMappingRepository;
private IContentRepository ContentRepository;
public ExecutiveController(
IExecutiveRepository executiveRepository,
IUserRepository userRepository,
IExecutiveSectionRepository executiveSectionRepository,
IExecutiveSectionMappingRepository executiveSectionMappingRepository,
IContentRepository contentRepository)
{
// Set the field values
this.ExecutiveRepository = executiveRepository,
this.UserRepository = userRepository,
this.ExecutiveSectionRepository = executiveSectionRepository,
this.ExecutiveSectionMappingRepository = executiveSectionMappingRepository,
this.ContentRepository = contentRepository;
}
public ActionResult Index(int id)
{
// Use one of your dependencies...
var executive = this.executiveRepository.GetExecutiveById(id);
}
}
Or you can use the [Inject] attribute which has the same effect:
public class ExecutiveController : Controller
{
[Inject]
public IExecutiveRepository executiveRepository { get; set; }
[Inject]
public IUserRepository userRepository { get; set; }
[Inject]
public IExecutiveSectionRepository executiveSectionRepository { get; set; }
[Inject]
public IExecutiveSectionMappingRepository executiveSectionMappingRepository { get; set; }
[Inject]
public IContentRepository contentRepository { get; set; }
public ExecutiveController()
{
}
public ActionResult Index(int id)
{
// Use one of your dependencies...
var executive = this.executiveRepository.GetExecutiveById(id);
}
}
You're creating a kernel per controller.
InRequestScope only ensures one instance per request per kernel.
So you need to adapt your setup of the kernel so there's only one kernel per web application. See:
Ninject.Web.Mvc
Tutorial
Youtube
This may not answer the question. But I tend to use the IDbContextFactory that EF provides you with and do something like this:
public interface IDefaultContextFactory : IDbContextFactory<CMSContext> {}
public class DefaultContextFactory : IDefaultContextFactory
{
private readonly Lazy<CMSContext> lazyContext = new Lazy<CMSContext>(() => new CMSContext());
public CMSContext Create()
{
return lazyContext.Value;
}
}
Then you just bind that, and when you need the context you can do something like this:
public class ExecutiveRepository : IExecutiveRepository, IDisposable
{
private readonly CMSContext context;
public ExecutiveRepository(IDefaultContextFactory contextFactory)
{
this.context = contextFactory.Create();
}
}
I believe #BatteryBackupUnit is correct, I would also consider using the above pattern for contexts.
I am new in mocking. I want to mock up my base repository which is depend on Entity Framework 6 DbContext But I fail. I searched in Google a lot but did not get any sufficient result. At last I got an example at testing with async queries and try to follow but it is worked for me.
Here is my code :
DbContext :
public class TimeSketchContext : DbContext
{
public virtual DbSet<EmployeeSkill> EmployeeSkill { get; set; }
}
Base Repository :
public class BaseRepository<T> : IRepositoryBase<T> where T : class, IEntity, new()
{
protected readonly DbContext InnerDbContext;
protected DbSet<T> InnerDbSet;
public BaseRepository(DbContext innerDbContext)
{
InnerDbContext = innerDbContext;
InnerDbSet = InnerDbContext.Set<T>();
}
public virtual Task<T> FindAsync(long id)
{
return InnerDbSet.FirstOrDefaultAsync(x=>x.Id == id);
}
}
Test :
[Fact]
public async Task DbTest()
{
var dummyData = GetEmployeeSkills();
var mockSet = new Mock<DbSet<EmployeeSkill>>();
mockSet.As<IDbAsyncEnumerable<EmployeeSkill>>()
.Setup(x => x.GetAsyncEnumerator())
.Returns(new TestDbAsyncEnumerator<EmployeeSkill>(dummyData.GetEnumerator()));
mockSet.As<IQueryable<EmployeeSkill>>()
.Setup(x => x.Provider)
.Returns(new TestDbAsyncQueryProvider<EmployeeSkill>(dummyData.Provider));
mockSet.As<IQueryable<EmployeeSkill>>().Setup(m => m.Expression).Returns(dummyData.Expression);
mockSet.As<IQueryable<EmployeeSkill>>().Setup(m => m.ElementType).Returns(dummyData.ElementType);
mockSet.As<IQueryable<EmployeeSkill>>().Setup(m => m.GetEnumerator()).Returns(dummyData.GetEnumerator());
var mockContext = new Mock<TimeSketchContext>();
mockContext.Setup(c => c.EmployeeSkill).Returns(mockSet.Object);
var baseRepository = new BaseRepository<EmployeeSkill>(mockContext.Object);
var data = await baseRepository.FindAsync(1);
Assert.NotEqual(null, data);
}
private EmployeeSkill GetEmployeeSkill()
{
return new EmployeeSkill
{
SkillDescription = "SkillDescription",
SkillName = "SkillName",
Id = 1
};
}
private IQueryable<EmployeeSkill> GetEmployeeSkills()
{
return new List<EmployeeSkill>
{
GetEmployeeSkill(),
GetEmployeeSkill(),
GetEmployeeSkill(),
}.AsQueryable();
}
Result is :
Assert.NotEqual() Failure
I think problem is
public BaseRepository(DbContext innerDbContext)
{
InnerDbContext = innerDbContext;
InnerDbSet = InnerDbContext.Set<T>(); <<<<<<<<<<<
}
But don`t understand why and how to solve this.
I am using :
Visual Studio 2013 Ultimate
Moq
xUnit
Thank`s in advance.
You are right the problem is in your InnerDbContext.Set<T>(); statement.
In the current version of the EF (6.0.2) the DbContext.Set<T> method is not virtual so it cannot be mocked with Moq.
So you cannot easily make your test pass except by changing your design of the BaseRepository to not depend on the whole DbContext but on one DbSet<T>:
So something like:
public BaseRepository(DbSet<T> dbSet)
{
InnerDbSet = dbSet;
}
Then you can pass directly in your mocked DbSet.
Or you can create a wrapper interface for DbContext:
public interface IDbContext
{
DbSet<T> Set<T>() where T : class;
}
public class TimeSketchContext : DbContext, IDbContext
{
public virtual DbSet<EmployeeSkill> EmployeeSkill { get; set; }
}
Then use IDbContext in your BaseRepository:
public class BaseRepository<T> : IRepositoryBase<T> where T : class, IEntity, new()
{
protected readonly IDbContext InnerDbContext;
protected DbSet<T> InnerDbSet;
public BaseRepository(IDbContext innerDbContext)
{
InnerDbContext = innerDbContext;
InnerDbSet = InnerDbContext.Set<T>();
}
public virtual Task<T> FindAsync(long id)
{
return InnerDbSet.FirstOrDefaultAsync(x => x.Id == id);
}
}
And finally you just need to change two lines in your test to make it pass:
var mockContext = new Mock<IDbContext>();
mockContext.Setup(c => c.Set<EmployeeSkill>()).Returns(mockSet.Object);