Entity Framework read queries locking all database - c#

I'm developing a web application using ASP.NET MVC and EF6 to access the database.
One of the features of my web application allow the user to download a Excel file. The query to get the information from the database takes like 5 seconds and I notice that until the query it's done we can't do anything on the rest of the web application.
Is this the normal behaviour of EF, lock the database even with AsNoTracking on the query?
If I'm not doing anything wrong and this is the default behaviour of EF how should I resolve this locking problem?
(Update)
I'm using a SQL Server database and the "lock" happens when for exemple I export the excel file and at the same time do a search that uses the same table.
To organize my code i'm using Repository and UnitOfWork pattern and to create the instances i'm using DI Unity.
The UnitOfWork implementation:
public class UnitOfWork : IUnitOfWork
{
private bool _disposed;
private DbContext _dbContext;
private Dictionary<string, dynamic> _repositories;
private DbContextTransaction _transaction;
public DbContext DbContext
{
get { return _dbContext; }
}
public UnitOfWork(DbContext dbContext)
{
_dbContext = dbContext;
}
public int SaveChanges()
{
return _dbContext.SaveChanges();
}
public IRepository<TEntity> Repository<TEntity>()
{
try
{
if (ServiceLocator.IsLocationProviderSet)
return ServiceLocator.Current.GetInstance<IRepository<TEntity>>();
if (_repositories == null)
_repositories = new Dictionary<string, dynamic>();
var type = typeof(TEntity).Name;
if (_repositories.ContainsKey(type))
return (IRepositoryAsync<TEntity>)_repositories[type];
var repositoryType = typeof(Repository<>);
_repositories.Add(type, Activator.CreateInstance(repositoryType.MakeGenericType(typeof(TEntity)), this));
return _repositories[type];
}
catch(ActivationException ex)
{
throw new ActivationException(string.Format("You need to configure the implementation of the IRepository<{0}> interface.", typeof(TEntity)), ex);
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~UnitOfWork()
{
Dispose(false);
}
public void Dispose(bool disposing)
{
if(!_disposed)
{
if(disposing)
{
try
{
_dbContext.Dispose();
_dbContext = null;
}
catch(ObjectDisposedException)
{
//the object has already be disposed
}
_disposed = true;
}
}
}
}
The Repository implementation:
public class Repository<TEntity> : IRepository<TEntity>
where TEntity : class
{
private readonly IUnitOfWork _unitOfWork;
private readonly DbContext _dbContext;
private readonly DbSet<TEntity> _dbSet;
public Repository(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
_dbContext = unitOfWork.DbContext;
_dbSet = _dbContext.Set<TEntity>();
}
#region IRepository<TEntity> implementation
public void Insert(TEntity entity)
{
_dbSet.Add(entity);
}
public void Update(TEntity entity)
{
_dbContext.Entry(entity).State = EntityState.Modified;
}
public void Delete(TEntity entity)
{
_dbSet.Remove(entity);
}
public IQueryable<TEntity> Queryable()
{
return _dbSet;
}
public IRepository<TEntity> GetRepository<TEntity>()
{
return _unitOfWork.Repository<TEntity>();
}
#endregion
}
The Unity configuration:
container.RegisterType<DbContext, DbSittiusContext>(new PerRequestLifetimeManager());
container.RegisterType<IUnitOfWork, UnitOfWork>(new PerRequestLifetimeManager());
//Catalog respository register types
container.RegisterType<IRepository<Product>, Repository<Product>>();
UnityServiceLocator locator = new UnityServiceLocator(container);
ServiceLocator.SetLocatorProvider(() => locator);
To create my query have to create a extension method like this:
public static Product FindPublishedAtDateById(this IRepository<Product> repository, int id, DateTime date)
{
return repository.
Queryable().
Where(p => p.Id == id).
Where(p => p.PublishedFrom <= date && (p.PublishedTo == null || p.PublishedTo >= date)).
SingleOrDefault();
}

If you're downloading a lot of data synchronously it will make the UI freeze up on you. Consider doing this asynchronously. What are you using client side, anyway?
I'm assuming you're generating an excel file from data in the database and it's just a matter of it being enough data that it takes ~5 seconds to create the file and send it to the user.

Related

How to use Generic Repository, Unit of Work pattern with multiple EDMX for Multiple Databases

I'm on the development of a project which has 3 MS SQL Server databases.
I use,
EntityFramework 6.4.0,
Generic Repository,
Repository Unit of Work Pattern.
ASP.Net MVC
Multipe Entity Data Models
I need to save changes into multiple tables in multiple DBs as one transaction (Using one SaveChanges() method)
I have done projects using the above patterns using a single database, single UoW class, single generic repository. Now I'm stuck with multiple DBs.
My generic repository Class as follows
public class GenericRepository<TEntity, TContext> : IGenericRepository<TEntity, TContext>
where TEntity : class
where TContext : DbContext
{
internal DbContext context;
internal DbSet<TEntity> dbSet;
public GenericRepository(DbContext context)
{
this.context = context;
this.dbSet = context.Set<TEntity>();
}
public IEnumerable<TEntity> GetAll()
{
return context.Set<TEntity>().ToList();
}
public virtual IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
IQueryable<TEntity> query = dbSet;
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}
public virtual TEntity GetByID(object id)
{
return dbSet.Find(id);
}
public virtual void Insert(TEntity entity)
{
dbSet.Add(entity);
}
public virtual void InsertRange(IList<TEntity> entityList)
{
dbSet.AddRange(entityList);
}
public virtual void Delete(object id)
{
TEntity entityToDelete = dbSet.Find(id);
Delete(entityToDelete);
}
public virtual void DeleteRange(IEnumerable<TEntity> lstEntity)
{
dbSet.RemoveRange(lstEntity);
}
public virtual void Delete(TEntity entityToDelete)
{
if (context.Entry(entityToDelete).State == EntityState.Detached)
{
dbSet.Attach(entityToDelete);
}
dbSet.Remove(entityToDelete);
}
public virtual void Update(TEntity entityToUpdate)
{
dbSet.Attach(entityToUpdate);
context.Entry(entityToUpdate).State = EntityState.Modified;
}
}
My Unit Of Work class as follows;
public class UnitOfWork<TContext> : IDisposable where TContext : DbContext, new()
{
private DbContext context;
private IGenericRepository<TBL_RC_DATA_PHL_EmployeeData_SIN, DbContext> tbl_Data_EmployeeData_sin;
public IGenericRepository<TBL_RC_DATA_PHL_EmployeeData_SIN, DbContext> Tbl_Data_EmployeeData_sin
{
get
{
if (this.tbl_Data_EmployeeData_sin == null)
{
this.tbl_Data_EmployeeData_sin = new GenericRepository<TBL_RC_DATA_PHL_EmployeeData_SIN, DbContext>(context);
}
return tbl_Data_EmployeeData_sin;
}
}
private IGenericRepository<TBL_RC_DATA_PHL_EmployeeData_PHL, DbContext> tbl_Data_EmployeeData_phl;
public IGenericRepository<TBL_RC_DATA_PHL_EmployeeData_PHL, DbContext> Tbl_Data_EmployeeData_phl
{
get
{
if (this.tbl_Data_EmployeeData_phl == null)
{
this.tbl_Data_EmployeeData_phl = new GenericRepository<TBL_RC_DATA_PHL_EmployeeData_PHL, DbContext>(context);
}
return tbl_Data_EmployeeData_phl;
}
}
private IGenericRepository<TBL_RC_DATA_PHL_EmployeeData_ENG, DbContext> tbl_Data_EmployeeData_eng;
public IGenericRepository<TBL_RC_DATA_PHL_EmployeeData_ENG, DbContext> Tbl_Data_EmployeeData_eng
{
get
{
if (this.tbl_Data_EmployeeData_eng == null)
{
this.tbl_Data_EmployeeData_eng = new GenericRepository<TBL_RC_DATA_PHL_EmployeeData_ENG, DbContext>(context);
}
return tbl_Data_EmployeeData_eng;
}
}
public UnitOfWork()
{
context = new TContext();
}
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);
}
}
My service layer classes as follows
public class svcEmployeeData
{
private UnitOfWork<DAL.RCPlusEntities> unitOfWork;
public svcEmployeeData()
{
unitOfWork = new UnitOfWork<DAL.RCPlusEntities>();
}
public void updateEmployees(TBL_RC_DATA_PHL_EmployeeData_SIN sin, TBL_RC_DATA_PHL_EmployeeData_ENG eng)
{
//updating Business Logic goes here
unitOfWork.Tbl_Data_EmployeeData_sin.Update(sin);
unitOfWork.Tbl_Data_EmployeeData_eng.Update(eng);
unitOfWork.Save();
}
}
How to transform this architecture into 3 EDMXes by 3 databases...?
Thanks in advance.
Below is based on the assumption that structure of all three databases is same. If structure is different, I will recommend to avoid this much generalization. Instead, maintain the different data access layer for each database.
The reason why you cannot reuse same class for other database is that, you are creating instance of DbContext in UnitOfWork constructor like below:
private DbContext context;
public UnitOfWork()
{
context = new TContext();
}
Instead, if you accept the instance of DbContext in constructor, I think the same should work with any database.
private DbContext context;
public UnitOfWork(DbContext context)
{
this.context = context;
}
Of-course, further you need to alter your service layer to inject the correct instance of DbContext.
By the way, re-think before using too much wrappers over wrappers. Please refer to this post for more details.
Now, as you said:
I need to save changes into multiple tables in multiple DBs as one transaction (Using one SaveChanges() method)
and commented:
I need to handle transactions. If any error occurred when updating the 2nd DB table, Changes on 1st DB tables should be rolled back. Easy to do it with one UnitOwWork.Save()
Consider the point raised by #Holger in comment.
Note: Cross-Database Transactions on SQL-Server are only supported on the same server. – Holger
The SaveChanges method will not help you here. Instead, you can use TransactionScope in service layer to commit/rollback entire batch across the databases.
So, in service layer, it looks something like below:
using (TransactionScope scope = new TransactionScope())
{
//Create unitOfWorkDb1 here
unitOfWorkDb1.DoSomething();
unitOfWorkDb1.Save();
//Create unitOfWorkDb2 here
unitOfWorkDb2.DoSomething();
unitOfWorkDb2.Save();
scope.Complete();
}
Please refer to this and this post about transaction scope. Also, this article is very informative.

