3

I am working with a CRM product that uses ASP.net MVC 3.0, Entity Framework and Windsor for IOC container.

I have injected the services which are work with repository layer to controller through Windsor.

However, I have implemented the strategy pattern for my ContactController to facilitates MyProfile, CustomerProfile, CompanyProfile.

Now my ContactContext class is depended on my ContactController class so that i can't inject ContactContext using Windsor as a result of that i have created instance of ContactContext in ContactController.

ContactController class implementation

 public class ContactController:BaseController
 {
    private ContactContext _contactContext;

    public ContactController(PersonService personService, CompanyService customerService)
    {
        _contactContext = new ContactContext(personService, customerService, this);
    }

    public ActionResult ContactProfile(string profileId, string profileType)
    {
       base.ValidateContactProfileIdAndProfileTypeInfo(profileType, profileId);
       return _contactContext.RenderContactProfile(ProfileType, ProfileId);
    }
  }

ContactContext implementation

public class ContactContext
{
    private Dictionary<ProfileType, IContactStrategy> _strategies =
    new Dictionary<ProfileType, IContactStrategy>();

    private BaseController _controller;

    public ContactContext(PersonService personService, CompanyService companyService, BaseController controller)
    {
        _strategies.Add(ProfileType.MyProfile, new MyProfileStrategy(personService));
        _strategies.Add(ProfileType.CustomerProfile, new PersonStrategy(personService));
        _strategies.Add(ProfileType.CompanyProfile, new CompanyStrategy(companyService));

        _controller = controller;
    }

    public ActionResult RenderProfileInfo(ProfileType profileType, long profileId)
    {
        return _strategies[profileType].GenerateProfileInfoView(profileId, _controller);
    }

    public ActionResult RenderPeopleInfo(ProfileType profileType, long profileId)
    {
        return _strategies[profileType].GeneratePeopleInfoView(profileId, _controller);
    }
 }

Strategies go like this

public class PersonStrategy:IContactStrategy
{
    private PersonService _personService;

    public PersonStrategy(PersonService personService)
    {
        _personService = personService;
    }


    #region Implementation of IContactStrategy

    public ActionResult GenerateProfileInfoView(long profileId, BaseController controller)
    {
        //TODO: Load Profile info from service
        PersonDetailsViewModel personDetailsViewModel = new PersonDetailsViewModel();
        personDetailsViewModel.Name = "Robert Martin";

        return controller.RenderPartialView("ProfileInfo", personDetailsViewModel);
    }

    public ActionResult GeneratePeopleInfoView(long profileId, BaseController controller)
    {
        //TODO: Load people from service
        return controller.RenderPartialView("PeopleView", new List<PersonLiteViewModel>());
    }
  }

public class CompanyStrategy : IContactStrategy
{
    private CompanyService _companyService;

    public CompanyStrategy(CompanyService companyService)
    {
        _companyService = companyService;
    }

    #region Implementation of IContactStrategy

    public ActionResult GenerateView(long profileId, BaseController controller)
    {
        throw new NotImplementedException();
    }

    public ActionResult GenerateProfileInfoView(long profileId, BaseController controller)
    {
        throw new NotImplementedException();
    }
 }

Question: How can i get rid of ContactContext dependency with ContactController?

marvelTracker
  • 4,691
  • 3
  • 37
  • 49
  • Related: http://stackoverflow.com/questions/1453128/is-there-a-good-proper-way-of-solving-the-dependency-injection-loop-problem-in-th – Mark Seemann Sep 06 '11 at 07:26

2 Answers2

1

I think there are a couple of options.

Based on the code examples given (which may not be complete so this may not be possible) you could just get your ContactContext to return a IContactStrategy to the controller and invoke the view there passing in the controller, as then the ContactContext doesn't need to know anything about the controller, it can just be responsible for setting up and farming out the correct implementation of the strategy.

HOWEVER:

Is there a reason to have the PersonService and CompanyService passed into the controller other than to then pass them into the ContactContext constructor? If not then this smells to me. If you are only passing something in so you can create something else using it then usually I think it would be better to just pass in an instance of the thing you are creating instead. Imagine if in the future you add another strategy which needs a VoluteerService or similar. You are going to have to add that to the controller constructor just so you can then pass that to the ContractContext constructor.

In this case I would do something like create a IContactStrategyFactory interface which I would pass an instance of in to the ContactController. This could be done by windsor and the default implementation would have dependencies on the PersonService and CustomerService. then I would either have

  1. a method to get the IContractStrategy by name and call that in the controller (as suggested at the beginning).
  2. have a method to get it passing the name and the controller and the controller and have the factory set the controller in the strategy and return it.
  3. have methods similar to what you have now on your ContactContext, but with an extra parameter (the controller).

Number 1 seems the simplest to me and I would probably go with this. 2. is an option but I don't really like this as having to set the controller outside the constructor means it could be forgotten. Having the factory means it probably won't be, but still... 3 is probably most similar to what you have now and might be the easiest. If you kept the class names you have now you could just remove the BaseController from the ContactContext constructor and instead add it to the GenerateProfileInfoView and GenerateProfileInfoView methods instead and the make the ContactController take ContactContext in the constructor (or an interface extracted from that)

Sam Holder
  • 32,535
  • 13
  • 101
  • 181
  • Factory will be responsible for creating ContactContext and the factory class will be injected by IOC through ContactController constructor. – marvelTracker Sep 06 '11 at 08:27
0

I have implemented factory to create ContactContext and inject it using the container

public class ContactController:BaseController
{
 private ContactContext _contactContext;

 public ContactController(ContactContextFactory contactContextFactory)
 {
    _contactContext = contactContextFactory.GetContactContext(this);
 }
}

public class ContactContextFactory
    {
        private PersonService _personService;
        private CompanyService _customerService;

        public ContactContextFactory(PersonService personService, CompanyService companyService)
        {
            _personService = personService;
            _customerService = companyService;
        }

        public ContactContext GetContactContext(BaseController baseController)
        {
            return new ContactContext(_personService, _customerService, baseController);
        }
    }

Any suggestions or improvements ?

marvelTracker
  • 4,691
  • 3
  • 37
  • 49
  • what would happen if the controller action need to be unit tested? might want to depend on the interfaces there to be able to pass in mocks? – Illuminati Sep 09 '11 at 13:57
  • also be watchful the default controller factory requires ur ctr be parameterless. u might want to implement a custom controller factory. – Illuminati Sep 09 '11 at 14:12
  • We have implemented a custom controller factory and thanks for the comments mate! – marvelTracker Sep 10 '11 at 01:02