We have a Disposable property in a controller is needed in other controllers too. For example, suppose the following property in a controller:
public ClassName<Template> Name { get; set; }
Which will be instantiated in the same controller's constructor like this:
public SomeController()
: this(new ClassName<Template>())
{
}
public SomeController(ClassName<Template> name)
{
Name = name;
}
Can I use the property by instantiating the container controller in other controllers?
If your question is: can I create an instance of the controller and use it in another one, the answer is yes, controllers are just classes which you can instantiate and use. Just like any other.
The thing that concerns me though, is that it seems you are using a property that should be a proper singleton as something that isn't, by re-instantiating another class that seems slightly related. If you can, move the property to an other class. Make it singleton, and use it accordingly.
Related
I'm using a custom JsonConverter and JsonSerializerSettings.TypeNameHandling = TypeNameHandling.Objects to create the required instances during deserialization. The instances are created by resolving the types from an Autofac IOC container. Everything works fine, except...
I have several "core objects" that request a unique Id in the constructor from a service (which is correctly injected into the constructor). When deserializing this should not happen because it is fairly expensive and the Ids will be populated from the Json file anyway once the instance has been created.
Currently, when resolving from within the custom JsonConverter I'm using _scope.Resolve<T>(new TypedParameter(typeof(IIdService), null)); to then - in the called constructor - check for null and act accordingly.
Some people apparently consider multiple constructors worse than a code-smell when using an IOC (which makes me wonder why Autofac offers several features regarding the topic), but in the context of deserialization I think it can make perfect sense.
As far as I can tell Autofac has mechanisms to decide which constructor to use during registration, but not when resolving. My preferred solution would be to add a custom attribute to a constructor (e.g. [CtorForDeserializing]) and use that for deciding. Is that possible?
There are a couple of extension points Autofac has for reflection-based activations but doesn't have well documented yet that may help you out: IConstructorFinder and IConstructorSelector.
IConstructorFinder is used to locate all the available constructors on a type. The core example is the DefaultConstructorFinder which locates only public constructors. If you wanted to, say, hide constructors with particular attributes or start finding internal/private constructors, you could create a custom finder. This really only happens once so you don't get to make runtime choices here.
IConstructorSelector is used to choose, at resolve time, which constructor should be used to instantiate the object. There are a couple of these in core Autofac, but the primary example is the MostParametersConstructorSelector which selects the constructor that has the most available matching parameters at the time. Constructors get found by the IConstructorFinder and then that set of constructors is what is presented to the IConstructorSelector to choose from. This is where you could make more runtime choices since it happens every time the object is resolved.
There are extension methods to help you add your finder/selector to a registration:
builder.RegisterType<MyType>()
.FindConstructorsWith(new MyConstructorFinder())
.UsingConstructor(new MyConstructorSelector());
You don't have to customize both things, you can just do one or the other if you want. I'm just showing you the extensions.
Actually Autofac is able to decide which constructor to use both ways - during registration or resolution. For resolution part here is the quote from documentation: "Autofac automatically uses the constructor for your class with the most parameters that are able to be obtained from the container" (see here).
Consider following example.
public interface ISomeService
{
Guid Id { get; }
}
public class SomeService : ISomeService
{
public Guid Id { get; }
public SomeService()
{
Id = Guid.NewGuid();
}
public SomeService(Guid id)
{
Id = id;
}
}
// Startup.cs:
builder.RegisterType<SomeService>().As<ISomeService>().InstancePerLifetimeScope();
// TestController.cs:
[Route("api/[controller]")]
public class TestController : Controller
{
private readonly IComponentContext _context;
public TestController(IComponentContext context)
{
_context = context;
}
[HttpGet]
public IActionResult Get()
{
var service = _context.Resolve<ISomeService>();
return Ok(service.Id);
}
[HttpGet("{id}")]
public IActionResult Get(Guid id)
{
var service = _context.Resolve<ISomeService>(new NamedParameter("id", id));
return Ok(service.Id);
}
}
// GET http://localhost:5000/api/test/e0198f72-6337-4880-b608-68935122cdea
// each and every response will be the same: e0198f72-6337-4880-b608-68935122cdea
// GET http://localhost:5000/api/test
// this way it responds with some random guid each time endpoint is called
Travis Illig sent me in the right direction - thanks!
I ended up implementing a solution around the following details:
Implement custom attributes, e.g.: public class DeserializeCtorAttribute : Attribute { }, which will be used by the (also to be implemented) IConstructorFinder.
Implement an empty generic interface, e.g.: IDeserializable<T>, which will be used for resolving the services/components.
Let relevant component classes implement the interface (MyClass : IDeserializable<MyClass>) and add an extra registration for the component:
_builder.RegisterType<MyClass>().As<IDeserializable<MyClass>>()
.FindConstructorsWith(MyConstructorFinder);
Use the implemented DeserializeCtorAttribute in the desired constructor of MyClass.
Let the JsonConverter create the required instance by calling (MyClass) scope.Resolve(IDeserializable<MyClass>); casting is required, but safe. Due to the registration the instance will be created using the desired constructor.
I'm creating a Web API and I'm using dependency inject wit Ninject.
I have:
IRTWRepository
IModelFactory
I'm injecting those 2 into my controllers like this.
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IRTWRepository>().To<RTWRepository>();
kernel.Bind<RTWAPIContext>().To<RTWAPIContext>();
kernel.Bind<IModelFactory>().To<ModelFactory>();
}
My RTWRepository constructor looks like this
public class RTWRepository :IRTWRepository
{
private RTWAPIContext _context;
public RTWRepository(RTWAPIContext context)
{
_context = context;
}}
My ModelFactory constructor looks like this
public class ModelFactory : IModelFactory
{
private IRTWRepository _repo;
public ModelFactory(IRTWRepository repo)
{
_repo = repo;
}
}
I have a Controller that looks like this:
public MaterialsController(IRTWRepository repository,IModelFactory modelFactory)
: base(repository,modelFactory)
{
}
Now, my question is : Is Ninject creating 2 separate contexts when creating an instance of my RTWRepository and also when creating an instance of ModelFactory?.
The reason that I'm asking that is because I'm having a problem when I try to save an entity that has a dependency to another object which was previously retrieve from the db.
I'm saving the entity in my controller but I'm creating it in my model factory along with is dependency.
public class RecycleCenter
{
public RecycleCenter()
{
}
public int MyProperty { get; set; }
[Required]
public virtual Address Address { get; set; }
}
The code above is for the entity Recycle Center which has an Address, this recycle center entity is created in my model factory and then in my controller I try to save it but when my repository execute this line
_context.RecycleCenters.Add(entity);
I'm getting this error
An entity object cannot be referenced by multiple instances of IEntityChangeTracker
So, somewhere in my code I'm using 2 context instead of 1 and I think is when creating the ModelFactory and RTWRepository, is this assumption correct?, if so how do I fix it?
TL;DR;
You probably need to change this line:
kernel.Bind<RTWAPIContext>().To<RTWAPIContext>();
to
kernel.Bind<RTWAPIContext>().To<RTWAPIContext>().InRequestContext();
Explanation:
When you define a binding in Ninject, you also specify how that object's lifecycle should be handled.
If you don't explicitly define it, Ninject's default lifecycle is Transient. Transient means that each time an instance is required, it will create a new one. In your case, you need to two instances: one for the RTWRepository of the ModelFactory and one for the RTWRepository of the MaterialsController.
You can modify the lifestyle to one of these options:
Singleton ==> kernel.Bind<RTWAPIContext>().To<RTWAPIContext>().InSingleTonScope();
Request ==> kernel.Bind<RTWAPIContext>().To<RTWAPIContext>().InRequestScope();
Thread ==> kernel.Bind<RTWAPIContext>().To<RTWAPIContext>().InThreadScope();
Named, Call, Parent, Custom
In your case, I think you need InRequestScope, but you have to check the necessary lifecycle as it depends on the application.
For further information please check out the documentation here: https://github.com/ninject/ninject/wiki/Object-Scopes
Most probably, it is. There's no annotation that is telling to Ninject "Hey, stop, when you have created the instance once, reuse it". You should agree that in most cases, you would want multiple instances of an object and that it is a rare case, where you want it only once.
If you want to reuse the instance, use the singleton pattern. Ninject is familiar with it, so you can bind the object mapping to a method
kernel.Bind<RTWAPIContext>().ToMethod(c => RTWAPIContext.GetInstance());
There is also a ToSingleton binding, but I bet you cannot make your context constructor private and implement C# specific singleton due to other ASP.NET problems (e.g. ASP.NET Identity will try to invoke the context's method for object creation).
I apologize if this has been answered in a previous post; I just couldn't find the answer specific to my question.
I was watching a MVC tutorial video and in one of their controllers, they kept instantiating a repository in every method that they needed it for. I'm used to this from previous ASP.NET DAL, but they made this comment:
... I shouldn't be instantiating the repository in every method, I
should be making use of MVC's Services...
I'm curious to know what this means exactly and if instantiating a repository in my controller methods where I need it will be bad practice.
I'm not an expert, but I think you should instantiate your repository only once in the Controller's constructor like this:
public interface ISampleRepository { }
public class SampleRepository : ISampleRepository { }
public class HomeController : Controller
{
private ISampleRepository myRepository;
public HomeController()
{
myRepository = new SampleRepository();
}
}
You don't have to instance your repository in every Action Method. I'm not sure but I think every time you instantiate your repository a new Database Context is created.
So, instantiating the same repository multiple times seems overkilling to me.
I have a base class that has a repository injected into the constructor, now any class that I derive off of it now also needs those parameters, but the repository is only used by the base class, is there a way to bind Ninject to just the base class and not go through the constructor? And most importantly, is it a good idea?
public class HtmlPageModel
{
private readonly IHtmlPageRepository _repository;
public HtmlPageModel (IHtmlPageRepository repository)
{
_repository = repository;
}
}
public class VideoPageViewModel : HtmlPageModel
{
public VideoPageViewModel(IHtmlPageRepository repository) : base(repository)
{
}
}
View models shouldn't have dependencies at all. Inject the repository into the controller and assign the values from there.
Also if many pages are using the same base view model this indicates that some part of the page is shown in many situations or even all the time. In this case it is better to have a custom controller and view for this area and use Html.RenderAction to render this part.
I still stand to my previous comment: You have to rethink your class hierarchy, this is not something you should work around with Ninject.
If your base class accepts a repository, so should all of your derived classes. As an alternative you could however inject a specially NullRepository into your VideoPageViewModel which basically does nothing (see Null Object pattern)
Binding by target type can be achieved using WhenInjectedInto() in Ninject:
kernel.Bind<IHtmlPageRepository>()
.To<HtmlPageRepository>()
.WhenInjectedInto<HtmlPageModel>();
kernel.Bind<IHtmlPageRepository>()
.To<NullRepository>()
.WhenInjectedInto<VideoPageViewModel>();
This is basically the same answer as brokenGlass. Why not just create another base class, and move the attributes/functionality from the your current base class into that one, but don't move the constructor/functionality that relates to the repository.
Sorry for the vague title. I'll explain what I'm trying to get working.
My Admin View uses an Admin ViewModel which in turn calls an Admin Service which is a collection of Admin Repositories. For the sake of simplicity lets say that there is only one repository referenced by the Admin Service.
When constructing my Admin View Model the top of my class looks like this
public class adminMenuVM
{
private readonly IAdminMenuService menuService;
public adminMenuVM(IAdminMenuService adminMenuService)
{
this.menuService = adminMenuService;
}
public adminMenuVM()
{
menuItems = getMenuItems();
menuCats = getMenuCats();
}
}
Where getMenuItems and getMenuCats are two methods that use the service to retrieve data which is then set for the view model.
My problem is that when I run the app I get the error "Object Reference not set to instance of an object". Now I know that this is because the adminMenuVM() is called by the viwModel and not the constructor initializing the service.
My question is how can I ensure that the service constructor is called and does its thing when my view model is called?
I've tried using :this on the parameterless constructor but it wont allow me to assign an interface since you can't create a new instance of an interface.
Edit # 18:49
I've had a thought but don't know how to implement it. Can anyone advise on how I could "Constructor Chain" or if it is possible? My thought is to chain the parameterless constructor to service constructor this ensuring it gets called. I may be way off but would be grateful for yer help.
The first constructor looks like it should be used by some Dependency Injection container.
You can use just one constructor to create an instance. When that DI creates an instance with a supplied "IAdminMenuService", then you can create a new instance by using the second constructor, but as that's a new instance, the "menuService" will be empty (null).
You will need to find out how to get tan instance of your adminMenuVM from the DI container (if that is what you use).
Remove that default constructor and put its contents into the constructor with the parameter.
Managed to figure this one out. Its not the cleanest solution but I can't see any other way to do it. I'll stick this in to help anyone else who comes across this issue.
The issue boiled down to passing the current instance of the service interface to the ViewModel. The only place I could find that the instance was being set was the constructor of my controller. So very simply I declared a public global variable of the interface service type and saved the instance to that. The current instance persits and can be passed to the constructor of my viewmodel and satisfy the viewmodel constructors requirement. To clarify I've included an example of the controller and a viewmodel.
Controller:
public class AdminController : Controller
{
private readonly IAdminMenuService lmService;
public IAdminMenuService testService;
public AdminController(IAdminMenuService layoutMarkupService)
{
this.lmService = layoutMarkupService;
testService = lmService;
}
public ActionResult Index()
{
return View();
}
public PartialViewResult menuPartial()
{
return PartialView("_AdminMenuPartial", new adminMenuVM(testService));
}
ViewModel:
public class adminMenuVM
{
private readonly IAdminMenuService menuService;
public adminMenuVM(IAdminMenuService AdminMenuService)
{
this.menuService = AdminMenuService;
menuItems = getMenuItems();
menuCats = getMenuCats();
}
}