I attempting to implement the Unit of Work and Repository Pattern in my ASP.NET MVC app as described here.
I was receiving the following error:
Value cannot be null.
Parameter name: entitySet
during a query. After doing some debugging, I noticed that my DBSet<T>
classes were throwing the following error:
{
"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."
}
System.SystemException { System.InvalidOperationException }
I have looked around Stack Overflow but cannot find a solution. I used the "Code First from Database Approach." I checked my connection string and it seems right. My version of Entity, as defined in the packages.config
file, is 6.1.3. I have tried commenting out some of the relationships within the DbContext
class. The definition of which is as follows:
public partial class BKTrainerContext : DbContext
{
public BKTrainerContext()
: base("name=BKTrainerContext")
{
}
public virtual DbSet<AccessLevel> AccessLevels { get; set; }
public virtual DbSet<BuildCardCategory> BuildCardCategories { get; set; }
public virtual DbSet<BuildCard> BuildCards { get; set; }
public virtual DbSet<Manager> Managers { get; set; }
public virtual DbSet<SliderImage> SliderImages { get; set; }
public virtual DbSet<StoreMessage> StoreMessages { get; set; }
public virtual DbSet<Store> Stores { get; set; }
public virtual DbSet<Video> Videos { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<AccessLevel>()
.HasMany(e => e.Managers)
.WithRequired(e => e.AccessLevel1)
.HasForeignKey(e => e.AccessLevel)
.WillCascadeOnDelete(false);
modelBuilder.Entity<BuildCardCategory>()
.HasMany(e => e.BuildCards)
.WithRequired(e => e.BuildCardCategory1)
.HasForeignKey(e => e.BuildCardCategory)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Manager>()
.HasMany(e => e.StoreMessages)
.WithRequired(e => e.Manager)
.HasForeignKey(e => e.MessageAuthor)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Manager>()
.HasMany(e => e.Stores)
.WithRequired(e => e.Manager)
.WillCascadeOnDelete(false);
modelBuilder.Entity<StoreMessage>()
.Property(e => e.MessageBody)
.IsUnicode(false);
modelBuilder.Entity<StoreMessage>()
.HasMany(e => e.Stores)
.WithMany(e => e.StoreMessages)
.Map(m => m.ToTable("MessagesStores").MapLeftKey("MessageID").MapRightKey("StoreID"));
}
}
This is my GenericRepo class
public class GenericRepo<T> : IGenericRepo<T> where T : class
{
internal BKTrainerContext dbContext;
internal DbSet<T> db;
public GenericRepo(BKTrainerContext context)
{
this.dbContext = context;
this.db = context.Set<T>();
}
public virtual IEnumerable<T> Get(Expression<Func<T, bool>> filter = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, string includeProperties = "")
{
IQueryable<T> query = db;
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 T GetEntity(object id)
{
return db.Find(id);
}
public virtual void Insert(T newEntity)
{
db.Add(newEntity);
}
public virtual void Delete(object id)
{
T enity = db.Find(id);
Delete(enity);
}
public virtual void Delete(T entity)
{
if (dbContext.Entry(entity).State == EntityState.Detached)
{
db.Attach(entity);
}
db.Remove(entity);
}
public virtual void Update(T entity)
{
db.Attach(entity);
dbContext.Entry(entity).State = EntityState.Modified;
}
}
The original error is thrown by the lines within Get();
those that call a query.xx
method.
My Unit of Work class is as follows:
public class UnitOfWork : IUnitOfWork
{
private BKTrainerContext dbContext = new BKTrainerContext();
private GenericRepo<BuildCard> buildCardRepo;
private GenericRepo<BuildCardCategory> buildCardCategoryRepo;
private GenericRepo<Manager> managerRepo;
private GenericRepo<StoreMessage> messageRepo;
private GenericRepo<SliderImage> sliderRepo;
private GenericRepo<Store> storeRepo;
private GenericRepo<Video> videoRepo;
private bool disposed = false;
public GenericRepo<BuildCard> BuildCardRepo
{
get
{
if (this.buildCardRepo == null) { buildCardRepo = new GenericRepo<BuildCard>(dbContext); }
return buildCardRepo;
}
}
public GenericRepo<BuildCardCategory> BuildCardCategoriesRepo
{
get
{
if (this.buildCardCategoryRepo == null) { buildCardCategoryRepo = new GenericRepo<BuildCardCategory>(dbContext); }
return buildCardCategoryRepo;
}
}
public GenericRepo<Manager> ManagerRepo
{
get
{
if (this.managerRepo == null) { managerRepo = new GenericRepo<Manager>(dbContext); }
return managerRepo;
}
}
public GenericRepo<StoreMessage> MessageRepo
{
get
{
if (this.messageRepo == null) { messageRepo = new GenericRepo<StoreMessage>(dbContext); }
return messageRepo;
}
}
public GenericRepo<SliderImage> SliderRepo
{
get
{
if (this.sliderRepo == null) { sliderRepo= new GenericRepo<SliderImage>(dbContext); }
return sliderRepo;
}
}
public GenericRepo<Store> StoreRepo
{
get
{
if (this.storeRepo == null) { storeRepo= new GenericRepo<Store>(dbContext); }
return storeRepo;
}
}
public GenericRepo<Video> VideoRepo
{
get
{
if (this.videoRepo == null) {videoRepo = new GenericRepo<Video>(dbContext); }
return videoRepo;
}
}
public void Save()
{
dbContext.SaveChanges();
}
protected virtual void Dispose(bool disposing) {
if (!this.disposed)
{
if (disposing)
{
dbContext.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
I am using Unity to inject it into my Controller.
public class HomeController : Controller
{
private IUnitOfWork worker;
public HomeController(IUnitOfWork unit)
{
this.worker = unit;
}
public ActionResult Index(bool failedLogin = false)
{
IndexViewModel vm = new IndexViewModel();
vm.invalidLogin = failedLogin;
return View(vm);
}
[HttpPost]
public ActionResult AttemptLogin(string userName, string password)
{
List<Store> stores = worker.StoreRepo.Get().ToList();
Store store = worker.StoreRepo.Get(u => u.Username == userName && u.Password == password).FirstOrDefault();
if (stores != null)
{
Session["UserLevel"] = 0;
Session["UserID"] = store.ID;
return RedirectToAction("Index", "User");
}
else
{
Manager manager = worker.ManagerRepo.Get(m => m.Username == userName && m.Password == password).FirstOrDefault();
if (manager != null)
{
Session["UserLevel"] = manager.AccessLevel;
Session["UserID"] = manager.ID;
return RedirectToAction("Index", "Admin");
}
else
{
return RedirectToAction("Index", new { failedLogin = true });
}
}
}
}
I would really appreciate any help. Thanks!
EDIT:
Injection configuration:
public class UnityConfig
{
#region Unity Container
private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
RegisterTypes(container);
return container;
});
/// <summary>
/// Gets the configured Unity container.
/// </summary>
public static IUnityContainer GetConfiguredContainer()
{
return container.Value;
}
#endregion
/// <summary>Registers the type mappings with the Unity container.</summary>
/// <param name="container">The unity container to configure.</param>
/// <remarks>There is no need to register concrete types such as controllers or API controllers (unless you want to
/// change the defaults), as Unity allows resolving a concrete type even if it was not previously registered.</remarks>
public static void RegisterTypes(IUnityContainer container)
{
// NOTE: To load from web.config uncomment the line below. Make sure to add a Microsoft.Practices.Unity.Configuration to the using statements.
// container.LoadConfiguration();
container.RegisterType<IUnitOfWork, UnitOfWork>();
// container.RegisterType<IProductRepository, ProductRepository>();
}
}
}
The UnityMVCActivator class
[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(BKBuildCard.App_Start.UnityWebActivator), "Start")]
[assembly: WebActivatorEx.ApplicationShutdownMethod(typeof(BKBuildCard.App_Start.UnityWebActivator), "Shutdown")]
namespace BKBuildCard.App_Start
{
/// <summary>Provides the bootstrapping for integrating Unity with ASP.NET MVC.</summary>
public static class UnityWebActivator
{
/// <summary>Integrates Unity when the application starts.</summary>
public static void Start()
{
var container = UnityConfig.GetConfiguredContainer();
FilterProviders.Providers.Remove(FilterProviders.Providers.OfType<FilterAttributeFilterProvider>().First());
FilterProviders.Providers.Add(new UnityFilterAttributeFilterProvider(container));
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
/// <summary>Disposes the Unity container when the application is shut down.</summary>
public static void Shutdown()
{
var container = UnityConfig.GetConfiguredContainer();
container.Dispose();
}
}
}
EDIT
I attempted to run the application by using a concrete implementation of the class, but the problem persists.
private UnitOfWork worker;
public HomeController() {
this.worker = new UnitOfWork();
}