6

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?

asked Oct 11, 2015 at 12:44
4
  • You need to be able to create this view model later while the application is running, right? for example when the user clicks some button? Maybe based on which button the user has clicked, you will determine which constructor to call, right? Commented Oct 11, 2015 at 12:58
  • Instead of having your ViewModel speak to the repository directly, shouldn't you make it depend on the Contract class only? Why do you need to know if the Contract is new? Commented Oct 11, 2015 at 12:59
  • @YacoubMassad Sure, but the button click handler will call unityContainer.Resolve<ContractWindowViewModel>(). How can I pass parameter through that construct? Commented Oct 11, 2015 at 12:59
  • 2
    The source of your problems is that you inject runtime data (the Contract instance) into the constructor of you components (the ContractWindowViewModel in 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 the Contract using the constructor, define a method on your view model that allows setting the existing contract. Commented Oct 12, 2015 at 7:41

2 Answers 2

7

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));
answered Oct 11, 2015 at 13:24
Sign up to request clarification or add additional context in comments.

Comments

0

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

answered Oct 11, 2015 at 13:21

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.