AliRıza Adıyahşi
AliRıza Adıyahşi

Reputation: 15876

ASP.NET MVC dependency injection?

UnitOfWorkManager

public class UnitOfWorkManager : IUnitOfWorkManager
{
    private bool _isDisposed;
    private readonly BEMContext _context;

    public UnitOfWorkManager(IBEMContext context)
    {
        // http://stackoverflow.com/questions/3552000/entity-framework-code-only-error-the-model-backing-the-context-has-changed-sinc
        Database.SetInitializer<BEMContext>(null);

        _context = context as BEMContext;
    }

    /// <summary>
    /// Provides an instance of a unit of work. This wrapping in the manager
    /// class helps keep concerns separated
    /// </summary>
    /// <returns></returns>
    public IUnitOfWork NewUnitOfWork()
    {
        return new UnitOfWork(_context);
    }

    /// <summary>
    /// Make sure there are no open sessions.
    /// In the web app this will be called when the injected UnitOfWork manager
    /// is disposed at the end of a request.
    /// </summary>
    public void Dispose()
    {
        if (!_isDisposed)
        {
            _context.Dispose();
            _isDisposed = true;
        }
    }
}

UnitOfWork

public class UnitOfWork : IUnitOfWork
{
    private readonly BEMContext _context;
    private readonly IDbTransaction _transaction;
    private readonly ObjectContext _objectContext;

    /// <summary>
    /// Constructor
    /// </summary>
    public UnitOfWork(BEMContext context)
    {
        _context = context;

        // In order to make calls that are overidden in the caching ef-wrapper, we need to use
        // transactions from the connection, rather than TransactionScope. 
        // This results in our call e.g. to commit() being intercepted 
        // by the wrapper so the cache can be adjusted.
        // This won't work with the dbcontext because it handles the connection itself, so we must use the underlying ObjectContext. 
        // http://blogs.msdn.com/b/diego/archive/2012/01/26/exception-from-dbcontext-api-entityconnection-can-only-be-constructed-with-a-closed-dbconnection.aspx
        _objectContext = ((IObjectContextAdapter)_context).ObjectContext;

        if (_objectContext.Connection.State != ConnectionState.Open)
        {
            _objectContext.Connection.Open();
            _transaction = _objectContext.Connection.BeginTransaction();
        }
    }

    public void SaveChanges()
    {
        _context.SaveChanges();
    }

    public void Commit()
    {
        _context.SaveChanges();
        _transaction.Commit();
    }

    public void Rollback()
    {
        _transaction.Rollback();

        // http://blog.oneunicorn.com/2011/04/03/rejecting-changes-to-entities-in-ef-4-1/

        foreach (var entry in _context.ChangeTracker.Entries())
        {
            switch (entry.State)
            {
                case EntityState.Modified:
                    entry.State = EntityState.Unchanged;
                    break;
                case EntityState.Added:
                    entry.State = EntityState.Detached;
                    break;
                case EntityState.Deleted:
                    // Note - problem with deleted entities:
                    // When an entity is deleted its relationships to other entities are severed. 
                    // This includes setting FKs to null for nullable FKs or marking the FKs as conceptually null (don’t ask!) 
                    // if the FK property is not nullable. You’ll need to reset the FK property values to 
                    // the values that they had previously in order to re-form the relationships. 
                    // This may include FK properties in other entities for relationships where the 
                    // deleted entity is the principal of the relationship–e.g. has the PK 
                    // rather than the FK. I know this is a pain–it would be great if it could be made easier in the future, but for now it is what it is.
                    entry.State = EntityState.Unchanged;
                    break;
            }
        }
    }

    public void Dispose()
    {
        if (_objectContext.Connection.State == ConnectionState.Open)
        {
            _objectContext.Connection.Close();
        }
    }
}

LanguageRepository

public class LanguageRepository : ILanguageRepository
{
    private readonly BEMContext _context;

    public LanguageRepository(IBEMContext context)
    {
        _context = context as BEMContext;
    }

    public Language Insert(Language language)
    {
        _context.Language.Add(language);

        return language;
    }
}

LocalizationService

public class LocalizationService : ILocalizationService
{
    private readonly ILanguageRepository _languageRepository;

    public LocalizationService(ILanguageRepository languageRepository)
    {
        _languageRepository = languageRepository;
    }

    public void CreateLanguage(Language language)
    {
        _languageRepository.Insert(language);
    }
}

BaseController

public class BaseController : Controller
{
    protected readonly IUnitOfWorkManager _unitOfWorkManager;

    public BaseController(IUnitOfWorkManager unitOfWorkManager)
    {
        _unitOfWorkManager = unitOfWorkManager;
    }

    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        base.OnActionExecuting(filterContext);
    }
}

