I am learning repository and unit of work patterns using generics and dependency injection. I have a persistent error that I have been churning on and I am clearly missing something fundamental.
I am getting the following error which whilst I understand that it is looking for a database table column called 'Id'
Because my Entity class inherits from BaseEntity I suspect this is the problem but I don't understand why and what I am best to do to fix it.
public class BaseEntity<T>
{
public T Id { get; set; }
}
Error returned
An exception of type 'System.Data.Entity.Core.EntityCommandExecutionException' >occurred in EntityFramework.SqlServer.dll but was not handled in user code
Additional information: An error occurred while executing the command >definition. See the inner exception for details.
Inner exception
{"Invalid column name 'Id'."}
To add some context I am using EF6 MVC5 and AutoFac for the Dependancy injection.
Entity class
(A column 'id' doesn't exist in the database - the key in the database is 'EmrgencyAttendanceId') and the Entity class also sets the key as 'EmrgencyAttendanceId' like so.
[Table("reporting.EDISRecords")]
public class EDISRecord : BaseEntity<int>
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int EmergencyAttendanceId { get; set; }
[StringLength(50)]
public string Hospital { get; set; }
[StringLength(20)]
public string URNumber { get; set; }
Controller action
The controller calls the GetRecord method of the EDISRecordService passing a Lambda expression. I use DbFunctions.TruncateTime because the database stores as date time and I only want to search on the date.
public ActionResult Search(string Date, string CaseNumber)
{
if (!string.IsNullOrEmpty(Date) || !string.IsNullOrEmpty(CaseNumber))
{
DateTime dt = DateTime.Parse(Date);
var EmergencyAttendance = _edisRecordService.GetRecord(m => (DbFunctions.TruncateTime(m.ArrivalDateTime) == dt) && (m.RTAIdentifier == CaseNumber));
//for initialising view model
SeperationSummaryViewModel model = new SeperationSummaryViewModel();
//assign values for view model
if (EmergencyAttendance != null)
{
if (EmergencyAttendance.DepartureDestination != null)
{
if (EmergencyAttendance.DepartureDestination.Substring(0, 1) == ".")
{
model.DepartureDestination = EmergencyAttendance.DepartureDestination.Substring(1);
}
else
{
model.DepartureDestination = EmergencyAttendance.DepartureDestination;
}
}
else
{
model.DepartureDestination = "Not recorded by Emergency Department";
}
if (EmergencyAttendance.InpatientAdmissionDiagnosis != null)
{
model.InpatientAdmissionDiagnosis = EmergencyAttendance.InpatientAdmissionDiagnosis;
}
else
{
model.InpatientAdmissionDiagnosis = "Not recorded by Emergency Department";
}
}
//send view model into UI (View)
return PartialView("_SeperationInformationPartialView", model);
}
else
{
if (string.IsNullOrEmpty(Date) || string.IsNullOrEmpty(CaseNumber))
{
return PartialView("Blank");
}
}
return PartialView("Error");
}
Service Class
The service class instantiates a unit of work and calls the repository Get method passing through the Lambda expression.
public class EDISRecordService : IEDISRecordService
{
private readonly IUnitOfWork<DataWarehouseDataManager> _unitOfWork;
public EDISRecordService(IUnitOfWork<DataWarehouseDataManager> unitOfWork)
{
_unitOfWork = unitOfWork;
}
public EDISRecord GetRecord(Expression<Func<EDISRecord, bool>> #where)
{
return _unitOfWork.EDISRecordRepository.Get(#where);
}
}
RepositoryBase class
The repository class inherits from the repository base class which impliments the Get method passing the lambda to 'Expression> #where'
public class RepositoryBase<TEntity> : Disposable, IRepository<TEntity>
where TEntity : class
{
private readonly DbContext _dataContext;
private IDbSet<TEntity> Dbset
{
get { return _dataContext.Set<TEntity>(); }
}
public RepositoryBase(DbContext dbContext)
{
_dataContext = dbContext;
}
public TEntity Get(Expression<Func<TEntity, bool>> #where)
{
return Dbset.Where(where).FirstOrDefault();
}
protected override void DisposeCore()
{
if (_dataContext != null)
_dataContext.Dispose();
}
}
It fails in this method
public TEntity Get(Expression<Func<TEntity, bool>> #where)
{
return Dbset.Where(where).FirstOrDefault(); <<<<<< Fails Here <<<<<
}
I was on the right track and it was because I was inheriting from the Base Entity.
When I re read my problem it became clear.
The solution was to simply not to inherit from base entity class and all is good.
Related
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.
Within MVC Web Application DbContext binding work properly with InRequestScope()
kernel.Bind<DbContext>().ToSelf().InRequestScope();
kernel.Bind<IUnitOfWork<DbContext>>().To<UnitOfWork<DbContext>>();
But from a Task Scheduler call DbContext in InRequestScope() unable to update Db Table (without any error), until I change Binding to InSingletonScope() OR InThreadScope()
Question: So is their any way change scope to InSingletonScope() / InThreadScope() for a Task Scheduler Call. ?
// For Task Scheduler Call, I tried bellow bind, but not working properly
kernel.Bind<DbContext>().ToSelf()
.When(request => request.Target.Type.Namespace.StartsWith("NameSpace.ClassName"))
.InSingletonScope();
** And probably I miss some thing. Need help.
Code Snippet Updated
#region Commented Code
public EmailTask() : this
( DependencyResolver.Current.GetService<IMessageManager>(),
, DependencyResolver.Current.GetService<IUnitOfWork<DbContext>>()) { }
#endregion
public EmailTask(IMessageManager messageManager, IUnitOfWork<DbContext> unitOfWork)
{
this._messageManager = messageManager;
this._unitOfWork = unitOfWork;
ProcessEmail();
}
public class NonRequestScopedParameter : IParameter { ... }
public void ProcessEmail()
{
var temp = SomeRepository.GetAll();
SendEmail(temp);
temp.Date = DateTime.Now;
SomeRepository.Update(temp);
unitOfWork.Commit();
}
public class ExecuteEmailTask : ITask
{
private readonly IResolutionRoot _resolutionRoot;
private int _maxTries = 5;
public ExecuteEmailTask(IResolutionRoot resolutionRoot)
{
_resolutionRoot = resolutionRoot;
}
public void Execute(XmlNode node)
{
XmlAttribute attribute1 = node.Attributes["maxTries"];
if (attribute1 != null && !String.IsNullOrEmpty(attribute1.Value))
{
this._maxTries = int.Parse(attribute1.Value);
}
/// send email messages
var task = _resolutionRoot.Get<EmailTask>(new NonRequestScopedParameter());
}
}
In Web.Config
<ScheduleTasks>
<Thread seconds="60">
<task name="ExecuteEmailTask" type="namespace.ExecuteEmailTask, AssemblyName" enabled="true" stopOnError="false" maxTries="5"/>
</Thread>
</ScheduleTasks>
In Global.asax
protected void Application_Start()
{
/* intialize Task */
TaskConfig.Init();
TaskManager.Instance.Initialize(TaskConfig.ScheduleTasks);
TaskManager.Instance.Start();
}
Ninject Bind Syntax
kernel.Bind<DbContext>().ToSelf().InRequestScope(); // Default bind
kernel.Bind<DbContext>().ToSelf()
.When(x => x.Parameters.OfType<NonRequestScopedParameter>().Any())
.InCallScope(); // For Scheduler
Note: EmailTask class also have SomeReposity as a Constructor Argument.
Queries:-
But what is the bind syntax to resolve TaskScheduler(IResolutionRoot resolutionRoot) ?
What is the configuration code to run TaskScheduler ?
As say to put IFakeDbContext directly into constructor, can this work with IUnitOfWork<FakeDbContext> ?
Problem
Task unable to call with Overloaded Constructor , it is only able to call TaskScheduler default Constructor.
Question 4: Can any way to invoke TaskScheduler(IResolutionRoot resolutionRoot) from TaskScheduler default constructor ?
Sample Code Snippet to create Task & run using System.Threading.Timer
private ITask createTask()
{
if (this.Enabled && (this._task == null))
{
if (this._taskType != null)
{
this._task = Activator.CreateInstance(this._taskType) as ITask;
}
this._enabled = this._task != null;
}
return this._task;
}
Question 5: Can I resolve TaskScheduler(IResolutionRoot resolutionRoot) here ?
Solved
public ExecuteEmailTask() :
this(DependencyResolver.Current.GetService<IResolutionRoot>())
OR
public ExecuteEmailTask() : this(new Bootstrapper().Kernel) { }
public ExecuteEmailTask(IResolutionRoot resolutionRoot)
{
_resolutionRoot = resolutionRoot;
}
First of, you should note that InSingletonScope() is usually a bad idea for DbContext's/Sessions. What happens if some other service changes data in the meantime? I would recommend investigating what effects this has.
For the scenario you first described, a correctly formulated .When(...) should work.
As an alternative to the .When(...) binding you could also use a .Named("FooBar") binding.
The constructor of the scheduled task would then need to look like:
ctor(Named["FooBar"] DbContext dbContext);
However, note, that this only (easily) works in case you need to inject the DbContext into a single constructor. If the task features dependencies and these need the same DbContext instance, too, it gets a bit tricker.
Since you updated your answer and say that this is the case, i would recommend an entirely different approach: Using a request parameter as basis for the When(...) condition combined with InCallScope binding. See below for an example.
Brace yourself, this is ab it of code :) The implementation requires the ninject.extensions.NamedScope extension (nuget).
I've also used xUnit and FluentAssertions nuget packages to execute the tests.
public class Test
{
// the two implementations are just for demonstration and easy verification purposes. You will only use one DbContext type.
public interface IFakeDbContext { }
public class RequestScopeDbContext : IFakeDbContext { }
public class CallScopeDbContext : IFakeDbContext { }
public class SomeTask
{
public IFakeDbContext FakeDbContext { get; set; }
public Dependency1 Dependency1 { get; set; }
public Dependency2 Dependency2 { get; set; }
public SomeTask(IFakeDbContext fakeDbContext, Dependency1 dependency1, Dependency2 dependency2)
{
FakeDbContext = fakeDbContext;
Dependency1 = dependency1;
Dependency2 = dependency2;
}
}
public class Dependency1
{
public IFakeDbContext FakeDbContext { get; set; }
public Dependency1(IFakeDbContext fakeDbContext)
{
FakeDbContext = fakeDbContext;
}
}
public class Dependency2
{
public IFakeDbContext FakeDbContext { get; set; }
public Dependency2(IFakeDbContext fakeDbContext)
{
FakeDbContext = fakeDbContext;
}
}
public class TaskScheduler
{
private readonly IResolutionRoot _resolutionRoot;
public TaskScheduler(IResolutionRoot resolutionRoot)
{
_resolutionRoot = resolutionRoot;
}
public SomeTask CreateScheduledTaskNow()
{
return _resolutionRoot.Get<SomeTask>(new NonRequestScopedParameter());
}
}
public class NonRequestScopedParameter : Ninject.Parameters.IParameter
{
public bool Equals(IParameter other)
{
if (other == null)
{
return false;
}
return other is NonRequestScopedParameter;
}
public object GetValue(IContext context, ITarget target)
{
throw new NotSupportedException("this parameter does not provide a value");
}
public string Name
{
get { return typeof(NonRequestScopedParameter).Name; }
}
// this is very important
public bool ShouldInherit
{
get { return true; }
}
}
[Fact]
public void FactMethodName()
{
var kernel = new StandardKernel();
// this is the default binding
kernel.Bind<IFakeDbContext>().To<RequestScopeDbContext>();
// this binding is _only_ used when the request contains a NonRequestScopedParameter
// in call scope means, that all objects built in the a single request get the same instance
kernel.Bind<IFakeDbContext>().To<CallScopeDbContext>()
.When(x => x.Parameters.OfType<NonRequestScopedParameter>().Any())
.InCallScope();
// let's try it out!
var task = kernel.Get<SomeTask>(new NonRequestScopedParameter());
// verify that the correct binding was used
task.FakeDbContext.Should().BeOfType<CallScopeDbContext>();
// verify that all children of the task get injected the same task instance
task.FakeDbContext.Should()
.Be(task.Dependency1.FakeDbContext)
.And.Be(task.Dependency2.FakeDbContext);
}
}
Since, as you say, the task scheduler does not make use of the IoC to create the task, it only supports a parameterless constructor. In that case you can make use DependencyResolver.Current (however, note that i'm in no way an expert on asp.net /MVC so i'm not making any claims that this is thread safe or working 100% reliably):
public class TaskExecutor : ITask
{
public TaskExecutor()
: this(DependencyResolver.Current.GetService<IResolutionRoot>())
{}
internal TaskExecutor(IResolutionRoot resolutionRoot)
{
this.resolutionRoot = resolutionRoot;
}
public void Execute()
{
IFooTask actualTask = this.resolution.Get<IFooTask>(new NonRequestScopedParameter());
actualTask.Execute();
}
}
I am trying to learn the repository pattern and looking at a generic repository I cannot see how to handle customized select statements. For example, using this article the author uses a select by ID and a select all.
public interface IGenericRepository<T> where T:class
{
IEnumerable<T> SelectAll();
T SelectByID(object id);
void Insert(T obj);
void Update(T obj);
void Delete(object id);
void Save();
}
Later the article the IGenericRepository interface is implemented using Northwind. Then that is used to create a Customer controller.
public class CustomerController : Controller
{
private IGenericRepository<Customer> repository = null;
public CustomerController()
{
this.repository = new GenericRepository<Customer>();
}
...
This would handle selecting a list of all Customers or for one Customer by ID but where I get stuck is some more real world examples like "select all Customers for a client" or "select all Customers for a region". Plus, you could have another controller based on a different entity that would filter on different attributes. I assume I'm missing something basic. If the user interface needed to present the Customer entity (or any other entity) by various filters, how would this be done by sticking with one generic repository?
Here you go; to handle any select criteria apart from the Id, you can add Where method
like below
public interface IGenericRepository<T> where T:class
{
IEnumerable<T> SelectAll();
T SelectByID(object id);
IEnumerable<T> Where(Expression<Func<T,bool>> predicate)// this one
void Insert(T obj);
void Update(T obj);
void Delete(object id);
void Save();
}
Now in the Where method implementation do it like this
public IEnumerable<T> Where(Expression<Func<T,bool>> predicate)
{
return _objectSet.Where(predicate).AsEnumerable();
}
Here _objectSet in created in repository constructor like this :
public Repository(ObjectContext context)
{
_context = context;
_objectSet = _context.CreateObjectSet<T>();
}
public CustomerController()
{
_context = new NorthwindEntities();
_reporsitory = new Repository<Customer>(_context);
}
Use of Where method like
reporsitory.Where(c=>c.Country=="Canada").ToList();
For full reference see this project on codeplex (download /browse source code)
https://efgenericrepository.codeplex.com/SourceControl/latest
I think the implementation of the GenericRepository should somehow be able to return the IQueryable of current entity, like adding Get() method.
protected IQueryable<T> Get() // Notice that the access modifier is protected.
{
return table;
}
Then you could just create a derived class from GenericRepository and add a select method that accepts the Filter class.
public class CustomerRepository : GenericRepository<Customer>
{
public IEnumerable<T> SelectAll(CustomerFilter filter){ .. }
}
The filter class contains 2 filters.
public class CustomerFilter
{
public int? ClientId { get; set; }
public int? RegionId { get; set; }
}
Then the SelectAll implementation would be.
public IEnumerable<T> SelectAll(CustomerFilter filter)
{
var query = Get();
if (filter == null)
{
return query;
}
if (filter.ClientId.HasValue)
{
query = query.Where(q => q.ClientId == filter.ClientId.Value);
}
if (filter.RegionId.HasValue)
{
query = query.Where(q => q.RegionId == filter.RegionId.Value);
}
return query;
}
In the controller, calling it like.
public ActionResult Get(int? clientId, int? regionId)
{
var filter = new CustomerFilter { ClientId = clientId, RegionId = regionId };
var customers = _repository.SelectAll(filter);
return View();
}
You might need to see this post as your reference.
An approach I've seen in one asp.net mvc based mission critical app, is to use the generic interface as defined in the question. Then there is an abstract class that implements that interface. And there is one more repository class that inherits the abstract class, which has all methods specific to that class.
public interface IGenericRepository<T> where T:class
{
...
}
public abstract class GenericRepository<T> : IGenericRepository where T:class
{
...
}
And the CustomerRepository class
public class CustomerRepository : GenericRepository<Customer>
{
//add method specific to Customer like select Customers in specific country
}
And in the controller
public class CustomerController : Controller
{
private CustomerRepository repository = null;
public CustomerController()
{
this.repository = new CustomerRepository();
}
...
I have an MVC intranet application which uses EF 6. I have setup the DataAccess project in a separate class library which has EF 6 referenced. I have an entity which implements an interface:
public interface IAuditable
{
DateTime CreatedDateTime { get; set; }
string CreatedBy { get; set; }
}
public class Collection : IAuditable
{
// Properties
}
However, in the SaveChanges method I obviously don't have access to HttpContext.Current.User.Identity.Name as it is in a separate class library, so I was wondering how one would set this in SaveChanges?
public override int SaveChanges()
{
var addedEntries = ChangeTracker.Entries().Where(x => x.State == EntityState.Added);
foreach (var dbEntityEntry in addedEntries)
{
var entity = dbEntityEntry.Entity as IAuditable;
if (entity != null)
{
entity.CreatedDateTime = DateTime.Now;
// how do I set entity.CreatedBy = HttpContext.Current.User.Identity.Name?
}
}
return base.SaveChanges();
}
Edit
Following on from #CodeCaster solution, I have the following:
[BreezeController]
public class BreezeController : ApiController
{
private readonly BTNIntranetRepository _repository;
public BreezeController(BTNIntranetRepository repository)
{
_repository = repository;
_repository.LoggedInUser = HttpContext.Current.User.Identity.Name;
}
// Methods
}
But HttpContext.Current.User is null
This can be solved in many ways.
You're not really showing relevant code, but you can for example give the library class you expose a public string LoggedInUser (or ActingUser or give it a name) property which you set when instantiating it:
public class SomeController : Controller
{
private IDataSource _dataSource;
public SomeController(IDataSource dataSource)
{
_dataSource = dataSource;
_dataSource.LoggedInUser = HttpContext.Current.User.Identity.Name
}
}
You can then simply use that property in your IDataSource.SaveChanges() method:
public override int SaveChanges()
{
// ...
entity.CreatedBy = this.LoggedInUser;
}
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.