Code first Migrations with IdentityUser gives an error - asp.net-mvc

I'm trying to use Code First Migrations with IdentityUser.
Here's my dbContext
public class JbDb : IdentityDbContext<User>
{
public JbDb()
: base(ConfigurationManager.ConnectionStrings["JbDb"].ConnectionString)
{
}
public IDbSet<User> User { get; set; }
public virtual IDbSet<T> DbSet<T>() where T : class
{
return Set<T>();
}
public virtual void Commit()
{
base.SaveChanges();
}
}
internal class Initialiser : CreateDatabaseIfNotExists<JbDb>
{
protected override void Seed(JbDb context)
{
context.SaveChanges();
}
}
Here's the code for User model
[Table("User", Schema = "Security")]
public class User:IdentityUser
{
public string UserId
{
get { return Id; }
}
public string Email { get; set; }
public bool IsEmailVerified { get; set; }
public bool IsDeleted { get; set; }
}
Connection String is like this:
<add name="JbDb" connectionString="Data Source=CENLP\HRMSQL;Initial Catalog=JbDb;Integrated Security=True" providerName="System.Data.SqlClient" />
but when i'm trying to enable the migrations, it gives an error saying
Checking if the context targets an existing database... System.InvalidOperationException: Multiple object sets per type are
not supported. The object sets 'User' and 'Users' can both contain
instances of type 'Jb.Model.Security.User'. at
System.Data.Entity.Internal.DbSetDiscoveryService.RegisterSets(DbModelBuilder
modelBuilder) at
System.Data.Entity.Internal.LazyInternalContext.CreateModelBuilder()
at
System.Data.Entity.Internal.LazyInternalContext.CreateModel(LazyInternalContext
internalContext) at
System.Data.Entity.Internal.RetryLazy2.GetValue(TInput input) at
System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
at
System.Data.Entity.Internal.InternalContext.CreateObjectContextForDdlOps()
at System.Data.Entity.Database.Exists() at
Microsoft.AspNet.Identity.EntityFramework.IdentityDbContext1.IsIdentityV1Schema(DbContext
db) at
Microsoft.AspNet.Identity.EntityFramework.IdentityDbContext1..ctor(String
nameOrConnectionString, Boolean throwIfV1Schema) at
Microsoft.AspNet.Identity.EntityFramework.IdentityDbContext1..ctor(String
nameOrConnectionString) at Jb.DataAccess.JbDb..ctor() in
d:\CENT-Jb\Jb\Jb.DataAccess\JbDb.cs:line 10
--- End of stack trace from previous location where exception was thrown --- at
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at
System.Data.Entity.Infrastructure.DbContextInfo.CreateInstance() at
System.Data.Entity.Infrastructure.DbContextInfo..ctor(Type
contextType, DbProviderInfo modelProviderInfo, AppConfig config,
DbConnectionInfo connectionInfo, Func`1 resolver) at
System.Data.Entity.Migrations.DbMigrator..ctor(DbMigrationsConfiguration
configuration, DbContext usersContext, DatabaseExistenceState
existenceState) at
System.Data.Entity.Migrations.DbMigrator..ctor(DbMigrationsConfiguration
configuration) at
System.Data.Entity.Migrations.Design.MigrationScaffolder..ctor(DbMigrationsConfiguration
migrationsConfiguration) at
System.Data.Entity.Migrations.Design.ToolingFacade.ScaffoldRunner.Run()
at System.AppDomain.DoCallBack(CrossAppDomainDelegate
callBackDelegate) at
System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
at System.Data.Entity.Migrations.Design.ToolingFacade.Run(BaseRunner
runner) at
System.Data.Entity.Migrations.Design.ToolingFacade.ScaffoldInitialCreate(String
language, String rootNamespace) at
System.Data.Entity.Migrations.EnableMigrationsCommand.<>c__DisplayClass2.<.ctor>b__0()
at
System.Data.Entity.Migrations.MigrationsDomainCommand.Execute(Action
command) Multiple object sets per type are not supported. The object
sets 'User' and 'Users' can both contain instances of type
'Jb.Model.Security.User'..
It will be really helpful if one can point me out what is wrong here

[Table("Users", Schema = "Security")]

Related

How do I include a [NotMapped] property in an EF business object without getting FirstChance IndexOutofRangeException?

I was wondering why my XAF WinForms EF application was slow loading a detail view.
Then I learned how to capture FirstChance Exceptions and discovered I was experiencing an IndexOutOfRange exception as described here
Sometimes I want to include a non mapped property in my business object such as Job in example.
public class OrderLineResult
{
public int LineId { get; set; }
public int Quantity { get; set; }
public string Description { get; set; }
[Browsable(false)] public int JobId { get; set; }
[NotMapped] [Browsable(false)] public virtual Job Job { get; set; }
}
And I have a method to get the data inside the OrderLineResult class
public static OrderLineResult[] GetData(int headId)
{
using var connect = new MyDbContext()
const string sql =
#"SET NOCOUNT ON;
create table #temp( JobId int, Quantity int, LineId int, Description )
/* code to populate the table */
select JobId,LineId,Quantity, Description from #temp"
var results = connect.Database.SqlQuery<OrderLineResult>(sql,headId).ToArray();
return results.ToArray();
}
}
Yet the IndexOutOfRange exception occurs for the Job property.
The call stack is
System.IndexOutOfRangeException
Job
at MyApp.Module.Win.Controllers.ToDoList.TaskActionController.<>c.<actExceptions_Execute>b__34_0(Object sender, FirstChanceExceptionEventArgs e)
at System.Data.ProviderBase.FieldNameLookup.GetOrdinal(String fieldName)
at System.Data.SqlClient.SqlDataReader.GetOrdinal(String name)
at System.Data.Entity.Core.Query.InternalTrees.ColumnMapFactory.TryGetColumnOrdinalFromReader(DbDataReader storeDataReader, String columnName, Int32& ordinal)
at System.Data.Entity.Core.Query.InternalTrees.ColumnMapFactory.CreateColumnMapFromReaderAndClrType(DbDataReader reader, Type type, MetadataWorkspace workspace)
at System.Data.Entity.Core.Objects.ObjectContext.InternalTranslate[TElement](DbDataReader reader, String entitySetName, MergeOption mergeOption, Boolean streaming, EntitySet& entitySet, TypeUsage& edmType)
at System.Data.Entity.Core.Objects.ObjectContext.ExecuteStoreQueryInternal[TElement](String commandText, String entitySetName, ExecutionOptions executionOptions, Object[] parameters)
at System.Data.Entity.Core.Objects.ObjectContext.<>c__DisplayClass186_0`1.<ExecuteStoreQueryReliably>b__1()
at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func`1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)
at System.Data.Entity.Core.Objects.ObjectContext.<>c__DisplayClass186_0`1.<ExecuteStoreQueryReliably>b__0()
at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1 operation)
at System.Data.Entity.Core.Objects.ObjectContext.ExecuteStoreQueryReliably[TElement](String commandText, String entitySetName, ExecutionOptions executionOptions, Object[] parameters)
at System.Data.Entity.Core.Objects.ObjectContext.ExecuteStoreQuery[TElement](String commandText, ExecutionOptions executionOptions, Object[] parameters)
at System.Data.Entity.Internal.LazyEnumerator`1.MoveNext()
at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
at MyApp.Module.BusinessObjects.NonPersistedBusinessObjects.OrderLineResult.GetData(Int32 headId)
I am using EntityFramework 6.4.4 and .Net Framework 4.7.2
This feels like a cludge,
I added
,null as job
to the last select statement

Ninject: How to access root object of NamedScope from factory

In my application I am using Ninject and the NamedScopeExtension. One of the objects deeper in the object graph needs access to the root object that defined the named scope. It seems to me that DefinesNamedScope() does not also imply InNamedScope() and instead a new root object is created when I request the root.
Example:
using System;
using Ninject;
using Ninject.Extensions.NamedScope;
using Ninject.Syntax;
namespace NInjectNamedScope
{
public interface IScopeRoot
{
Guid Guid { get; }
void DoSomething();
}
public interface IFactory
{
Guid Guid { get; }
IOther CreateOther();
}
public interface IOther
{
void SayHello();
}
internal class ScopeRoot : IScopeRoot
{
private readonly IFactory m_factory;
private readonly IResolutionRoot m_kernel;
public Guid Guid { get; private set; }
public ScopeRoot(IFactory factory, IResolutionRoot kernel)
{
m_factory = factory;
m_kernel = kernel;
Guid = Guid.NewGuid();
}
public void DoSomething()
{
Console.WriteLine("ScopeRoot.DoSomething(): Entering");
Console.WriteLine("ScopeRoot GUID: {0}", Guid);
Console.WriteLine("IFactory GUID: {0}", m_factory.Guid);
var other = m_factory.CreateOther();
Console.WriteLine("ScopeRoot.DoSomething(): Other created");
other.SayHello();
Console.WriteLine("ScopeRoot.DoSomething(): Exiting");
}
}
internal class Factory : IFactory
{
private IResolutionRoot m_kernel;
public Guid Guid { get; private set; }
public Factory(IResolutionRoot kernel)
{
m_kernel = kernel;
Guid = Guid.NewGuid();
}
public IOther CreateOther()
{
return m_kernel.Get<IOther>();
}
}
internal class Other : IOther
{
private readonly IScopeRoot m_root;
private readonly IFactory m_factory;
public Other(IScopeRoot root, IFactory factory)
{
m_root = root;
m_factory = factory;
}
public void SayHello()
{
Console.WriteLine("Other.SayHello(): Hello");
Console.WriteLine("Our IScopeRoot has GUID: {0}", m_root.Guid);
Console.WriteLine("Our IFactory has GUID: {0}", m_factory.Guid);
}
}
public class MainClass
{
public static void Main(string[] args)
{
var kernel = new StandardKernel();
kernel.Bind<IScopeRoot>().To<ScopeRoot>().DefinesNamedScope("RootScope");
kernel.Bind<IFactory>().To<Factory>().InNamedScope("RootScope");
kernel.Bind<IOther>().To<Other>().InNamedScope("RootScope");
var root = kernel.Get<IScopeRoot>();
root.DoSomething();
Console.ReadKey();
}
}
}
In this example, Other is receiving the same instance of Factory as the root does, but a new instance of ScopeRoot is created instead of injecting the existing instance that defined the named scope.
How can I access the root of the named scope in a factory? Please note that this example is simplified. In reality, there are several layers of objects between the scope root and the factory method, so I cannot simply pass this to the factory.
Yes you're right, out of the box Ninject can't do .DefinesNamedScope().InNamedScope(). Except maybe for late "creation" (factory, lazy) this couldn't work anyway, because it would create a cyclic dependency.
The simplest way to achieve what you want is to create a "root of the root"... well just one class ActualRoot which is bound with DefinesNamedScope() and gets an IRootScope injected, which again will be bound with .InNamedScope(). The bad thing about this is, that you will need to inject/Get<> an ActualRoot instead of a IRootScope.
As far as i remember, what you can also do instead, is:
Bind<IRootScope>().To<RootScope>()
.InNamedScope(scopeName);
and then retrieve it as follows:
IResolutionRoot.Get<IRootScope>(new NamedScopeParameter(scopeName));
This way you don't need a DefinesNamedScope().

AutoMapper+xUnit: Missing type map configuration or unsupported mapping

I cannot figure this one out. I have a N-Tier ASP.MVC application and I am writing my first Unit Test and it seems to fail on my AutoMapper configuration. I have used AutoMapper a million times and never had any problems using it.
I'm sure I am missing something simple, but I have been staring at this for 24 hours now.
Class Library: APP.DOMAIN
public class User : IEntity<int>
{
public int Id { get; set; }
[StringLength(20), Required]
public string UserName { get; set; }
}
Class Library: APP.SERVICE
References App.Domain
public class UserViewModel
{
public int Id { get; set; }
public string UserName { get; set; }
}
I have my AutoMapper bootstrapper in the service layer.
public static class AutoMapperBootstrapper
{
public static void RegisterMappings()
{
Mapper.CreateMap<User, UserViewModel>();
}
}
UserService.cs
public class UserService : IUserService
{
private readonly IUserRepository _userRepository;
public UserService(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public List<UserViewModel> GetUsers()
{
var users = _userRepository.GetAll();
if (users == null)
{
throw new Exception("No users found.");
}
return Mapper.Map<List<UserViewModel>>(users); // FAILS ON AUTOMAPPER
}
}
ASP.MVC Layer: APP.WEB
References App.Service
private void Application_Start(object sender, EventArgs e)
{
// Register AutoMapper
AutoMapperBootstrapper.RegisterMappings();
Mapper.AssertConfigurationIsValid();
// Code that runs on application startup
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
Unit Test Layer:
public class TestUserRepository :IUserRepository
{
public IEnumerable<User> GetAll()
{
var users = new List<User>()
{
new User { Id = 1, UserName = "Mary"},
new User { Id = 2, UserName = "Joe"}
};
return users;
}
}
public class UserServiceTest
{
private IUserService _userService;
private readonly IUserRepository _userRepository;
public UserServiceTest()
{
_userRepository = new TestUserRepository();
}
[Fact]
public void GetUsers_Should_Return_Correct_Number_Of_Users()
{
// Arrange
_userService = new UserService(_userRepository);
// Act
var result = _userService.GetUsers(); // FAILS ON AUTOMAPPER
// Assert
Assert.True(result.Any(u => u.UserName == "Mary"));
}
}
Failing Test Message:
*** Failures ***
Exception
AutoMapper.AutoMapperMappingException: AutoMapper.AutoMapperMappingException : Missing type map configuration or unsupported mapping.
Mapping types:
User -> UserViewModel
App.Data.Model.User -> App.Service.ViewModels.UserViewModel
Destination path:
List`1[0]
Source value:
App.Data.Model.User
at App.Service.Services.UserService.GetUsers() in D:\Repositories\App\App.Service\Services\UserService.cs:line 36
at App.Tests.Service.Tests.UserServiceTest.GetUsers_Should_Return_Correct_Number_Of_Users() in D:\Repositories\App\App.Tests\Service.Tests\UserServiceTest.cs:line 34
A little late to the party but have you tried setting the mapping before running the test?
public class UserServiceTest
{
public UserServiceTest()
{
// register the mappings before running the test
AutoMapperBootstrapper.RegisterMappings();
}
...
}
What we would need to do is Inject Custom Mapper Mock as given below. Add all those custom profiles that you have used for that particular class that you are unit testing and inject ConfigureMapper() in the Constructor of that class which is expecting IMapper Object
public IMapper ConfigureMapper()
{
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile<CustomProfile>();
cfg.AddProfile<UserCustomProfile>();
cfg.AddProfile<UserWorkProfile>();
});
return config.CreateMapper();
}
Hope this solves the issue.
I'm not sure what the problem is, it's been a while since I've last used AutoMapper, but I'm quite sure that the following will work:
return users.Select(Mapper.Map<UserViewModel>);
I have a problem with this line:
var authorDTO = mapper.Map<AuthorCreationDTO>(AuthorinsideDB);
So I change the version of Autormapper
from:
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="7.0.0" />
to
Version="6.0.0"
and it worked.