LocalizationController

public class LocalizationController : BaseController
{
    private readonly ILocalizationService _localizationService;

    public LocalizationController(ILocalizationService localizationService, IUnitOfWorkManager unitOfWorkManager)
        : base(unitOfWorkManager)
    {
        _localizationService = localizationService;
    }

    public ActionResult AddLanguage()
    {
        return View();
    }

    [HttpPost]
    public ActionResult AddLanguage(LanguageModel model)
    {
        try
        {
            if (ModelState.IsValid)
            {
                using (var UnitOfWork = _unitOfWorkManager.NewUnitOfWork())
                {
                    try
                    {
                        var language = Mapper.Map<LanguageModel, Language>(model);
                        _localizationService.CreateLanguage(language);
                        UnitOfWork.Commit();
                    }
                    catch (Exception ex)
                    {
                        UnitOfWork.Rollback();
                        throw new Exception(ex.Message);
                    }
                }
            }
        }
        catch (Exception ex)
        {
            throw new Exception(ex.Message);
        }

        return View(model);
    }
}

I cant add language to DB. Because service and unitOfWork classes use different context. Service change the context, but UnitOfWork save changes on another context. I mean following:

context in _localizationService.CreateLanguage(language); enter image description here

context in UnitOfWork enter image description here

Of cource, change does not effect db(I cant add entity to db). Because there are two different DbContex. I hope, I can explain, but I dont know how can I ask this question. How Can I solve this problem.

EDIT

I use Unity.MVC4 for IOC like following

public static class Bootstrapper
{
    public static IUnityContainer Initialise()
    {
        var container = BuildUnityContainer();

        DependencyResolver.SetResolver(new UnityDependencyResolver(container));

        return container;
    }

    private static IUnityContainer BuildUnityContainer()
    {
        var container = new UnityContainer();

        // register all your components with the container here
        // it is NOT necessary to register your controllers

        // e.g. container.RegisterType<ITestService, TestService>();    
        RegisterTypes(container);

        return container;
    }

    public static void RegisterTypes(IUnityContainer container)
    {
        container.RegisterType<IBEMContext, BEMContext>();

        container.RegisterType<ILocalizationService, LocalizationService>();

        container.RegisterType<ILanguageRepository, LanguageRepository>();
        container.RegisterType<ILocaleResourceKeyRepository, LocaleResourceKeyRepository>();
        container.RegisterType<ILocaleResourceValueRepository, LocaleResourceValueRepository>();

        container.RegisterType<IUnitOfWorkManager, UnitOfWorkManager>();
    }
}

Upvotes: 0

Views: 1428

Answers (2)

AliRıza Adıyahşi
AliRıza Adıyahşi

Reputation: 15876

My solution is:

/// <summary>
/// Bind the given interface in request scope
/// </summary>
public static class IOCExtensions
{
    public static void BindInRequestScope<T1, T2>(this IUnityContainer container) where T2 : T1
    {
        container.RegisterType<T1, T2>(new HierarchicalLifetimeManager());
    }

    public static void BindInSingletonScope<T1, T2>(this IUnityContainer container) where T2 : T1
    {
        container.RegisterType<T1, T2>(new ContainerControlledLifetimeManager());
    }
}

public static class Bootstrapper
{
    public static IUnityContainer Initialise()
    {
        var container = BuildUnityContainer();

        DependencyResolver.SetResolver(new UnityDependencyResolver(container));

        return container;
    }

    private static IUnityContainer BuildUnityContainer()
    {
        var container = new UnityContainer();

        // register all your components with the container here
        // it is NOT necessary to register your controllers

        // e.g. container.RegisterType<ITestService, TestService>();    
        RegisterTypes(container);

        return container;
    }

    public static void RegisterTypes(IUnityContainer container)
    {
        container.BindInRequestScope<IBEMContext, BEMContext>();

        container.BindInRequestScope<ILocalizationService, LocalizationService>();

        container.BindInRequestScope<ILanguageRepository, LanguageRepository>();
        container.BindInRequestScope<ILocaleResourceKeyRepository, LocaleResourceKeyRepository>();
        container.BindInRequestScope<ILocaleResourceValueRepository, LocaleResourceValueRepository>();

        container.BindInRequestScope<IUnitOfWorkManager, UnitOfWorkManager>();
    }
}

Upvotes: 0

Alex Paven
Alex Paven

Reputation: 5549

Unity is creating new instances of the dependencies, which means that as both IUnitOfWorkManager and ILocalizationService are instantiated, their dependencies are instantiated separately. Find a way to share the dependency on the context; ideally only the unit of work should be really aware of it, and anything else should go through the unit of work to make changes to the context, I think.

Upvotes: 1

Related Questions