Question

Final answer:

I used the combination of @WiktorZychla answer and Ninject in InRequestScope() method. I re factored my repository to accept injections of context and then inside my NinjectControllerFactory I added the line:

ninjectKernel.Bind<EFDbContext>().ToSelf().InRequestScope();

(note: I replaced the:

ninjectKernel.Bind<ISellingLocation>().To<EFSellingLocationRepository>().InReque‌​stScope().WithConstructorArgument("context",new EFDbContext());

line I mentioned in one of the comments, with the:

ninjectKernel.Bind<ISellingLocation>().To<EFSellingLocationRepository>();

as it was causing errors)

I also installed Ninject.MVC3 with nuget, and it created the file: "NinjectWebCommon.cs" with the line:

DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));

While some say this line is optional, other articles state it should be used in order for InRequestScope to work properly in MVC sites.

Original qestion:

I currently have few EF repositories, every one looks similar to the following:

public class EFCityRepository : ICityRepository
{
private EFDbContext context = new EFDbContext();

public bool Create(City cityToCreate)
{
...
}

public City Get(int cityID)
{
...
}
}

As you can see, right now I’m using single global EFDbContext for all operations, from what I read this is bad - so I tried changing it (inside the Create, Get and other methods) to the "using" statement, something like the following:

public City Get(int cityID)
{
using(EFDbContext context)
{
...some opeartions…
return entities;
}
}

And now I get a lot of problems related to Entities lazy loading, I have to use something like the following:

context.Entry(getResult.FirstOrDefault()).Reference(x => x.Address).Load();
context.Entry(getResult.FirstOrDefault()).Reference(x => x.Agency).Load();
context.Entry(getResult.FirstOrDefault().Address).Reference(x => x.City).Load();

just to make simple Get to work the way I want to, otherwise every time I try to access Address for example, I get: “The ObjectContext instance has been disposed and can no longer be used for operations that require a connection”. Of course it works OK, when context is global.

I need some advise: should I use the local context and use eagerly loading instead of lazy loading? Or is the global context acceptable here?

Also how the heck is one suppose to use lazy loading anyway? As I see it - I would have to write separate logic for every operation that use some repository - or am I wrong?

Edit 1:

@Askolein:

OK, currently my application consists of few sub projects:

Common
Domain - here I have my repositories
Helpers
Utils
WebUI

The repository I use to trigger the error looks like this:

public interface ISellingLocation
{
KeyValuePair<bool, Exception> Create(SellingLocation sellingLocationToAdd);
KeyValuePair<SellingLocation, Exception> Get(int sellingLocationID);
KeyValuePair<bool, Exception> Update(SellingLocation sellingLocationToUpdate);
KeyValuePair<bool, Exception> Delete(int sellingLocationID);

KeyValuePair<List<SellingLocation>, Exception> GetAll();
KeyValuePair<List<SellingLocation>, Exception> GetAll(int agencyID);
KeyValuePair<List<SellingLocation>, Exception> GetFiltered(string filter);
KeyValuePair<List<SellingLocation>, Exception> GetFiltered(Expression<Func<SellingLocation, bool>> filter);

KeyValuePair<bool, Exception> DisableSellingLocations(List<int> sellingLocationsIDs);
}

And the implementation of the GetFiltered method, like this:

public KeyValuePair<List<SellingLocation>, Exception> GetFiltered(Expression<Func<SellingLocation, bool>> filter)
{
Exception lastException = null;

using (var transaction = new TransactionScope())
{
using (EFDbContext context = new EFDbContext())
{
try
{
var getResult = context.SellingPoints.Where(filter).ToList();
//var getResult2 = getResult.ToList();

context.Entry(getResult.FirstOrDefault()).Reference(x => x.Address).Load();
context.Entry(getResult.FirstOrDefault()).Reference(x => x.Agency).Load();
context.Entry(getResult.FirstOrDefault().Address).Reference(x => x.City).Load();


transaction.Complete();

return new KeyValuePair<List<SellingLocation>, Exception>(getResult, lastException);
}
catch (Exception ex)
{
lastException = ex;

return new KeyValuePair<List<SellingLocation>, Exception>(new List<SellingLocation>(), ex);
}
}
}
}