StructureMap, NHibernate and multiple databases

I'm working on an Asp.Net MVC 3 application using Fluent NHibernate. I'm just attempting to add an IoC container using StructureMap.
I have implemented a custom controller factory which uses StructureMap to create the controller and inject dependencies. Each controller constructor takes one or more services, which in turn take a DAO as constructor argument. Each DAO constructor takes an ISessionFactory.
For my StructureMap NHibernate registry I have the following:
internal class NHibernateRegistry : Registry
{
public NHibernateRegistry()
{
var connectionString = ConfigurationManager.ConnectionStrings["AppDb"].ConnectionString;
For<ISessionFactory>()
.Singleton()
.Use(x => new AppSessionFactory().GetSessionFactory(connectionString));
For<ISession>()
.HybridHttpOrThreadLocalScoped()
.Use(x => x.GetInstance<ISessionFactory>().OpenSession());
}
}
public class AppSessionFactory
{
public ISessionFactory GetSessionFactory(string connectionString)
{
return GetConfig(connectionString)
.BuildSessionFactory();
}
public static FluentConfiguration GetConfig(string connectionString)
{
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005.ConnectionString(x => x.Is(connectionString)))
.Mappings(
x => x.FluentMappings.AddFromAssemblyOf<AppEntity>());
}
}
This all works fine for a single database and single session factory. However the application uses multiple databases.
What is the best way to handle this?
Registering multiple session factories is easy - the problem is selecting the right one when you need it. For example, let's say we have some sort of laboratory that has multiple databases. Each lab has a Location and multiple Samples for that location. We could have a SampleRepository that models that. Each Location has a unique key to identify it (e.g. "LabX", "LabY", "BlackMesa"). We can use that unique key as the name of the database connection string in the app.config file. In this example, we would have three connection strings in the app.config file. Here's a sample connectionStrings section:
<connectionStrings>
<add name="LabX" connectionString="Data Source=labx;User ID=someuser;Password=somepassword"/>
<add name="LabY" connectionString="Data Source=laby;User ID=someuser;Password=somepassword"/>
<add name="BlackMesa" connectionString="Data Source=blackmesa;User ID=freemang;Password=crowbar"/>
</connectionStrings>
Thus, we need to have a unique session factory for each connection string. Let's create a NamedSessionFactory that wraps ISessionFactory:
public interface INamedSessionFactory
{
public string Name { get; } // The name from the config file (e.g. "BlackMesa")
public ISessionFactory SessionFactory { get; }
}
public class NamedSessionFactory : INamedSessionFactory
{
public string Name { get; private set; }
public ISessionFactory SessionFactory { get; private set; }
public NamedSessionFactory(string name, ISessionFactory sessionFactory)
{
Name = name;
SessionFactory = sessionFactory;
}
}
Now we need to modify your AppSessionFactory a bit. First off, what you've created is a session factory factory - that's not quite what we're looking for. We want to give our factory a location and get a session out of it, not a session factory. Fluent NHibernate is what gives us session factories.
public interface IAppSessionFactory
{
ISession GetSessionForLocation(string locationKey);
}
The trick here is accept a list of INamedSessionFactory objects in the constructor. StructureMap should give us all of the INamedSessionFactory objects that we've registered. We'll get to registration in a second.
public class AppSessionFactory : IAppSessionFactory
{
private readonly IList<INamedSessionFactory> _factories;
public AppSessionFactory(IEnumerable<INamedSessionFactory factories)
{
_factories = new List<INamedSessionFactory>(factories);
}
This is where the magic happens. Given a location key, we run through our list of factories looking for one with the same name as locationKey, then ask it to open a session and return it to the caller.
public ISession GetSessionForLocation(string locationKey)
{
var sessionFactory = _factories.Where(x => x.Name == locationKey).Single();
return sessionFactory.OpenSession();
}
}
Now let's wire this all together.
internal class NHibernateRegistry : Registry
{
public NHibernateRegistry()
{
We're going to loop through all of the connection strings in our app.config file (there would be three of them in this example) and register an INamedSessionFactory object for each one.
foreach (ConnectionStringSettings location in ConfigurationManager.ConnectionStrings)
{
For<INamedSessionFactory>()
.Singleton()
.Use(x => new NamedSessionFactory(
location.Name,
GetSessionFactory(location.ConnectionString));
}
We also need to register IAppSessionFactory.
For<IAppSessionFactory>()
.Singleton()
.Use<AppSessionFactory>();
}
You'll notice that we've moved this logic out of the factory class... These are helper methods for creating session factories from Fluent NHibernate.
private static ISessionFactory GetSessionFactory(string connectionString)
{
return GetConfig(connectionString)
.BuildSessionFactory();
}
public static FluentConfiguration GetConfig(string connectionString)
{
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005.ConnectionString(x => x.Is(connectionString)))
.Mappings(
x => x.FluentMappings.AddFromAssemblyOf<AppEntity>());
}
}
That should do it! Let's create a repository for getting at our samples...
public class SampleRepository
{
private readonly IAppSessionFactory _factory;
public SampleRepository(IAppSessionFactory factory)
{
_factory = factory;
}
public IEnumerable<Sample> GetSamplesForLocation(Location location)
{
using (ISession session = _factory.GetSessionForLocation(location.Key)
{
foreach (Sample sample in session.Query<Sample>())
yield return sample;
}
}
}
Now you can get a single instance of SampleRepository and use the GetSamplesForLocation method to pull samples from any of the three databases we have registered in app.config. Might want to avoid BlackMesa though. I understand there were problems there.
Are you sure this thing works? string ISessionFactory
public string ISessionFactory SessionFactory { get; private set; }
should this be
public interface INamedSessionFactory
{
ISessionFactory SessionFactory { get; set; }
string Name { get; }
}
public class NamedSessionFactory : INamedSessionFactory
{
public ISessionFactory SessionFactory { get; set; }
public string Name { get; private set; }
public NamedSessionFactory(string Name, ISessionFactory SessionFactory)
{
this.Name = Name;
this.SessionFactory = SessionFactory;
}
}

ASP.NET MVC 3 Application using Ninject, Entity Framework 4 Code-First CTP 5, Patterns

I've tried to build some base project with above technologies. I wanted maximum flexibility and testability so I tried to use patterns along the way to make this as a base for future projects. However, it seem
something is wrong or whatever and I really need help here. So i have two questions :
Is there anything wrong with my current code? I've applied patterns correctly? Any suggestions or recommendation that would lead me in the right direction?
Why do this code actually connect to the database, create it, but doesn't support insert even if I perform the corrects operation? (Look at the end of the post for details about this error) FIXED
I believe this could also help others since I haven't found enough information in order to make something up correctly. I am pretty sure lots of people try to do it the right way and are not sure like me if what I am doing is right.
I have two entities: Comment and Review
COMMENT
public class Comment
{
[Key]
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string Author { get; set; }
public virtual string Body { get; set; }
}
REVIEW
public class Review
{
[Key]
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string Author { get; set; }
public virtual string Body { get; set; }
public virtual bool Visible { get; set; }
public IEnumerable<Comment> Comments { get; set; }
}
I built up a base repository for each of them this way :
GENERIC REPOSITORY
public abstract class EFRepositoryBase<T> : IRepository<T> where T : class
{
private Database _database;
private readonly IDbSet<T> _dbset;
protected IDatabaseFactory DatabaseFactory { get; private set; }
protected Database Database { get { return _database ?? (_database = DatabaseFactory.Get()); } }
public EFRepositoryBase(IDatabaseFactory databaseFactory)
{
DatabaseFactory = databaseFactory;
_dbset = Database.Set<T>();
}
public virtual void Add(T entity)
{
_dbset.Add(entity);
}
public virtual void Delete(T entity)
{
_dbset.Remove(entity);
}
public virtual T GetById(long id)
{
return _dbset.Find(id);
}
public virtual IEnumerable<T> All()
{
return _dbset.ToList();
}
}
For specific operations, I use an interface:
public interface IReviewRepository : IRepository<Review> {
// Add specific review operations
IEnumerable<Review> FindByAuthor(string author);
}
So I am getting the generics operations from the abstract class plus the specific operations:
public class EFReviewRepository : EFRepositoryBase<Review>, IReviewRepository
{
public EFReviewRepository(IDatabaseFactory databaseFactory)
: base(databaseFactory)
{ }
public IEnumerable<Review> FindByAuthor(string author)
{
return base.Database.Reviews.Where(r => r.Author.StartsWith(author))
.AsEnumerable<Review>();
}
}
As you figured out, I also use a database factory will produce the database context :
DATABASE FACTORY
public class DatabaseFactory : Disposable, IDatabaseFactory
{
private Database _database;
public Database Get()
{
return _database ?? (_database = new Database(#"AppDb"));
}
protected override void DisposeCore()
{
if (_database != null)
_database.Dispose();
}
}
DISPOSABLE (Some extensions methods...)
public class Disposable : IDisposable
{
private bool isDisposed;
~Disposable()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!isDisposed && disposing)
{
DisposeCore();
}
isDisposed = true;
}
protected virtual void DisposeCore()
{
}
}
DATABASE
public class Database : DbContext
{
private IDbSet<Review> _reviews;
public IDbSet<Review> Reviews
{
get { return _reviews ?? (_reviews = DbSet<Review>()); }
}
public virtual IDbSet<T> DbSet<T>() where T : class
{
return Set<T>();
}
public Database(string connectionString)
: base(connectionString)
{
//_reviews = Reviews;
}
public virtual void Commit()
{
base.SaveChanges();
}
/*
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// TODO: Use Fluent API Here
}
*/
}
And to finish, I have my unit of work....
UNIT OF WORK
public class UnitOfWork : IUnitOfWork
{
private readonly IDatabaseFactory _databaseFactory;
private Database _database;
public UnitOfWork(IDatabaseFactory databaseFactory)
{
_databaseFactory = databaseFactory;
}
protected Database Database
{
get { return _database ?? (_database = _databaseFactory.Get()); }
}
public void Commit()
{
Database.Commit();
}
}
I also bound using Ninject the interfaces:
NINJECT CONTROLLER FACTORY
public class NinjectControllerFactory : DefaultControllerFactory
{
// A Ninject "Kernel" is the thing that can supply object instances
private IKernel kernel = new StandardKernel(new ReviewsDemoServices());
// ASP.NET MVC calls this to get the controller for each request
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
return null;
return (IController)kernel.Get(controllerType);
}
private class ReviewsDemoServices : NinjectModule
{
public override void Load()
{
// Bindings...
Bind<IReviewRepository>().To<EFReviewRepository>();
Bind<IUnitOfWork>().To<UnitOfWork>();
Bind<IDatabaseFactory>().To<DatabaseFactory>();
Bind<IDisposable>().To<Disposable>();
}
}
}
However, when I call in the constructor (the default action) ...
public class ReviewController : Controller
{
private readonly IReviewRepository _reviewRepository;
private readonly IUnitOfWork _unitOfWork;
public ReviewController(IReviewRepository postRepository, IUnitOfWork unitOfWork)
{
_reviewRepository = postRepository;
_unitOfWork = unitOfWork;
}
public ActionResult Index()
{
Review r = new Review { Id = 1, Name = "Test", Visible = true, Author = "a", Body = "b" };
_reviewRepository.Add(r);
_unitOfWork.Commit();
return View(_reviewRepository.All());
}
}
This seem to create the database but doesnt't insert anything in the database in EF4. It seem that I may figured out the problem.. while looking at the database object.. the connection state is closed and server version throw an exception of this kind :
ServerVersion = '(((System.Data.Entity.DbContext (_database)).Database.Connection).ServerVersion' threw an exception of type 'System.InvalidOperationException'
I am doing the right things? Is there anything wrong in what I've built ?
Also if you have recommandation about the code I posted, I would be glad. I am just trying to the learn the right way for building any kind of application in MVC 3. I want a good a start.
I use :
Entity Framework 4 with Code-First
ASP.NET MVC 3
Ninject as DI Container
SQL Server Express (not R2)
Visual Studio 2010 Web Express
Eww. This one was sneaky. Actually i don't know ninject much so i couldnt figure it out right away.
I found the solution for the SECOND question which was related to the error by finding that ninject actually shoot two instance of the DatabaseFactory, one for the repository and one for the unit of work. Actually, the error was not the problem. It was an internal error in the object database but its normal i think since im using Entity Framework.
The real problem was that Ninject was binding two different instance of IDatabaseFactory which lead to 2 connection open.
The review was added to the first set in _reviewRepostory which was using the first instance of the Database.
When calling commit on the unit of work.. it saved nothing due to the fact that the review wasnt on this database instance. In fact, the unit of work called the databasefactory which lead to creating a new instance since ninject sent a new instance of it.
To fix it simply use :
Bind<IDatabaseFactory>().To<DatabaseFactory>().InSingletonScope();
instead of
Bind<IDatabaseFactory>().To<DatabaseFactory>();
And now all the system work correctly!
Now, would love some answers regarding the first question which was if there anything wrong with my current code ? Ive applied patterns correctly ? Any suggestions or recommendation that would lead me in the right direction ?
One small observation: by having your EFRepositoryBase and IReviewRepository have methods that return an IEnumerable<> instead of an IQueryable<>, you prevent subsequent methods from adding filter expressions/constraints or projections or so on to the query. Instead, by using IEnumerable<>, you will do any subsequent filtering (e.g. using LINQ extension methods) on the full result set, rather than allowing those operations to affect and simplify the SQL statement that gets run against the datastore.
In other words, you are doing further filtering at the webserver level, not at the database level where it really belongs if possible.
Then again, this may be intentional - sometimes using IEnumerable<> is valid if you do want to prevent callers of your function from modifying the SQL that is generated, etc.

Resources