Giter Club home page Giter Club logo

vany0114 / ef.dbcontextfactory Goto Github PK

View Code? Open in Web Editor NEW
91.0 9.0 21.0 12.86 MB

With EF.DbContextFactory you can resolve easily your DbContext dependencies in a safe way injecting a factory instead of an instance itself, enabling you to work in multi-thread contexts with Entity Framework or just work safest with DbContext following the Microsoft recommendations about the DbContext lifetime.

Home Page: http://elvanydev.com/EF-DbContextFactory/

License: MIT License

C# 73.88% ASP 0.31% CSS 0.76% JavaScript 12.00% Batchfile 0.05% HTML 13.01%
csharp dotnet dotnet-core dotnetcore entity-framework entity-framework-core entityframework dbcontext netframework concurrency

ef.dbcontextfactory's Introduction

EF.DbContextFactory

Appveyor GitHub Issues first-timers-only License Tweet

With EF.DbContextFactory you can resolve easily your DbContext dependencies in a safe way injecting a factory instead of an instance itself, enabling you to work in multi-thread contexts with Entity Framework Core or just work safest with DbContext following the Microsoft recommendations about the DbContext lifecycle but keeping your code clean and testable using dependency injection pattern.

Package Status

EFCore.DbContextFactory NuGet
EF.DbContextFactory.Unity NuGet
EF.DbContextFactory.Ninject NuGet
EF.DbContextFactory.StructureMap NuGet
EF.DbContextFactory.StructureMap.WebApi NuGet
EF.DbContextFactory.SimpleInjector NuGet

The Problem 😧

The Entity Framework DbContext has a well-known problem: it’s not thread safe. So it means, you can’t get an instance of the same entity class tracked by multiple contexts at the same time. For example, if you have a realtime, collaborative, concurrency or reactive application/scenario, using, for instance, SignalR or multiple threads in background (which are common characteristics in modern applications). I bet you have faced this kind of exception:

"The context cannot be used while the model is being created. This exception may be thrown if the context is used inside the OnModelCreating method or if the same context instance is accessed by multiple threads concurrently. Note that instance members of DbContext and related classes are not guaranteed to be thread safe"

The Solutions 🤔

