MVC db context overuse? - entity-framework

I have added a database repository layer to my MVC application which does the CRUD. Sometimes my controllers need to call multiple db repositories and I do this by calling the db respitories I need. This in turn creates multiple db context objects. One for each repository. Should there be multiple db context objects or should I pass in a single db context to the repository object?

In your controller you should use one dbContext. Because When you try to update your model in db, you may get error. Because of different dbContext.
Check HERE

There should be only one, I highly recommend using Unit of Work pattern:
Here's a quick and simple example:
public interface IUoW : IDisposable
{
MyDbContext DbContext { get; set; }
void SaveChanges();
}
public class UoW : IUoW
{
public MyDbContext DbContext { get; set; }
public UoW()
{
DbContext = new MyDbContext();
}
public void SaveChanges()
{
DbContext.SaveChanges();
}
public void Dispose()
{
DbContext.Dispose();
}
}
You need to instantiate UoW once for each request and pass it to your repository:
public class MyRepository
{
private MyDbContext _context;
public MyRepository(IUoW uow)
{
_context = uow.MyDbContext;
}
// your crud methods
}
Of course it's just a very simple example of it and I've seen people implement this pattern in many different ways.

Related

How to add a parameterized DbContext to a Unit of work

I am trying to refactor my asp.net mvc + entity framework project to use repository pattern, and a unit of work. In the newest versions of asp.net mvc DbContext is creating automatically using a parameterized constructor, it looks like this:
public class SchoolContext : DbContext
{
public SchoolContext(DbContextOptions<SchoolContext> options)
: base(options)
{
}
public DbSet<Student> Students { get; set; }
public DbSet<Group> Groups { get; set; }
public DbSet<Course> Courses { get; set; }
}
How can I create an instance of this DbContext in unitOfWork class, what options should I write it the brackets?
P.S: I can't remove the parameters, cause they are used in program.cs in the next way
builder.Services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("SchoolContext") ?? throw new InvalidOperationException("Connection string 'SchoolContext' not found.")));
And I also can't add an emty constuctor, cause it will cause an error due to the above statement. (you can't have both constructors)
Thanks to the comments I figured it out, here what I was looking for:
In the class create an instance of context
private SchoolContext _context;
And then initialize it using a constructor like this
public UnitOfWork()
{
var contextOptions = new DbContextOptionsBuilder<SchoolContext>()
.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=Mentoring.Data;Trusted_Connection=True;MultipleActiveResultSets=true")
.Options;
_context = new SchoolContext(contextOptions);
}

Log Exceptions with EntityFramework in WebAPI Exception Filter or IExceptionLogger

I'm thinking to use ExceptionLogger or ExceptionFilterAttribute in my Web API 2.x application. My OR/M is Entity Framework 6 and AutoFac as my IoC.
Consider exception attribute below:
public class ApiExceptionFilterAttribute : ExceptionFilterAttribute
{
public IDbContext MyContext { get; set; }
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
base.OnException(actionExecutedContext);
// some code
MyContext.SaveChanges();
}
}
I think, if my app throws any exception OnException method will be invoke and I need a new instance of DbContext to save the error log, but my AutoFac configuration for DbContext is InstancePerRequest so I think AutoFac will resolve MyContext as a shared DbContext.
Here is my AutoFac configuration for DbContext:
builder.RegisterType<DbContext>()
.As<IDbContext>()
.InstancePerRequest();
How could I make AutoFac to reslove DbContext as a new instance for ApiExceptionFilterAttribute?
Ok, since no one answered my question, I talk a little about how everyone could solve this problem. To solve this problem you can only use ServiceLocator pattern (it's an anti-pattern.). By injecting a life-time-scope object of AutoFac you are able to create different scopes.
public class ApiExceptionFilterAttribute : ExceptionFilterAttribute
{
// public IDbContext MyContext { get; set; }
public readonly ILifetimeScope LifeTimeScpoe{ get; set; }
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
base.OnException(actionExecutedContext);
// some code
using(var scope = LifeTimeScope.BeginLifeTimeScope("ExceptionLogging"))
{
using(var dbContext = scope.Resolve<>(IDbContext))
{
// do some log logic
dbContext.SaveChanges();
}
}
}
}