I'm calling this method in my controller like this:

var allSellingLocationsForCurrentUser = sellingLocationRepository.GetFiltered(x => x.IsEnabled);

if(allSellingLocationsForCurrentUser.Value == null)
{
AgencySalesSellingLocationsListViewModel agencySalesSellingLocationsListViewModel = new AgencySalesSellingLocationsListViewModel();

foreach (var item in allSellingLocationsForCurrentUser.Key)
{
agencySalesSellingLocationsListViewModel.aaData.Add(new AgencySalesSellingLocationsListViewModelRow()
{
ID = item.SellingLocationID,
DT_RowId = item.SellingLocationID.ToString(),
Name = item.Name,
City = item.Address.City.Name,
Street = item.Address.Street
});
}

return Json(agencySalesSellingLocationsListViewModel, JsonRequestBehavior.AllowGet);
}

I understand why I'm get the error, and as I said before - if I explicitly tell entity, to load: Address, Agency and Address.City - it will work just fine.

@WiktorZychla:

My current DataContext looks like this:

public class EFDbContext : DbContext
{
public EFDbContext():base("DefaultConnection")
{

}

public DbSet<User> Users { get; set; }
public DbSet<UserData> UserDatas { get; set; }
public DbSet<Address> Addresses { get; set; }
public DbSet<SkillCategory> SkillCategories {get;set;}
public DbSet<Skill> Skills {get;set;}
public DbSet<SkillAnswer> SkillAnswers { get; set; }
//public DbSet<UserSkills> UserSkills { get; set; }
public DbSet<User2Skill> User2Skill { get; set; }
public DbSet<Agency> Agencies { get; set; }
public DbSet<UniversalDictionary> UniversalDictionaries { get; set; }
public DbSet<UserAddressTimeTable> UserAddressTimeTables { get; set; }
public DbSet<City> Cities { get; set; }
public DbSet<SellingLocation> SellingPoints { get; set; }
}

If I understand You correctly - I would have to encapsulate my EFCityRepository and use something like:

using(SomeContext context = new SomeContext())
{
    EFCityRepository repository = new EFCityRepository(context);
    var result = repository.Get(id);
    ...do some work...
}

Isn't that a little overkill? Right now I use Ninject, and inject my Controllers with repository interfaces (IUserRepository, IUserRepository etc.) - so my controller methods are working like Units of Work, If I understand correctly - I would have to either: inject my DbContext inside my controller methods, or create another layer between controler methods, and repository... do I understand this correctly?

@cosset:

As I stated above - I consider my controller methods as Units of Work... if I was to implement something You suggested - where should I put it? Inside Domain or WebUI? It would have to be another layer between repository and controller, correct?

Thank you all for the suggestions.

Best regards

Was it helpful?

Solution

Instead of making the context local to repository methods, why don't you go the other way around - make the repository independent on the context:

public class EFCityRepository : ICityRepository
{ 
    public EFCityRepository( EFDbContext context )
    {
        this.context = context;
    }        

    private EFDbContext context;

    public bool Create(City cityToCreate)
    {
        ...
    }

    public City Get(int cityID)
    {
        ...
    }
}

This approach gives you the best flexibility. You can share the very same context between repositories, you can have an extra context per repository, whatever.

For web-based applications, the common practice is to have your context shared "per request" which means that the very same context is used thoughout a single request and it is disposed at the end of the request pipeline.

Edit: as suggested by Maess, you should definitely look at the possibility to semi-automatically manage the lifetime of your context by a Dependency Injection engine (like Unity or Ninject). The DI engine could also help you significantly by automatically resolving constructor dependencies. This is another story but could be a solid step forward for your architecture.

OTHER TIPS

I recommend use use pattern UnitOfWork.Simple implementation bellow.

public interface IUnitOfWork : IDisposable
{
    void Save();
}

public class EntityFrameworkUnitOfWork : IUnitOfWork
{
    private EFDbContext context = new EFDbContext ();
    internal ICityRepository cityRepo;
    public ICityRepository CityRepository
    {
        get
        {
            if (cityRepo== null)
            {
                cityRepo = new EFCityRepository(context);
            }
            return cityRepo;
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top