ASP.NET Core Dependency Injection, inject with parameters - c#

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.

Related

Net Core 3 - Accessing DBContext outside of the web api

i'm building a small webapi to work in conjunction with additional functionalities running in the background.
In the specific case I have a class called TelegramBot:
public class TelegramBot
{
static ITelegramBotClient botClient;
private readonly BotManagerContext _botManagerContext;
public TelegramBot(BotManagerContext botManagerContext)
{
_botManagerContext = botManagerContext;
botClient = new TelegramBotClient("xxxx:yyyyy");
botClient.OnMessage += Bot_OnMessage;
botClient.StartReceiving();
}
That I'm trying to run together with the web api. BotManagerContext is a DbContext initialized in the web api, i'm trying to retrieve it using dependency injection - so i'm trying to add the TelegramBot class into the Startup.cs file so that it starts as a Singleton and can retrieve the dbcontext
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<BotManagerContext>(opt =>
opt.UseSqlite("Data Source=BotManager.db"));
services.AddControllers();
services.AddSingleton<TelegramBot>();
}
Question is - how do I implement this? using an interface? I'm fairly new to this and I don't know how to implement it :)
Thanks
Implementing your own IHostedService would be the best way to go about this. For getting the dbcontext in the service you can use IserviceProvider as your dependency. Serviceprovider will give you the dbcontext.
You can configure your custom hosted service to be added as a singleton then. Check this documentation for details:
https://learn.microsoft.com/en-us/dotnet/architecture/microservices/multi-container-microservice-net-applications/background-tasks-with-ihostedservice#implementing-ihostedservice-with-a-custom-hosted-service-class-deriving-from-the-backgroundservice-base-class

Please explain how my DbContext is being instantiate?

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

Why is my EF Core DbContext not bound by DI?

I have an Azure Function App with a function that runs on a blob trigger. I've proven that this function can run through the Azure Portal and responds to this blob trigger without issues... or at least it did.
Now that I've added functionality which makes use of EF Core (2.2.4), it gives me the following error, both when debugging locally and when publishing to Azure:
Microsoft.Azure.WebJobs.Host: Error indexing method 'ParseThings'. Microsoft.Azure.WebJobs.Host: Cannot bind parameter 'context' to type AvastusContext. Make sure the parameter Type is supported by the binding. If you're using binding extensions (e.g. Azure Storage, ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. builder.AddAzureStorage(), builder.AddServiceBus(), builder.AddTimers(), etc.).
I have a Startup class as instructed by Azure Function App documentation here, and have followed their example to the letter, aside from the following line in place of their configured example services:
[assembly: FunctionsStartup(typeof(AvstFunctionApp.Startup))]
namespace AvstFunctionApp
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddDbContext<AvastusContext>(options => options.UseSqlServer(Environment.GetEnvironmentVariable("AvastusDb")));
}
}
}
And the beginning of my function:
public static class ParseThings
{
[FunctionName("ParseThings")]
public static void Run([BlobTrigger("summaries/{name}", Connection = "StorageConnectionString")]Stream myBlob, string name, ILogger log, AvastusContext context)
I can confirm that the AddDbContext line is getting hit in a debugger, so presumably there's some bug happening behind the scenes here, or I'm doing something incredibly silly.
Things I've tried that haven't worked include:
Adding .BuildServiceProvider(true) to the AddDbContext line
Using WebJobsStartup instead of the more recently advertised FunctionsStartup
Downgrading to .NET Core 2.2.0
Changing the Function class and Run method from static to instance
Fixing incorrect namespace of the injected AvastusContext
It's also worth noting that there are two other functions in this Function App project which don't seem to have any serious issues, and I've been able to get dependency injection working using a similar method with EF Core for another (ASP.NET Core MVC) project in this solution.
Thank you in advance for any help anyone can provide!
P.S. I find it incredibly weird that there hasn't been anything describing this situation with the later versions of .NET Core, Azure Function Apps, and EF Core on the interwebs, which leads me to believe that this might be a simple mistake. Hopefully not.
perhaps one solution can be you can try injecting IServiceProvider in your function instead of AvastusContext like I have injected in the repository class below:
private readonly IServiceProvider serviceProvider;
public SomeRepository(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
using var context = this.serviceProvider.GetService<XYZDBContext>();
This will provide a context object to you. Also, Not sure why you are trying to access context in the function directly for good practice have a context class defined, and maintain repository to do any CRUD operation in the code.
Startup.cs you can add extra configurations like :
builder.Services.AddDbContext<XYZDBContext>(
options =>
{
options.UseSqlServer(
conn,
sqlServerOptionsAction:
sqlOptions =>
{
sqlOptions.EnableRetryOnFailure(maxRetryCount: 3, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
});
}, ServiceLifetime.Transient);
this configuration works perfectly fine in my current solution. Try this out.
Function app can't resolve dbcontext in functions as it can only resolve BindingContext. You need to create custom bindings to use dbcontext directly in function app.
Other way to get dbcontext injected via DI is to pass it to constructor and using a class level variable in the function.
public class ParseThings
{
private AvastusContext _context;
public ParseThings(AvastusContext context){
_context = context;
}
[FunctionName("ParseThings")]
public void Run([BlobTrigger("summaries/{name}", Connection = "StorageConnectionString")]Stream myBlob, string name, ILogger log){
// use _context here
}
}
If it still doesn't resolve you might want to look it into whether the functionsStartup is configured properly

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);
}

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