Unit of Work and Repository Pattern in MVC controller constructor injection using Unity not doing any changes to database

1.) I am a building new MVC application with 3 tier project architecture having:
Common Project with entities
Business/Service holding interfaces and logic classes and
Data holding repositories, interfaces, DbContext and UnitOfWorkclasses. I am using Unity Config to register dependencies, DbContext and UnitOfWork.
2.) I created a repository for each table and one generic repository that does basic CRUD operations.
Example Entity residing in Common Project:
public class MenuSecd
{
[Key, Column(Order = 0)]
public string prg_module { get; set; }
[Key, Column(Order = 1)]
public int prg_numb { get; set; }
[Key, Column(Order = 2)]
public string menu_level { get; set; }
}
My generic Entity Logic Interface residing in Business Project:
public interface IEntityLogic<T> : ILogic where T : class
{
void Create(T entity);
void Delete(T entity);
IEnumerable<T> GetAll();
void Update(T entity);
}
Entity Logic Class:
public abstract class EntityLogic<T> : IEntityLogic<T> where T : class
{
IUnitOfWork _unitOfWork;
IGenericRepository<T> _repository;
public EntityLogic(IUnitOfWork unitOfWork, IGenericRepository<T> repository)
{
_unitOfWork = unitOfWork;
_repository = repository;
}
public virtual void Create(T entity)
{
if(entity == null)
{
throw new ArgumentNullException(nameof(entity));
}
_repository.Add(entity);
_unitOfWork.Commit();
}
}
Example Business Logic class for the entity defined in Common Project:
public class MenuSecdLogic : EntityLogic<MenuSecd>, IMenuSecdLogic
{
IUnitOfWork _unitOfWork;
IMenuSecdRepository _repository;
public MenuSecdLogic(IUnitOfWork unitOfWork, IMenuSecdRepository repository) : base(unitOfWork, repository)
{
_unitOfWork = unitOfWork;
_repository = repository;
}
public List<MenuSecd> GetItems(string usrgrp_id)
{
return _repository.GetItems(usrgrp_id);
}
}
My Generic Repository in Data Project looks like:
public abstract class GenericRepository<T> : IGenericRepository<T> where T : class
{
protected DbContext _entities;
protected readonly IDbSet<T> _dbset;
public GenericRepository(DbContext context)
{
_entities = context;
_dbset = context.Set<T>();
}
public virtual T Add(T entity)
{
return _dbset.Add(entity);
}
public virtual T Delete(T entity)
{
return _dbset.Remove(entity);
}
public virtual void Edit(T entity)
{
_entities.Entry(entity).State = EntityState.Modified;
}
}
Repository Interface for the same Entity is defined as:
public interface IMenuSecdRepository : IGenericRepository<MenuSecd>
{
List<MenuSecd> GetItems(string usrgrp_id);
}
Repository class for above mentioned interface is:
public class MenuSecdRepository : GenericRepository<MenuSecd>, IMenuSecdRepository
{
public MenuSecdRepository(DbContext context) : base(context)
{
}
public List<MenuSecd> GetItems(string usrgrp_id)
{
return _dbset.Where(m => m.usrgrp_id == usrgrp_id).ToList();
}
}
My DbContext looks like:
public class DashboardContext : DbContext
{
public DashboardContext() : base("Name=DBEntities")
{
}
public DbSet<MenuSecd> menusecd { get; set; }
public override int SaveChanges()
{
var modifiedEntries = ChangeTracker.Entries().Where(x => x.State == EntityState.Added || x.State == EntityState.Modified);
//future custom implementation like auditing
return base.SaveChanges();
}
}
My UnitOfWork looks like:
public sealed class UnitOfWork : IUnitOfWork
{
private DbContext _dbContext;
public UnitOfWork(DbContext context)
{
_dbContext = context;
}
public int Commit()
{
return _dbContext.SaveChanges();
}
//disposes current object
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
//disposes all external resources
private void Dispose(bool disposing)
{
if (disposing)
{
if (_dbContext != null)
{
_dbContext.Dispose();
_dbContext = null;
}
}
}
}
My controller:
public class DashController : Controller
{
private readonly IMenuSecdLogic _menuSecdLogic;
public DashController(IMenuSecdLogic menuSecdLogic)
{
_menuSecdLogic = menuSecdLogic;
}
public void Save()
{
var menuSecd = new menuSecd();
//populate all fields for entity MenuSecd
_menuSecdLogic.Create(menuSecd);
}
}
My Unity Config in App_Start looks like :
public static void RegisterTypes(IUnityContainer container)
{
container.RegisterType<DbContext, DashboardContext>();
container.RegisterType<IUnitOfWork, UnitOfWork>();
container.RegisterType(typeof(IGenericRepository<>), typeof(GenericRepository<>));
container.RegisterType<IMenuSecdLogic, MenuSecdLogic>();
container.RegisterType<IMenuSecdRepository, MenuSecdRepository>();
}
So when run above project everything builds fine. But when controller calls:
_menuSecdLogic.Create(menuSecd);
It reaches Entity Logic and adds a new entity to _repository at :
_repository.Add(entity);
_unitOfWork.Commit();
But when it hits next line to actually save it to database which is :
return _dbContext.SaveChanges();
in UnitOfWork.cs file.
It comes to dashboardContext where it finally have to save it to database. But it does execute :
var modifiedEntries = ChangeTracker.Entries().Where(x => x.State == EntityState.Added || x.State == EntityState.Modified);
return base.SaveChanges();
But nothing changes in database. There will be no record in database. To test I have added modifiedEntries to see if it is in context or not. By the time control reaches this point I see no modified entries at all. But in EntityLogic.cs it does add a new entity to local entities in repository.
I am not sure what is happening with UnitOfWork here. I ran SQL Profiler to see if it is hitting database or not. Interestingly it is not hitting database at all. But if my make following changes to EntityLogic like this:
public virtual void Create(T entity)
{
if(entity == null)
{
throw new ArgumentNullException(nameof(entity));
}
_repository.Add(entity);
_repository.Save();
//_unitOfWork.Commit();
}
It hits Database and records gets saved fine. But I am not getting why it is neither tracking changes nor hitting database if I use _unitOfWork.Commit() which I want to do. Please help.
It looks like your issue is the scope of your DbContext. Your UnitOfWork and GenericRepository<T> classes are getting different instances.
Not super familiar with Unity, but it looks like you want to use something like this for your DbContext registration:
container.RegisterType<DbContext, DashboadContext>(new PerRequestLifetimeManager());
This will create a single DashboardContext for each request, and your UnitOfWork and GenericRepository<T> classes will be working within the same context.

