I'm introducing Dependency Injection to my project. I've got a dialog window, which serves as an editor to either new or existing entity. Its ViewModel looks like the following:
public class ContractWindowViewModel
{
private IRepository<Contract> contractRepository;
private Contract model;
private bool isNew;
public ContractWindowViewModel(Contract contract, IRepository<Contract> contractRepository)
{
if (contract == null)
throw new ArgumentNullException(nameof(contract));
if (contractRepository == null)
throw new ArgumentNullException(nameof(contractRepository));
this.contractRepository = contractRepository;
this.model = contract;
this.isNew = false;
}
public ContractWindowViewModel(IRepository<Contract> contractRepository)
{
this.contractRepository = contractRepository;
this.model = new Contract();
this.isNew = true;
}
// (...)
}
The plan is to inject IRepository<Contract>. But sometimes I need to pass Contract to the ViewModel (if I want to edit an existing one) or not (if I want to create new one). How should I do that using Unity?
2 Answers 2
Using the DI container from your code (e.g. in a button event handler) is called Service Location and is considered to be an anti-pattern.
You should instead have an factory that allows you to create your view model from inside your button handler (or your ViewModel command).
Here is an example of a factory:
public interface IContractWindowViewModelFactory
{
ContractWindowViewModel CreateForNewContract();
ContractWindowViewModel CreateForExistingContract(Contract existing_contract);
}
public class ContractWindowViewModelFactory : IContractWindowViewModelFactory
{
private readonly IRepository<Contract> m_Repository;
public ContractWindowViewModelFactory(IRepository<Contract> repository)
{
m_Repository = repository;
}
public ContractWindowViewModel CreateForNewContract()
{
return new ContractWindowViewModel(m_Repository);
}
public ContractWindowViewModel CreateForExistingContract(Contract existing_contract)
{
return new ContractWindowViewModel(existing_contract, m_Repository);
}
}
Now you need to inject IContractWindowViewModelFactory into the class that needs to be able to create the ContractWindowViewModel view model (e.g. via constructor injection).
Since you are using a DI container, you need to register IContractWindowViewModelFactory with ContractWindowViewModelFactory in the Composition Root. You also need to register IRepository<Contract> (which I am guessing you have already done).
Now, inside your button handler (or command handler), you can use the factory to create a ContractWindowViewModel for a new or existing Contract.
If for some reason, you still want to use the container from the button handler (which I encourage you not to do), then you can use named registrations like this:
In your composition root:
container.RegisterType<ContractWindowViewModel>(
"ForNew",
new InjectionConstructor(
new ResolvedParameter<IRepository<Contract>>()));
container.RegisterType<ContractWindowViewModel>(
"ForExisting",
new InjectionConstructor(
new ResolvedParameter<Contract>(),
new ResolvedParameter<IRepository<Contract>>()));
In your handler, you can use this for new contracts:
var viewmodel_for_new = container.Resolve<ContractWindowViewModel>("ForNew");
Or this for existing contracts:
Contract existing_contract = ...
var viewmodel_for_existing = container.Resolve<ContractWindowViewModel>(
"ForExisting",
new ParameterOverride("contract", existing_contract));
Comments
May be something like this...
unityContainer.Resolve<ContractWindowViewModel>(
new ResolverOverride[] {
new ParameterOverride("contract", new Contract()),
new ParameterOverride("contractRepository", new Repository<Contract>())
});
Source http://mikaelkoskinen.net/post/unity-passing-constructor-parameters-to-resolve
Comments
Explore related questions
See similar questions with these tags.
Contractclass only? Why do you need to know if theContractis new?unityContainer.Resolve<ContractWindowViewModel>(). How can I pass parameter through that construct?Contractinstance) into the constructor of you components (theContractWindowViewModelin this case). Your object graphs should be stateless, and runtime data should be passed through an existing object graph using method calls. Any time you violate that idea, you make everything much harder (as you are already experiencing). So instead of injecting theContractusing the constructor, define a method on your view model that allows setting the existing contract.