There are multiple solutions to manage concurrency scenarios from data perspective, the most common patterns are Pessimistic Concurrency (Locking) and Optimistic Concurrency, actually Entity Framework has an implementation of Optimistic Concurrency. So these solutions are implemented usually on the database side or even in both, backend and database sides, but the problem with DbContext is that's happening on memory, don't even in database. An approach what allows you to keep your code clean, follow good practices and keep on using Entity Framework and obvoiusly that works fine in multiple threads is injecting a factory in your repositories/unit of work (or whatever you're using it code smell) instead of the instance itself and use it and dispose it as soon as possible.

Key points 😬

  • Dispose DbContext immediately.
  • Less consume of memory.
  • Create the instance and connection database only when you really need it.
  • Works in concurrency scenarios.
  • Without locking.

Motivation 😎

I have worked with Entity Framework in a lot of projects, it’s very useful, it can make you more productive and it has a lot of great features that make it an awesome ORM, but like everything in the world, it has its downsides or issues. Sometime I was working on a project with concurrency scenarios, reading a queue from a message bus, sending messages to another bus with SignalR and so on. Everything was going good until I did a real test with multiple users connected at the same time, it turns out Entity Framework doesn’t work fine in that scenario. I did know that DbContext is not thread safe therefore I was injecting my DbContext instance per request following the Microsoft recommendatios so every request would has a new instance and then avoid problems sharing the contexts and state’s entities inside the context, but it doesn’t work in concurrency scenarios. I really had a problem, beause I didn’t want to hardcode DbContext creation inside my repository using the using statement to create and dispose inmediatly, but I had to support concurrency scenarios with Entity Framework in a proper way. So I remembered sometime studying the awesome CQRS Journey Microsoft project, where those guys were injecting their repositories like a factory and one of them explained me why. This was his answer:

"This is to avoid having a permanent reference to an instance of the context. Entity Framework context life cycles should be as short as possible. Using a delegate, the context is instantiated and disposed inside the class it is injected in and on every needs."

Getting Started 😀

EF.DbContextFactory provides you extensions to inject the DbContext as a factory using the Microsoft default implementation of dependency injection for Microsoft.Extensions.DependencyInjection as well as integration with most popular dependency injection frameworks such as Unity, Ninject, Structuremap, Simple Injector. So there are five Nuget packages so far listed above that you can use like an extension to inject your DbContext as a factory.

All of nuget packages add a generic extension method to the dependency injection framework container called AddDbContextFactory. It needs the derived DbContext Type and as an optional parameter, the name or the connection string itself. If you have the default one (DefaultConnection) in the configuration file, you dont need to specify it

You just need to inject your DbContext as a factory instead of the instance itself:

public class OrderRepositoryWithFactory : IOrderRepository
{
    private readonly Func<OrderContext> _factory;

    public OrderRepositoryWithFactory(Func<OrderContext> factory)
    {
        _factory = factory;
    }
    .
    .
    .
}

And then just use it when you need it executing the factory, you can do that with the Invoke method or implicitly just using the parentheses and that's it!

public class OrderRepositoryWithFactory : IOrderRepository
{
    .
    .
    .
    public void Add(Order order)
    {
        using (var context = _factory.Invoke())
        {
            context.Orders.Add(order);
            context.SaveChanges();
        }
    }
    
    public void DeleteById(Guid id)
    {
        // implicit way way
        using (var context = _factory())
        {
            var order = context.Orders.FirstOrDefault(x => x.Id == id);
            context.Entry(order).State = EntityState.Deleted;
            context.SaveChanges();
        }
    }
}

EFCore.DbContextFactory

If you are using the Microsoft DI container you only need to install EFCore.DbContextFactory nuget package. After that, you are able to access to the extension method from the ServiceCollection object.

EFCore.DbContextFactory supports netstandard2.0 and netstandard2.1

The easiest way to resolve your DbContext factory is using the extension method called AddSqlServerDbContextFactory. It automatically configures your DbContext to use SqlServer and you can pass it optionally the name or the connection string itself If you have the default one (DefaultConnection) in the configuration file, you dont need to specify it and your ILoggerFactory, if you want.

using EFCore.DbContextFactory.Extensions;
.
.
.
services.AddSqlServerDbContextFactory<OrderContext>();

Also you can use the known method AddDbContextFactory with the difference that it receives the DbContextOptionsBuilder object so you’re able to build your DbContext as you need.

var dbLogger = new LoggerFactory(new[]
{
    new ConsoleLoggerProvider((category, level)
        => category == DbLoggerCategory.Database.Command.Name
           && level == LogLevel.Information, true)
});

// ************************************sql server**********************************************
// this is like if you had called the AddSqlServerDbContextFactory method.
services.AddDbContextFactory<OrderContext>(builder => builder
    .UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))
    .UseLoggerFactory(dbLogger));