In Memory Database with.Net Core not adding data to entity collection

I am creating xunit test with EF Core 2.0.0 and InMemory 2.0.0. I noticed that the entities are not being added in the context. However it is being added at context..Local
Below is the snippet of the code
public UnitOfWorkTest()
{
_appointment = new Appointment
{
AppointmentType = AppointmentTypes.EyeTest,
AppProgress = Appointment.Confirmed,
BranchIdentifier = "MEL",
DateAdded = DateTime.Now,
Duration = 30,
Resid = "KAI",
};
}
public MyDbContext InitContext()
{
var options = new DbContextOptionsBuilder<MyDbContext>()
.UseInMemoryDatabase("Add_writes_to_database")
.Options;
return new MyDbContext(options);
}
public async Task UnitOfWork_Transaction_Test()
{
using (var context = InitContext())
{
using (var unitOfWork = new UnitOfWork(context))
{
context.Appointment.Add(_appointment);
await unitOfWork.Commit();
Assert.True(context.Appointment.Local.Count == 1);
}
}
}
Unit of Work
public sealed class UnitOfWork : IUnitOfWork
{
private IDbContext _dbContext;
public UnitOfWork(IDbContext context)
{
_dbContext = context;
}
public async Task<int> Commit()
{
// Save changes with the default options
return await _dbContext.SaveChangesAsync();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (disposing)
{
if (_dbContext != null)
{
_dbContext.Dispose();
_dbContext = null;
}
}
}
}
IDbContext
public interface IDbContext : IDisposable
{
DbSet<TEntity> Set<TEntity>() where TEntity : class;
EntityEntry<TEntity> Entry<TEntity>(TEntity entity) where TEntity : class;
EntityEntry Entry(object entity);
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken));
}
context.Appointment always return empty list/null but I can see the added entity in context.Appointment.Local
Any idea why this happens? How do I get the entity added in Appointment collection and not in Appointment.Local collection?
After this line context.Appointment.Add(_appointment); try to save changes in your context context.SaveChanges().I hope it will help.
In-memory database is faking a test double. Your current test relies on the implementation details of "DbSet<T> properties" within this test double.
I think implementation details is the answer to why Set<T>() works inplace:
Assert.True(context.Set<Appointment>().Count == 1);
DbSet<T> properties are there for convenience, rather than to implement data access. It is not even obligatory to have - all of - them in the context class (until you provide navigation properties in entity classes).
You can also check: Using DbContext Set<T>() instead of exposing on the context for clarification.

