Please explain how my DbContext is being instantiate? - c#

About 3 years ago I build a Web API and created a generic repository with Ninject for DI. Today I'm reviewing my code to make some changes and I can't understand where my DbContext is being instantiated. I can't remember what I didn't, I believe this was a hybrid of a bunch of implementation I reading up on Generic Repo.
I've spent quite some time reading through documentation online but couldn't find an answer. Can someone please explain it to me so I can document it properly? Below is my sample generic repo, application repo, my controller and Ninject binding. I know in .net core you can inject it from the startup but this was web api 2 using EF6.
Generic Repo:
public GenericRepository(DbContext _context, ILogService _log)
{
this.context = _context;
entities = context.Set<T>();
}
Entity Repo:
public ApplicationRepository(TransactionDbContext context) : base(context)
{
}
Controller:
public ApplicationController(IApplicationRepository _applicationRepository)
{
this.applicationRepository = _applicationRepository
}
Ninject Binding:
kernel.Bind<IApplicationRepository>().To<ApplicationRepository>();

By default, Ninject allows you to auto-resolve concrete types.
For more information, you can read the Ninject wiki: https://github.com/ninject/ninject/wiki/Dependency-Injection-With-Ninject

Related

Initializing DBContext in Asp.Net Core

I want to use Repository & Unit Of Work in my project.
But in ASP.NET MVC when we want use DBContext to use this code
MyDbContext db=new MyDbContext();
but in ASP.NET Core when write this code it want an argument
because use this code in DbContext Class
public MyDbContext(DbContextOptions<MyDbContext> options) : base(options) { }
Error:
what is the problem?
You can initilize your DB context like this:
var optionBuilder = new DbContextOptionsBuilder<MyDbContext>();
optionBuilder.UseSqlServer("Server=localhost;...");
var context = new MyDbContext(optionBuilder.Options);
Previous code is configuring the options to the connection and then creating a MyDbContext using those options.
If you want to use a InMemoryDatabase for unit testing for example you can change it to this:
var optionBuilder = new DbContextOptionsBuilder<MyDbContext>().UseInMemoryDatabase("testindDB")`;
public MyDbContext(DbContextOptions<MyDbContext> options)
You have not empty constructor in your MyDbContext class, So you should do pass parameter DbContextOptions<MyDbContext> options in constructor.
For example you can see it -> link1
You shouldn't be instantiating the DbContext, you should be requesting it as a constructor argument to your repository. Then your IOC container will provide the DbContext at runtime. This ensures that you can use the same DbContext throughout a given ASP.NET web request, which will prevent a host of problems you're otherwise likely to encounter.
You can see an example of a generic repository here:
http://deviq.com/repository-pattern/
You also typically don't need a separate Unit of Work in ASP.NET applications (but sometimes you do). This is because your requests should be very small and you should be able to do most of the work in a single controller or service, and then simply save through the repository. It's not that you never need UoW, but it's less necessary than in a thick client scenario (e.g. windows app or service).
You can try it: In your class UnitOfWork
private MyDBContext _context;
public UnitOfWork(MyDBContext context)
{
_context = context;
}
In your controller:
private UnitOfWork _unitOfWork;
public MoviesController(MyDBContext context)
{
_unitOfWork = new UnitOfWork(context);
}

Factory Pattern with Identity 2.0 and Entity Framework

We are developing a Full Stack Authorised WebApi with Entity Framework and Identity 2.0.
Its based on a git repo here
WebApi Full Stack Entity Framework Repository
In our service layer we pass across the a custom interface from our context i.e.
private readonly Func<IGPFocusDataContext> _contextFactory;
public PatientService(Func<IGPFocusDataContext> contextFactory)
{
this._contextFactory = contextFactory;
}
I've created a similar UserService, its at this point I want to inject the UserManager and RoleManager interface in a similar way.
Can anyone recommend the best way of doing this?
Looking at the repo you can add the instances/implementations of UserManger and RoleManager to the Unity container in UnityConfig.cs something like:
container.RegisterType<UserManager, UserManager>();
Or
container.RegisterType<IUserManager, UserManager>();
And then in your UserService
private readonly IUserManger _userManger;
private readonly IRoleManger _roleManger;
public UserService(IUserManger userManger, IRoleManager roleManger)
{
this._userManager = userManger;
this._roleManger = roleManger;
}
When you add the service in your controller similar to the PatientService in PatientsController, although I am not familiair with Unity, I expect Unity ties it all together.

ASP.NET Core Dependency Injection, inject with parameters

In an ASP.NET Core 1.0 project, using DI how can I pass parameters to constructor. For instance, how do I register the following service in Startup.cs. services.AddTransient(typeof(IStateService), new StateService()); does not work since StateService() requires an input parameter of type BlogingContext. Or, are there alternative way of building the following service with database involved? Here State is a table coming from SQL Server Db. App is using EntityFrameworkCore with Code First approach. I'm using latest release of ASP.NET Core 1.0 and VS2015-Update 3 released on June 27, 2016
I see a similar example here but not quite the same type of input parameter.
Service:
public interface IStateService
{
IEnumerable<State> List();
}
public class StateService : IStateService
{
private BloggingContext _context;
public StateService(BloggingContext context)
{
_context = context;
}
public IEnumerable<State> List()
{
return _context.States.ToList();
}
}
As documentation states here (Scroll a bit down) you should register the IStateService and BloggingContext like:
services.AddDbContext<BloggingContext>();
services.AddScoped<IStateService, StateService>();
Then DI will resolve the whole dependency tree for you. Note that you should use scoped lifetime on service, because the service should use same lifetime as DbContext and it uses scoped.

Issues Configuring StructureMap.MVC5 to work with Identity

I am currently trying to reconfigure StructureMap in our application after upgrading from an old version (2.6) that was never correctly implemented in the first place.
I am new to using DI Containers to begin with, and am finding documentation for newer StructureMap versions hard to find. I uninstalled the old 2.6 version of StructureMap and installed StructureMap.MVC5 (since I am using MVC5).
What I am having issues with is the AccountController. I have StructureMap set up to use the parameterless constructor, but when my application tries to create the UserManager, I get an InvalidOperationException, "No owin.Environment item was found in the context."
Obviously I need additional configuration for StructureMap, but I have no idea what/how. I can find a million sources for this error, all suggesting adding a tag in web.config, but none of them seem to be DI container specific - and I only have this issue when I use StructureMap vs letting the framework create the controller.
Below is the relevant code; that section of the AccountController is just stock template code.
AccountController.cs
private ApplicationUserManager _userManager;
public AccountController()
{
}
public AccountController(ApplicationUserManager userManager)
{
UserManager = userManager;
}
public ApplicationUserManager UserManager
{
get
{
// This is where the exception is thrown
return _userManager ??
HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
DefaultRegistry.cs
public DefaultRegistry()
{
Scan(
scan =>
{
scan.TheCallingAssembly();
scan.WithDefaultConventions();
scan.With(new ControllerConvention());
});
For<IBasicRepository>()
.Use<EntityRepository>()
.LifecycleIs<HttpContextLifecycle>()
.Ctor<string>("ConnectionString")
.Is(ConfigurationManager.ConnectionStrings["MyContext"].ConnectionString);
For<AccountController>()
.Use<AccountController>()
.SelectConstructor(() => new AccountController());
}
As #Erik Funkenbusch pointed out, I was doing competing things. I ended up making UserManager an auto-property, removed the parameterless constructor, and let StructureMap inject the ApplicationUserManager.
public ApplicationUserManager UserManager { get; private set; }
public AccountController(ApplicationUserManager userManager)
{
UserManager = userManager;
}
Then, I simply needed to configure the IUserStore and DbContext that Identity uses in DefaultRegistry.cs:
For<IUserStore<ApplicationUser, int>>()
.Use<UserStore<ApplicationUser, CustomRole, int, CustomUserLogin,
CustomUserRole, CustomUserClaim>>()
.LifecycleIs<HttpContextLifecycle>();
For<DbContext>()
.Use(() => new ApplicationDbContext())
.LifecycleIs<HttpContextLifecycle>();
This was all I needed to do to get StructureMap.MVC working with Identity.
Part of my initial hangup was that I didn't realize the way StructureMap.MVC (and other DI containers) worked. (See my related question.) I was expecting it to just work with my stock AccountController which got initialized by the framework (and thought it magically intercepted object creation to inject anything I had configured), not realizing that StructureMap must initialize the controllers itself in order for it to perform constructor injection. So when I ran into issues, I was A. Surprised that StructureMap had anything to do with my AccountController in the first place (since I wasn't explicitly configuring injection for any of its parameters - only for my Repository used in other controllers), and B. I wasn't thinking about changing my stock code but rather thinking about how to configure StructureMap. It turned out I needed to do both. Luckily, it was an easy modification and I learned a little more about how DI containers work.

How-to inject the Entity Framework DbContext into the ConfigurationBasedRepository of SharpRepository

I really would like to use SharpRepository together with Ninject, but I do not understand how to configure Ninject to share the Entity Framework DbContext between the repositories.
I am using Entity Framework version 5 and Ninject version 3.
Currently I am using Ef5Repository in my source code, but I want to replace it with ConfigurationBasedRepository. But I cannot figure out how to pass (or inject) the EF DbContext to the repositories.
Example (current state):
using SharpRepository.Repository;
public interface IProductRepository : IRepository<Product>
{
}
using SharpRepository.Ef5Repository;
using System.Data.Entity;
// TODO Tightly coupled to Ef5Repository.
public class ProductRepository : Ef5Repository<Product>, IProductRepository
{
// TODO The DbContext has to be injected manually.
public ProductRepository(DbContext context) : base(context)
{
}
// [...]
}
Goal:
using SharpRepository.Repository;
public interface IProductRepository : IRepository<Product>
{
}
public class ProductRepository : ConfigurationBasedRepository<Product, int>, IProductRepository
{
// [...]
}
I've already read the two blog posts SharpRepository: Getting Started and SharpRepository: Configuration, but they both do not help me, since:
The used DIC is StructureMap, not Ninject.
The source code examples are incomplete (e.g. usage of not declared variables).
So my question: Can someone provide me with some source code example how-to to achieve the goal described above (sharing one Entity Framework DbContext instance between all repositories extending ConfigurationBasedRepository)?
First, you will need to install the SharpRepository.Ioc.Ninject NuGet package. There are extension methods in here for hooking up Ninject to handle the loading a generic repository and setting the dependency resolver that SharpRepository uses.
Where ever you are setting up your Ninject binding rules (all the calls to kernel.Bind<>), you will need to add:
kernel.BindSharpRepository();
Next, in your Global.asax, or App_Start code, or your Bootstrapper logic (where ever you are calling application startup code) you will need to add the following:
// kernel is the specific kernel that you are setting up all the binding for
RepositoryDependencyResolver.SetDependencyResolver(new NinjectDependencyResolver(kernel));
This will tell SharpRepository to use this Ninject Kernel when getting a new DbContext.
The last thing to do is to setup the rules for binding for the DbContext itself. If you are in a web application you will most likely want the scope of the DbContext to be per request. I personally don't use Ninject but I found this reference for using InRequestScope. I believe your code would look something like this:
kernel.Bind<DbContext>().To<MyCustomEfContext>().InRequestScope().WithConstructorArgument("connectionString", ConfigurationManager.ConnectionStrings["MyCustomEfContext"].ConnectionString);
Most people won't need this next piece but if you have custom logic in your CustomEfContext (I have an override for logging on calls to SaveChanges() for example), then you'll need to define your custom context type in the configuration file like so:
<repositories>
<repository name="ef5Repository" connectionString="CustomEfContext" cachingStrategy="standardCachingStrategy" dbContextType="My.Data.CustomEfContext, My.Data" factory="SharpRepository.Ef5Repository.Ef5ConfigRepositoryFactory, SharpRepository.Ef5Repository" />
</repositories>
Where dbContextType defines the type of the custom DbContext you are using using the full type, namespace syntax. If you do this then you'll need to set Ninject to Bind on the custom context by changing .Bind<DbContext>() to .Bind<CustomEfContext>(). But like I said normally you can use DbContext directly without an issue.
First of all, the solution provided in the answer by Jeff T works!
I will conclude the steps I took to make Ninject work in a ASP.NET MVC 4 + EF 5 project. It is important to mention that the Specific Repository pattern is implemented via SharpRepository in the following example.
Required software
Install Ninject and "Ninject.MVC3" (which also installs "Ninject.Web.Common") via NuGet.
Install SharpRepository, "SharpRepository for EF5" and "SharpRepository with Ninject IOC" via NuGet.
Define the Repository layer
Create a DbContext derived class, e.g. Domain.EfContext. It is the
"recommended way to work with context".
Declare all required DbSet<T> as public properties, e.g. public DbSet<Product> Products { get; set; }
Declare the following two constructors in the class Domain.EfContext:
public EfContext() : base() {}
public EfContext(string connectionName) : base(connectionName) {}
Define an interface for the Specific Repository, e.g.:
// TODO By extending IRepository, the interface implements default Create-Read-Update-Delete (CRUD) logic.
// We can use "traits" to make the repository more "specific", e.g. via extending "ICanInsert".
// https://github.com/SharpRepository/SharpRepository/blob/master/SharpRepository.Samples/HowToUseTraits.cs
public interface IProjectRepository : IRepository<Project>
{
// TODO Add domain specific logic here.
}
Define a class which is implementing the Specific Repository and inherits from SharpRepository.Repository.ConfigurationBasedRepository<T, TKey>, e.g.:
public class ProductRepository : ConfigurationBasedRepository<Product, int>, IProductRepository
{
// TODO Implement domain specific logic here.
}
Define the Consumer layer
Create a Controller, e.g. Controllers.ProductController.
public class ProductController : Controller
{
private IProductRepository Repository { get; private set; }
// TODO Will be used by the DiC.
public ProductController(IProductRepository repository)
{
this.Repository = repository;
}
}
Set up Dependency Injection (DI) via the Dependency Injection Container (DiC) Ninject
The file App_Start/NinjectWebCommon.cs is automatically created by Ninject.Web.Common and we can load our modules and register our services in the method RegisterServices(IKernel kernel) : void of the class NinjectWebCommon.
Here is the complete source code of that method for the example:
private static void RegisterServices(IKernel kernel)
{
kernel.BindSharpRepository();
RepositoryDependencyResolver.SetDependencyResolver(
new NinjectDependencyResolver(kernel)
);
string connectionString = ConfigurationManager.ConnectionStrings["EfContext"].ConnectionString;
kernel.Bind<DbContext>()
.To<EfContext>()
.InRequestScope()
.WithConstructorArgument("connectionString", connectionString);
kernel.Bind<IProductRepository>().To<ProductRepository>();
}
Define the following sharpRepository section in the Web.config:
<sharpRepository>
<repositories default="ef5Repository">
<repository name="ef5Repository"
connectionString="EfContext"
cachingStrategy="standardCachingStrategy"
dbContextType="Domain.EfContext, Domain"
factory="SharpRepository.Ef5Repository.Ef5ConfigRepositoryFactory, SharpRepository.Ef5Repository"
/>
</repositories>
</sharpRepository>
In addition, the connectionStrings section to make the example complete (I am using SQL Server LocalDB).
<connectionStrings>
<add name="EfContext" providerName="System.Data.SqlClient" connectionString="Data Source=(localdb)\v11.0;Initial Catalog=Domain;Integrated Security=True" />
</connectionStrings>
I hope that this conclusion helps other people to get ASP.NET MVC 4 together with Entity Framework 5 and SharpRepository up and running!
Please leave me a reply if I took one or more unnecessary steps or if you see possibilities to improve the architecture described in the example.
Btw, I had to add the dbContextType attribute to the repository section to make it work (in contrast to the answer of Jeff T).
EDIT (2013-08-28): Striked out unnecessary steps (not required with the latest version of SharpRepository).

Categories

Resources