services.AddDbContextFactory<OrderContext>((provider, builder) => builder
    .UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
            
// ************************************sqlite**************************************************
services.AddDbContextFactory<OrderContext>(builder => builder
    .UseSqlite(Configuration.GetConnectionString("DefaultConnection"))
    .UseLoggerFactory(dbLogger));

// ************************************in memory***********************************************
services.AddDbContextFactory<OrderContext>(builder => builder
    .UseInMemoryDatabase("OrdersExample")
    .UseLoggerFactory(dbLogger));

You can find more examples here

Ninject Asp.Net Mvc and Web Api

If you are using Ninject as DI container into your Asp.Net Mvc or Web Api project you must install EF.DbContextFactory.Ninject nuget package. After that, you are able to access to the extension method from the Kernel object from Ninject.

using EF.DbContextFactory.Ninject.Extensions;
.
.
.
kernel.AddDbContextFactory<OrderContext>();

StructureMap Asp.Net Mvc and Web Api

If you are using StructureMap as DI container into your Asp.Net Mvc or Web Api project you must install EF.DbContextFactory.StructureMap nuget package. After that, you are able to access the extension method from the Registry object from StructureMap.

using EF.DbContextFactory.StructureMap.Extensions;
.
.
.
this.AddDbContextFactory<OrderContext>();

StructureMap 4.1.0.361 Asp.Net Mvc and Web Api or WebApi.StructureMap

If you are using StructureMap >= 4.1.0.361 as DI container or or WebApi.StructureMap for Web Api projects you must install EF.DbContextFactory.StructureMap.WebApi nuget package. After that, you are able to access the extension method from the Registry object from StructureMap. (In my opinion this StructureMap version is cleaner)

using EF.DbContextFactory.StructureMap.WebApi.Extensions;
.
.
.
this.AddDbContextFactory<OrderContext>();

Unity Asp.Net Mvc and Web Api

If you are using Unity as DI container into your Asp.Net Mvc or Web Api project you must install EF.DbContextFactory.Unity nuget package. After that, you are able to access the extension method from the UnityContainer object from Unity.

using EF.DbContextFactory.Unity.Extensions;
.
.
.
container.AddDbContextFactory<OrderContext>();

SimpleInjector Asp.Net Mvc and Web Api

If you are using SimpleInjector as DI container into your Asp.Net Mvc or Web Api project you must install EF.DbContextFactory.SimpleInjector nuget package. After that, you are able to access the extension method from the Container object from SimpleInjector.

using EF.DbContextFactory.SimpleInjector.Extensions;
.
.
.
container.AddDbContextFactory<OrderContext>();

Examples 🤘

You can take a look at the examples to see every extension in action, all you need is to run the migrations and that's it. Every example project has two controllers, one to receive a repository that implements the DbContextFactory and another one that doesn't, and every one creates and deletes orders at the same time in different threads to simulate the concurrency. So you can see how the one that doesn't implement the DbContextFactory throws errors related to concurrency issues.

Contribution ❤️ 💪

Your contributions are always welcome, feel free to improve it or create new extensions for others dependency injection frameworks! All your work should be done in your forked repository. Once you finish your work, please send a pull request onto dev branch for review. For more details take a look at PR checklist and contributions guidelines.

Visit my blog http://elvanydev.com/EF-DbContextFactory/ to view the whole post and to know the motivation for this project!

Support

If you find this project helpful you can support me!

ef.dbcontextfactory's People

Contributors

ptyang avatar terravenil avatar vany0114 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ef.dbcontextfactory's Issues

I am having doubts while learning DbContextFactory

Hello. I am having doubts when using DbContextFactory. Is there any difference between CreateDbContext() and new DbContext()?
Is there any difference between using DbContextFactory and using(=new DbContext())? Thank you, can you tell me?

Unity.Abstractions?

I'm using the Unity version. When I try to use container.AddDbContextFactory<...>(), I get an error like "The type IUnityContainer is defined in an assembly that is not referenced. You must add a reference to assembly 'Unity.Abstractions yadda yadda yadda'"

Any idea what I could be doing wrong?

Remove dependency on AspNetCore.All where it isn't needed

It would be nice if AspNetCore.All could be removed from a dependency here, as it's quite large - and from looking at the extension/source file, there isn't a need there.

This would make using the EF.DbContextFactory nicer outside of the context of an ASP .NET Core application.

Not Working With IdentityDbContext

Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory Lifetime: Scoped ImplementationType: Microsoft.AspNetCore.Identity.UserClaimsPrincipalFactory1 Unable to resolve service for type 'HRPlatform.Data.Context.HRPDbContext' while attempting to activate

Add support for migrations to EFCore.DbContextFactory

Heey Giovani (it's me again),

I'm trying to split my project in to multiple projects. Now i have a seperate project for my repository layer. I would like to do services.AddDbContextFactory on this project but it is a .netstandard project.

Only the project that is using the different libraries is configured as .netcore. This way i have to register the database at the application instead of the project where it came from. Is there a way to do the services.AddDbContextFactory from a .netstandard library that is implemented by a .netcore application.

Kind Regards,
Luc

Healthchecks support

Hello vany0114,

We use the dbcontextfactory and would like to start using the EF 2.2 health checks. After starting with the health checks i encountered a problem. The healthcheck delivered in AspNet makes use of DbContext. And not the DbContextFactory we use.

Is it possible to create or implement the healthcheck of 2.2 in this package?

King regards Inlustris,

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.