EF Code First An entity object cannot be referenced by multiple instances of IEntityChangeTracker

I have a problem for which lots of answers exist. The exception I've gotten says:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker.
As I've surfed the web and stack over flow pages, I've reached to this point that the instance of the DbContext should be only one in each UnitOfWork or the previous one must be detached. It sounds reasonable! But, my problem is that I have no idea where in my code (posted below) I have to place the modifications.
I've been trying to use Generic Repository and Unit of Work pattern plus EF code first approach.
public abstract class Repository<T> : IRepository<T> where T : BaseEntity
{
protected DbContext EntityContext;
protected readonly IDbSet<T> DbSet;
protected Repository(DbContext context)
{
EntityContext = context;
DbSet = context.Set<T>();
}
public virtual IEnumerable<T> GetList()
{
return DbSet.AsEnumerable();
}
public virtual T Add(T entity)
{
return DbSet.Add(entity);
}
}
The generic Unit of Work:
public sealed class UnitOfWork : IUnitOfWork
{
private DbContext _context;
public UnitOfWork(DbContext context)
{
_context = context;
}
public int Commit()
{
return _context.SaveChanges();
}
private void Dispose(bool disposing)
{
if (!disposing) return;
if (_context == null) return;
_context.Dispose();
_context = null;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
The client repository
public class ClientRepository : Repository<Client>, IClientRepository
{
public ClientRepository(DbContext context)
: base(context)
{
}
public override Client Add(Client entity)
{
return DbSet.Add(entity); // <-- Here the exception comes
}
}
This is the main part of the 'GenerateClients' method placed inside the 'DataGenerator' project.
var countries = container.Resolve<ICountryService>().GetList().ToList();
if (!countries.Any())
{
throw new InvalidDataGeneratorException(Strings.NoCountryToCreateClient);
}
var clientService = container.Resolve<IClientService>();
clientService.Create(new Client
{
Name = "Dell",
CountryId = countries[0].Id,
Country = countries[0],
AddressLineOne = "76-98 Victoria Street",});
And here the ClientService which uses the ClientRepository:
public class ClientService : Service<Client>, IClientService
{
IUnitOfWork UnitOfWork { get; set; }
private readonly IClientRepository _clientRepository;
private readonly ICalculatorService _calculatorService;
public ClientService(IClientRepository clientRepository,
IUnitOfWork unitOfWork,
ICalculatorService calculatorService)
: base(clientRepository, unitOfWork)
{
UnitOfWork = unitOfWork;
_calculatorService = calculatorService;
_clientRepository = clientRepository;
}
public override void Create(Client client)
{
_clientRepository.Add(client);
_clientRepository.Save();
}
}
Please note that some of the methods have been removed in order to minimize the length of the question.
Actually, when I run my Data Generator project which is of type Console, preceding to adding Clients, a number of Countries have been added with no problem, but as it comes to the Client service and it goes down into the ClientRepository, the exception raises up.
I don't know where should I place the modifications to avoid the exception. Many thanks
does Client have a relation to country and are you adding it as Client.Country = RELATED_COUNTRY perhaps it's trying to add the same country twice, I usually use Client.CountryId = ID_OF_COUNTRY

AutoFac DbContext issue - cannot be used while the model is creating

I'm having a few issues getting started with AutoFac and IoC. We've got a working application however, I'm starting from scratch with this one and can't see where the differences between the two are.
I am testing this with a simple AJAX page which is calling the Service layer via a ServiceStack API. When using MockRepositories this works fine so I know that side of things is working.
However when I replace the mocks with ones that use Entity Framework, although all the registrations appear to be correct and working, I get the error "The context cannot be used while the model is being created."
I have included my code below:
public class SomeObject
{
public int Id { get; set; }
}
public class IoCExampleContext : DbContext, IIoCExampleContext
{
public IDbSet<SomeObject> SomeObjects { get; set; }
static IoCExampleContext()
{
Database.SetInitializer(new IoCExampleDatabaseInitilizer());
}
public IoCExampleContext(string connectionStringName)
: base(connectionStringName)
{
Configuration.ProxyCreationEnabled = false;
}
public IoCExampleContext()
: this("name=IoCExample")
{}
public string ConnectionString
{
get { return Database.Connection.ConnectionString; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
BuildModels(modelBuilder);
}
private void BuildModels(DbModelBuilder builder)
{
var typeToUse = typeof(SomeObjectModelBuilder);
var namespaceToUse = typeToUse.Namespace;
var toReg = Assembly
.GetAssembly(typeToUse)
.GetTypes()
.Where(type => type.Namespace != null && type.Namespace.StartsWith(namespaceToUse))
.Where(type => type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>));
foreach (object configurationInstance in toReg.Select(Activator.CreateInstance))
{
builder.Configurations.Add((dynamic)configurationInstance);
}
}
}
public class IoCExampleDatabaseInitilizer : CreateDatabaseIfNotExists<IoCExampleContext>
{
protected override void Seed(IoCExampleContext context)
{
}
}
public interface IRepository<TEntity> where TEntity : class
{
IQueryable<TEntity> GetQuery();
IEnumerable<TEntity> GetAll();
IEnumerable<TEntity> Where(Expression<Func<TEntity, bool>> predicate);
// ...Various "standard" CRUD calls
}
public class GenericRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected DbContext _context;
private readonly DbSet<TEntity> _dbSet;
public GenericRepository(DbContext context)
{
_context = context;
_dbSet = _context.Set<TEntity>();
}
public IQueryable<TEntity> GetQuery()
{
return _dbSet;
}
public IEnumerable<TEntity> GetAll()
{
return GetQuery().AsEnumerable();
}
public IEnumerable<TEntity> Where(Expression<Func<TEntity, bool>> predicate)
{
return GetQuery().Where(predicate);
}
// ...Various "standard" CRUD calls
public void Dispose()
{
OnDispose(true);
}
protected void OnDispose(bool disposing)
{
if (disposing)
{
if (_context != null)
{
_context.Dispose();
_context = null;
}
}
}
}
public class DependencyBootstrapper
{
private ContainerBuilder _builder;
public IContainer Start()
{
_builder = new ContainerBuilder();
_builder.RegisterFilterProvider();
RegisterControllers();
return _builder.Build();
}
private void RegisterControllers()
{
RegisterAssembly(Assembly.GetExecutingAssembly());
_builder.RegisterModelBinderProvider();
RegisterPerLifetimeConnections();
RegisterRepositories();
RegisterServices();
}
private void RegisterAssembly(Assembly assembly)
{
_builder.RegisterModelBinders(assembly);
_builder.RegisterControllers(assembly);
}
private void RegisterRepositories()
{
_builder.RegisterGeneric(typeof(GenericRepository<>)).As(typeof(IRepository<>));
_builder.RegisterType<GenericRepository<SomeObject>>().As<IRepository<SomeObject>>();
//... More registrations
}
private void RegisterServices()
{
_builder.RegisterType<SomeObjectService>().As<ISomeObjectService>();
//... More registrations
}
private void RegisterPerLifetimeConnections()
{
const string connectionStringName = "IoCExample";
_builder.RegisterType<IoCExampleContext>()
.As<DbContext>()
.WithParameter("connectionStringName", connectionStringName)
.InstancePerLifetimeScope();
_builder.Register(c => new HttpContextWrapper(HttpContext.Current))
.As<HttpContextBase>();
}
}
I don't know if it's relevant but as we can't get access to the global.asax methods we're calling the bootstrapper through PreApplicationStartMethod.OnPreApplicationStart (which as far as I am aware, is pretty much the same thing as Application_Start).
What's a little concerning is that when I enable Multiple Active Result Sets on the connection string it works -which would suggest to me that I'm registering the DbContext incorrectly and it's spanning multiple contexts.
Can anyone spot where I'm going wrong?
The connection string is the issue. Ensure you've set it up correctly in your web/app.comfig.

Categories