I am getting started with SignalR, and it works great once everything is configured. However, almost all the applications that I work on use Castle Windsor, so it would be great to be able to use them together. The reason that I want to do this is so that I can use Castle dependencies/services inside of a persistent connection.
I dug around in the source code, and it looks like I could either replace DependencyResolver with a Castle based one (i.e., Castle implementing IDependencyResolver), or I could change the usage of DependencyResolver to Castle.
Which one of these is a better idea? Is there another approach that I could use to combine Castle and SignalR?
Thanks,
Erick
August 2016 update
Following from a comment I no longer use the approach below but now use the GlobalHost.DependencyResolver
So in Global.asax.cs I initialise things
public static void Init(IWindsorContainer container)
{
var conn = configurationManager.ConnectionStrings["SRSQL"].ConnectionString;
GlobalHost.DependencyResolver.Register(typeof(IHubActivator),
() => new SignalHubActivator(container));
GlobalHost.DependencyResolver.Register(typeof(ILoggingService),
container.Resolve<ILoggingService>);
//etc or you could just pass your existing container to the resolver
GlobalHost.DependencyResolver.UseSqlServer(conn);
}
and then in the hub
private ILoggingService LoggingService{ get; set; }
public NotificationHub()
{
LoggingService = GlobalHost.DependencyResolver.Resolve<ILoggingService>();
}
and for completeness
public class SignalHubActivator: IHubActivator
{
private readonly IWindsorContainer _container;
public SignalHubActivator(IWindsorContainer container)
{
_container = container;
}
public IHub Create(HubDescriptor descriptor)
{
var result= _container.Resolve(descriptor.HubType) as IHub;
if (result is Hub)
{
_container.Release(result);
}
return result;
}
}
OLD ANSWER from 2012
I went with the first option of setting our own DependencyResolver
AspNetHost.SetResolver(new SignalResolver(_container));
I can provide SignalResolver if desired but leaving out for readability for now.
Another important note is that the hubs must have an empty constructor so our castle container injects through properties, e.g.
public class NotificationHub : Hub, INotificationHub
{
public INotificationService NotificationService { get; set; }
and the resolver requested
public class SignalResolver : DefaultDependencyResolver
{
private readonly IWindsorContainer _container;
public SignalResolver(IWindsorContainer container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
_container = container;
}
public override object GetService(Type serviceType)
{
return TryGet(serviceType) ?? base.GetService(serviceType);
}
public override IEnumerable<object> GetServices(Type serviceType)
{
return TryGetAll(serviceType).Concat(base.GetServices(serviceType));
}
private object TryGet(Type serviceType)
{
try
{
return _container.Resolve(serviceType);
}
catch (Exception)
{
return null;
}
}
private IEnumerable<object> TryGetAll(Type serviceType)
{
try
{
var array = _container.ResolveAll(serviceType);
return array.Cast<object>().ToList();
}
catch (Exception)
{
return null;
}
}
}
Here's what I ended up doing. First I followed along with the Windsor wiki to get my ASP.NET MVC3 setup. My Global.asax.cs:
private static IWindsorContainer _container;
protected void Application_Start()
{
BootstrapContainer();
RegisterRoutes(RouteTable.Routes);
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
}
protected void Application_End()
{
_container.Dispose();
}
private static void BootstrapContainer()
{
_container = new WindsorContainer().Install(FromAssembly.This());
RouteTable.Routes.MapHubs(new CastleWindsorDependencyResolver(_container));
var controllerFactory = new WindsorControllerFactory(_container.Kernel);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
}
...
CastleWindsorDependencyResolver came from here
Copied:
public class CastleWindsorDependencyResolver : DefaultDependencyResolver
{
private readonly IWindsorContainer _container;
public CastleWindsorDependencyResolver(IWindsorContainer container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
_container = container;
// perform the lazy registrations
foreach (var c in _lazyRegistrations)
_container.Register(c);
_lazyRegistrations.Clear();
}
public override object GetService(Type serviceType)
{
if (_container.Kernel.HasComponent(serviceType))
return _container.Resolve(serviceType);
return base.GetService(serviceType);
}
public override IEnumerable<object> GetServices(Type serviceType)
{
IEnumerable<object> objects;
if (_container.Kernel.HasComponent(serviceType))
objects = _container.ResolveAll(serviceType).Cast<object>();
else
objects = new object[] { };
var originalContainerServices = base.GetServices(serviceType);
if (originalContainerServices != null)
return objects.Concat(originalContainerServices);
return objects;
}
public override void Register(Type serviceType, Func<object> activator)
{
if (_container != null)
// cannot unregister components in windsor, so we use a trick
_container.Register(Component.For(serviceType).UsingFactoryMethod<object>(activator, true).OverridesExistingRegistration());
else
// lazy registration for when the container is up
_lazyRegistrations.Add(Component.For(serviceType).UsingFactoryMethod<object>(activator));
// register the factory method in the default container too
//base.Register(serviceType, activator);
}
// a form of laxy initialization is actually needed because the DefaultDependencyResolver starts initializing itself immediately
// while we now want to store everything inside CastleWindsor, so the actual registration step have to be postponed until the
// container is available
private List<ComponentRegistration<object>> _lazyRegistrations = new List<ComponentRegistration<object>>();
}
public static class WindsorTrickyExtensions
{
/// <summary>
/// Overrideses the existing registration:
/// to overide an existiong component registration you need to do two things:
/// 1- give it a name.
/// 2- set it as default.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="componentRegistration">The component registration.</param>
/// <returns></returns>
public static ComponentRegistration<T> OverridesExistingRegistration<T>(this ComponentRegistration<T> componentRegistration) where T : class
{
return componentRegistration
.Named(Guid.NewGuid().ToString())
.IsDefault();
}
}
I wasn't sure WTF the HubsInstaller was trying to do from that same project but I made my own which seems to work fine (I am of course open to any suggestions why this could suck):
public class HubsInstallers : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly()
.BasedOn<IHub>()
.LifestyleTransient());
}
}
Also this is for the newer SignalR versions 0.5+
dove answer is fine but it is a bit confusing, adding another more specific answer.
My main goal is this to work:
[HubName("MyHub")]
public class MyHub : Hub
{
public IJobRepository JobRepository { get; }
public MyHub(IJobRepository jobRepository)
{
JobRepository = jobRepository ?? throw new ArgumentNullException(nameof(jobRepository));
}
...
}
Of course what you want is your Hubs to be created for you, they are usually created by SignalR but now that they have some dependencies SignalR cannot create them.
SignalR itself has a Dependency Resolver (in SignalR namespace) which uses to get its own dependencies, you can add stuff to it, but we want Windsor remember?
So we are going to change just how the IHubActivator creates hubs, we are not going to use SignalR's but this one:
public class SignalRHubActivator : IHubActivator
{
private readonly IWindsorContainer _container;
public SignalRHubActivator(IWindsorContainer container)
{
_container = container;
}
public IHub Create(HubDescriptor descriptor)
{
var result = _container.Resolve(descriptor.HubType) as IHub;
if (result is Hub)
{
_container.Release(result);
}
return result;
}
}
To replace this in SignalR container you have to do something like:
// Get an instance of the hub creator (see note below)
var _hubActivator = new SignalRHubActivator(container);
// Get the SignalR's Default Dependency Resolver
var signalRResolver = new Microsoft.AspNet.SignalR.DefaultDependencyResolver();
// Override the IHubActivator service
signalRResolver.Register(typeof(IHubActivator), () => _hubActivator);
// now map SignalR with this configuration
appBuilder.MapSignalR(new HubConfiguration { Resolver = signalRResolver });
And that's it, you should also register all your Hubs with Windsor
container.Register(Classes.FromThisAssembly()
.BasedOn(typeof(Microsoft.AspNet.SignalR.Hub)));
...
container.Register(Component.For<IJobRepository>()).ImplementedBy<JobRepository>());
Note: I registered the SignalRHubActivator as a component too, this is because the Startup class I use receives the activator as a dependency:
container.Register(Component.For<SignalRHubActivator>().
DependsOn(Dependency.OnValue("container", container)));
Related
First time I call my service everything works fine. After that I receive this error:
"Error loading Ninject component ICache\r\nNo such component has been registered in the kernel's component container.
The controller I am calling has IKernel as a parameter in its constructor and my guess is that this is the problem.
How am i supposed to pass a kernel object into my service?
My controller constructor:
public MyController(IKernel diContainer)
{
_diContainer = diContainer;
}
Part of my Global.asax file:
var kernel = NinjectWebApi.Kernel;
kernel.Bind<IMyController().To<MyController();
//Set the dependency resolver to use ninject
GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
I am using Ninject 3.2.
For my other services where I am not passing in the Kernel to the service this approach works fine.
public class NinjectDependencyScope : IDependencyScope
{
private IResolutionRoot _resolver;
public NinjectDependencyScope(IResolutionRoot resolver)
{
_resolver = resolver;
}
public object GetService(Type serviceType)
{
if (_resolver == null)
{
throw new ObjectDisposedException("this", "This scope has been disposed.");
}
return _resolver.TryGet(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
if (_resolver == null)
{
throw new ObjectDisposedException("this", "This scope has been disposed.");
}
return _resolver.GetAll(serviceType);
}
public void Dispose()
{
var disposable = _resolver as IDisposable;
disposable?.Dispose();
_resolver = null;
}
}
public class NinjectDependencyResolver : NinjectDependencyScope, IDependencyResolver
{
private readonly IKernel _kernel;
public NinjectDependencyResolver(IKernel kernel) : base(kernel)
{
_kernel = kernel;
}
public IDependencyScope BeginScope()
{
return new NinjectDependencyScope(_kernel.BeginBlock());
}
}
It seems you dispose container inside your NinjectDependenvyResolver Dispose method.
I have a project with multiple layers(assemblies). I used Castle Windsor to solve Dependency Injection. Also create my controllers with castle. so I installed and used castle in Web API and WebUI Layers too. Beside I have a bootstrapper layer which registers all services and repositories.
First my web API:
In WebApiConfig.cs file, I registered UI Dependencies with flowing code:
public static void Register(HttpConfiguration config)
{
var corsAttr = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(corsAttr);
// Web API configuration and services
var container = Bootstrapper.WireUp();// to register services and repositories
RegisterControllers(container);
config.Services.Replace(typeof(IHttpControllerActivator), new WindsorControllerActivator(container));
}
private static void RegisterControllers(IWindsorContainer container)
{
//container.Register(Component.For<VisitorController>().LifestylePerWebRequest());
//container.Register(Component.For<ValueController>().LifestylePerWebRequest());
//container.Register(Component.For<ValidateController>().LifestylePerWebRequest());
//container.Register(Component.For<CaptchaImageController>().LifestylePerWebRequest());
container.Register(Classes.FromAssemblyContaining<VisitorController>()
.BasedOn<ApiController>().LifestyleTransient());
}
And activator factory is like this:
public class WindsorControllerActivator : IHttpControllerActivator
{
private readonly IWindsorContainer _container;
public WindsorControllerActivator(IWindsorContainer container)
{
_container = container;
}
public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
return (IHttpController)_container.Resolve(controllerType);//System.ArgumentNullException: Value cannot be null. error
}
}
Also in WebUI project I used Castle Windsor to create my controllers. But when I run the project I get "System.ArgumentNullException: Value cannot be null." error when is resolving Controller.I mean in this line:
(IHttpController)_container.Resolve(controllerType);
I also tried "LifeStylePerWebRequest" for my controllers and nothing were changed.
Whats more, nothing is wrong with WireUp Method,(Registering service and repositories works fine). This problem is just for controllers(in WebUI and WebApi projects). Same problem happens for WebUI project.
I normally use implementation of IDependencyResolver instead of IHttpControllerActivator. You can this approach instead. Add the floowing class:
public class WindsorDependencyResolver : IDependencyResolver
{
readonly IWindsorContainer _container;
public WindsorDependencyResolver(IWindsorContainer container)
{
if (container == null)
{
throw new ArgumentNullException(nameof(container));
}
_container = container;
}
public IDependencyScope BeginScope()
{
return new WindsorDependencyScope(this, _container.Release);
}
public void Dispose()
{
}
public object GetService(Type t)
{
var ret = _container.Kernel.HasComponent(t) ? _container.Resolve(t) : null;
return ret;
}
public IEnumerable<object> GetServices(Type t)
{
var ret = _container.ResolveAll(t).Cast<object>().ToArray();
return ret;
}
}
This class is required by the dependency resolver:
public class WindsorDependencyScope : IDependencyScope
{
readonly List<object> _instances;
readonly Action<object> _release;
readonly IDependencyScope _scope;
public WindsorDependencyScope(IDependencyScope scope, Action<object> release)
{
if (scope == null)
{
throw new ArgumentNullException(nameof(scope));
}
if (release == null)
{
throw new ArgumentNullException(nameof(release));
}
_scope = scope;
_release = release;
_instances = new List<object>();
}
public void Dispose()
{
foreach (object instance in _instances)
{
_release(instance);
}
_instances.Clear();
}
public object GetService(Type t)
{
var service = _scope.GetService(t);
AddToScope(service);
return service;
}
public IEnumerable<object> GetServices(Type t)
{
var services = _scope.GetServices(t);
AddToScope(services);
return services;
}
void AddToScope(params object[] services)
{
if (services.Any())
{
_instances.AddRange(services);
}
}
}
And this is how you register the the dependency resolver in you Register method:
config.DependencyResolver = new WindsorDependencyResolver(container);
I would recommend to register your controllers with lifestyle transient as the dependency resolver takes care of disposing them.
I have ASP.NET MVC5 web application and i also have Web API in the same application. I am uisng Unity (version 4) for DI.
I am configuring the Unity container on APP start as below
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
UnityConfiguration.Config();
}
}
public class UnityConfiguration()
{
public void Config()
{
UnityContainer container = new UnityContainer();
container.RegisterType<IMyService, Myservice>();
container.RegisterType<IGenericRepository, GenericRepository>();
container.RegisterType<DbContext, MyEntities>();
}
}
public class GenericRepository:IGenericRepository
{
private DbContext _dbcontext;
public GenericRepository(DbContext dbcontext)
{
_dbcontext = dbcontext;
}
}
public class MyService:IMyService
{
private IGenericRepository _repo;
publi void MyService(IGenericRepository repository)
{
_repo = repository;
}
}
public class MyApiController:ApiController
{
provate IMyService _service;
MyApiController(IMyService myservice)
{
_service = myservice;
}
public IEnumerable<MyModel> GetData()
{
var result = _service.GetData();
return result.ConvertToMyModel();
}
}
However when i call the url like
localhost://lookup/getdata
I get error
Type 'LookupController' does not have a default constructor
How do i solve this issue? Do i need to register each controller i create with Unity or Unity automatically registers all MVC controllers?
I tend to use the Unity.Mvc-package.
You do not need to register the controllers, but you need to register Unity with WebAPI.
public class UnityConfiguration()
{
public IUnityContainer Config()
{
IUnityContainer container = new UnityContainer();
container.RegisterType<IMyService, Myservice>();
container.RegisterType<IGenericRepository, GenericRepository>();
container.RegisterType<DbContext, MyEntities>();
// return the container so it can be used for the dependencyresolver.
return container;
}
}
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Your routes...
// Register Unity with Web API.
var container = UnityConfiguration.Config();
config.DependencyResolver = new UnityResolver(container);
// Maybe some formatters?
}
}
You also need a DependencyResolver:
public class UnityResolver : IDependencyResolver
{
protected IUnityContainer container;
public UnityResolver(IUnityContainer container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
this.container = container;
}
public object GetService(Type serviceType)
{
try
{
return container.Resolve(serviceType);
}
catch (ResolutionFailedException)
{
return null;
}
}
public IEnumerable<object> GetServices(Type serviceType)
{
try
{
return container.ResolveAll(serviceType);
}
catch (ResolutionFailedException)
{
return new List<object>();
}
}
public IDependencyScope BeginScope()
{
var child = container.CreateChildContainer();
return new UnityResolver(child);
}
public void Dispose()
{
container.Dispose();
}
}
You can also take a look at this similiar question, except for the Owin-part. Unity.WebApi | Make sure that the controller has a parameterless public constructor
I had the same error and in my case the problem was, that i forgot to register a dependency that one of the classes, I had registered for dependency injection, injects in the constructor.
In your example, could it be that you inject something into MyEntities that you forgot to Register?
Install Nuget Package Unit.WebAP instead of Unity.MVC5
Make sure the correct unity package is installed using nuget
I Installed Unity.MVC5 and was facing similar exception "parameterless constructor"
public static void RegisterComponents()
{
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>();
container.RegisterType<ICar, Tesla>();
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
}
I think I'm missing something very simple and maybe just need a new set of eyes. I have an ASP.NET MVC application. In that app, I am using Unity for my IoC to handle dependency injection. Each of my repositories need to have a database factory injected into it and each database factory needs to have a principal injected into it. So far, I've been utilizing the PerRequestLifetimeManager to register these.
//Repositories
container.RegisterType<ChatMessageRepository>(new PerRequestLifetimeManager());
container.RegisterType<SignalRConnectionRepository>(new PerRequestLifetimeManager());
//Context
container.RegisterType<IPrincipal, Principal>(new PerRequestLifetimeManager());
container.RegisterType<IDatabaseFactory, DatabaseFactory>(new PerRequestLifetimeManager());
container.RegisterType<UnitOfWork>(new PerRequestLifetimeManager());
Logically, I've tried to register my Hub in the same fashion.
container.RegisterType<ChatHub>(new PerRequestLifetimeManager());
However, whenever I run my app and navigate away from my chat page, I get a "Resolution of the dependency failed" exception and the InnerException tells me "Operation is not valid due to the current state of the object." I've also tried using the default (Transient), PerResolve, and ContainerControlled lifetime Unity managers when registering these guys and cannot seem to get resolve my issue.
Could someone just provide me some demo code with how you used Unity in an ASP.NET MVC application to handle dependency injection into your signalr hubs?
Here's where Unity will inject parameters into my SignalR Hub
public class ChatHub : Hub
{
private readonly ChatMessageRepository _chatMessageRepository;
private readonly SignalRConnectionRepository _signalRConnectionRepository;
private readonly UnitOfWork _unitOfWork;
public ChatHub(ChatMessageRepository chatMessageRepository,
SignalRConnectionRepository signalRConnectionRepository,
UnitOfWork unitOfWork)
{
_chatMessageRepository = chatMessageRepository;
_signalRConnectionRepository = signalRConnectionRepository;
_unitOfWork = unitOfWork;
} ... }
Thanks!
Do it in 3 steps
First. Create UnityHubActivator class
public class UnityHubActivator : IHubActivator
{
private readonly IUnityContainer _container;
public UnityHubActivator(IUnityContainer container)
{
_container = container;
}
public IHub Create(HubDescriptor descriptor)
{
return (IHub)_container.Resolve(descriptor.HubType);
}
}
Second. Create Unity container and register your dependency resolver before run Startup class
unityContainer = new UnityContainer();
var unityHubActivator = new UnityHubActivator(_unityContainer);
GlobalHost.DependencyResolver.Register(typeof(IHubActivator), () => unityHubActivator);
//register some types in container
WebApp.Start<Startup>(startOptions);
Third. Use it in your Hub
public class MyHub : Hub
{
public MyHub(Logger logger)
{
logger.Info("hub constructor");
}
}
Note. I do not change anything in my Startup class
There's a trick to do that. You will need to do something like this:
container.RegisterType< ChatHub >(new InjectionFactory(CreateChatHub));
......
and then create a private method CreateChatHub
private static object CreateChatHub(IUnityContainer container)
{
return new ChatHub();
}
1 Create "UnitySignalRDependencyResolver.cs"
public class UnitySignalRDependencyResolver : DefaultDependencyResolver
{
protected IUnityContainer Container;
private bool IsDisposed = false;
public UnitySignalRDependencyResolver(IUnityContainer container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
Container = container.CreateChildContainer();
}
/// <summary>
/// Gets the Autofac implementation of the dependency resolver.
/// </summary>
public static UnitySignalRDependencyResolver Current
{
get { return GlobalHost.DependencyResolver as UnitySignalRDependencyResolver; }
}
public override object GetService(Type serviceType)
{
if (Container.IsRegistered(serviceType))
{
return Container.Resolve(serviceType);
}
return base.GetService(serviceType);
}
public override IEnumerable<object> GetServices(Type serviceType)
{
if (Container.IsRegistered(serviceType))
{
return Container.ResolveAll(serviceType);
}
return base.GetServices(serviceType);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (IsDisposed)
{
return;
}
if (disposing)
{
Container.Dispose();
}
IsDisposed = true;
}
}
2.Add your resolver to Owin pipeline
public class Startup
{
public void Configuration(IAppBuilder app)
{
// Get container
IUnityContainer container = UnityConfig.Container;
// Create resolver
var resolver = new UnitySignalRDependencyResolver(container);
// Create SignalR Configuration
var config = new HubConfiguration
{
Resolver = resolver
};
// Start SignalR
app.Map("/signalr", map =>
{
map.RunSignalR(config);
});
}
}
3.Inject your dependency in your controller's constructor
public class ValuesController : ApiController
{
private readonly IMyDependency _myDependency;
public ValuesController(IMyDependency myDependency)
{
_myDependency= myDependency;
}
}
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);
context in UnitOfWork
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>();
}
}
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.
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>();
}
}