Strategy pattern creates a dependency with Controller class in Asp.net MVC 3.0
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)
{
bas开发者_开发知识库e.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
?
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
- a method to get the
IContractStrategy
by name and call that in the controller (as suggested at the beginning). - 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.
- 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)
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 ?
精彩评论