Creating a generic database context in MVC 4

I wrote a MVC 4 app. I have some questions:
public class DatabaseContext<TEntity>: DbContext where TEntity: class
{
...
public DbSet<TEntity> entity = {get; set;}
...
}
I want to create a generic database context like this DatabaseContext and use it for all my Entities defined in database tables.
Please, write an example.
I don't now how to initialize generic context in global.asax once and use it every time, in whichever part of the project necessary.
Please, write some examples.
It seems to me to what you're implementing is the Repository Pattern:
public interface IRepository<TEntity> where TEntity : class
{
IEnumerable<TEntity> GetAll();
TEntity GetById(Guid id);
}
Prevent letting your repository inherit from DbContext, since DbContext is an implementation of the Unit of Work pattern and a unit of work is not a repository (but rather contains or manages multiple repositories).
What you can do is to let your repository use the DbContext internally:
public class Repository<TEntity> : IRepository<TEntity>
{
private readonly DbContext context;
public Repository(DbContext context)
{
this.context = context;
}
public IEnumerable<TEntity> GetAll()
{
return this.context.Set<TEntity>();
}
public TEntity GetById(Guid id)
{
var entity = this.context.Set<Entity>().Find(id);
if (entity == null) throw new KeyNotFoundException(
typeof(TEntity).Name + " with id " + id + " not found);
return entity;
}
}
UPDATE
Since I'm a Dependency Injection enthusiast, I think that Dependency Injection is the solution to your problem. And since I'm a developer for the Simple Injector project, I'll show you how to do this using Simple Injector:
Step 1: Install the Simple Injector MVC Integration Quick Start NuGet package into your MVC project (I assume you know how to install NuGet packages).
Step 2: Compile your project. You'll get a compiler error in the SimpleInjectorInitializer class that the package just added. This is the line where you will have to make your registrations. You can just remove this #error line.
Step 3: Add the SimpleInjector.Extensions namespace to the top of the SimpleInjectorInitializer file:
using SimpleInjector.Extensions;
Step 4: Make the following registrations in the InitializeContainer method:
container.RegisterOpenGeneric(typeof(IRepository<>), typeof(Repository<>));
container.RegisterPerWebRequest<DbContext>(
() => new DbContext("Your connection string here"));
Step 5: Add the IRepository<T> dependencies to your contollers:
public class CustomerController : Controller
{
private readonly IRepository<Customer> customerRepository;
public CustomerController(IRepository<Customer> customerRepository)
{
this.customerRepository = customerRepository;
}
// controller methods here.
}
Now your repositories will be automatically be injected into your controllers.
Is there a reason you want to make a DatabaseContext with a generic type parameter? You won't be to instantiate it in a single place because each different DatabaseContext is a separate .NET Type.
Unless you're sticking rigidly to a particular pattern, I personally don't see any practical advantage of this approach over a single DatabaseContext with many sets:
public class DatabaseContext: DbContext
{
public DbSet<SomeEntity> SomeEntities { get; set; }
public DbSet<OtherEntity> OtherEntities { get; set; }
}
...
myDatabaseContext.SomeEntities.GetAll();
myDatabaseContext.OtherEntities.GetAll();
// OR:
myDatabaseContext.Set<SomeEntity>().GetAll();
myDatabaseContext.Set<OtherEntity>().GetAll();

Why DbContext doesn't implement IDbContext interface?

Why there is no IDbContext interface in the Entity Framework? Wouldn't it be easier to test things if there was an existing interface with methods like SaveChanges() etc. from which you could derive your custom database context interface?
public interface ICustomDbContext : IDbContext
{
// add entity set properties to existing set of methods in IDbContext
IDbSet<SomeEntity> SomeEntities { get; }
}
I see this IDbContext:
See this link And then you make a new partial class for your Entities Context With That interface.
public partial class YourModelEntities : DbContext, IDbContext
EDITED:
I edited this post, This Works for me.
My Context
namespace dao
{
public interface ContextI : IDisposable
{
DbSet<TEntity> Set<TEntity>() where TEntity : class;
DbSet Set(Type entityType);
int SaveChanges();
IEnumerable<DbEntityValidationResult> GetValidationErrors();
DbEntityEntry<TEntity> Entry<TEntity>(TEntity entity) where TEntity:class;
DbEntityEntry Entry(object entity);
string ConnectionString { get; set; }
bool AutoDetectChangedEnabled { get; set; }
void ExecuteSqlCommand(string p, params object[] o);
void ExecuteSqlCommand(string p);
}
}
YourModelEntities is your auto-generated partial class, and your need to create a new partial class with the same name, then add your new context interface, for this example is ContextI
NOTE: The interface hasn't implement all methods, because the methods are implemented in your auto-generate code.
namespace dao
{
public partial class YourModelEntities :DbContext, ContextI
{
public string ConnectionString
{
get
{
return this.Database.Connection.ConnectionString;
}
set
{
this.Database.Connection.ConnectionString = value;
}
}
bool AutoDetectChangedEnabled
{
get
{
return true;
}
set
{
throw new NotImplementedException();
}
}
public void ExecuteSqlCommand(string p,params object[] os)
{
this.Database.ExecuteSqlCommand(p, os);
}
public void ExecuteSqlCommand(string p)
{
this.Database.ExecuteSqlCommand(p);
}
bool ContextI.AutoDetectChangedEnabled
{
get
{
return this.Configuration.AutoDetectChangesEnabled;
}
set
{
this.Configuration.AutoDetectChangesEnabled = value;
}
}
}
}
I was thinking also about that, I assume you are going to use it for mocking DbContext. I find no reason for that, except that you will need to implement your own DbSet manually in your anyway for your mocked class (so will need to rewrite your own interface anyway).
Just create a mock DbContext extending your production DbContext overriding the methods that complicate testing. That way, any changes to the production DbContext are automatically reflected in the tests, save for the overridden methods. For any other classes that deal with persistence and take the DbContext just extend them as well passing in the extended mock DbContext.
namespace Test.Mocks
{
public sealed class MockDatabaseContext : MainProject.Persistence.Database.DatabaseContext
{
public MockDatabaseContext(ConfigurationWrapper config) : base(config)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var dbPath = "test.db";
optionsBuilder.UseSqlite($"Filename={dbPath}");
}
}
}
namespace Test.Mocks
{
public class MockInventoryFacade : InventoryFacade
{
public MockInventoryFacade(MockDatabaseContext databaseContext) : base(databaseContext)
{
}
}
}
There is no IDbContext because it would be useless, the only implementation of it would be the DbContext.
EF team is also going this way with IDbSet if you look at this design meeting note
For me, the real problem with EF when it comes to unit testing is the DbConnection in the DbContext, fortunately there is Effort a nice project on codeplex that starts to fill this.
Effort is a powerful tool that enables a convenient way to create automated tests for Entity Framework based applications.
It is basically an ADO.NET provider that executes all the data operations on a lightweight in-process main memory database instead of a traditional external database. It provides some intuitive helper methods too that make really easy to use this provider with existing ObjectContext or DbContext classes. A simple addition to existing code might be enough to create data driven tests that can run without the presence of the external database.
With this, you can leave your DbContext and DbSet as is and do your unit tests easily.
The only drawback with this is the difference between Linq providers where some unit tests may pass with effort and not with the real backend.
UPDATE with EF7
I still maintain that IDbContext would be useless and the problem comes from the DbConnection.
EF7 will not have an IDbContext either, in order to do unit testing they are now giving an in memory provider.
You can see Rowan Miller doing a demo here: Modern Data Applications with Entity Framework 7

EF4 - ChangeObjectState is no longer accessible in RC

After migrated EF4 CTP5 to RC1, I notice the ObjectContext is no longer accessible through DbContext. It means I can't access ChangeObjectState method.
class DataContext : DbContext
{
public DataContext()
{
}
public DataContext(DbCompiledModel dbModel)
: base(dbModel)
{
}
public DbSet<MyClass> MyClasses { get; set; }
public void ChangeObjectState<T>(T entity, EntityState entityState)
{
// this is no longer working.. where is ObjectContext?
ObjectContext.ChangeObjectState(entity, entityState);
}
}
Has anyone have any idea how access that method in RC1?
Thanks.
You don't need to access ObjectContext to change object state. Use this:
this.Entry<T>(entity).State = entityState;