开发者

Strategy pattern creates a dependency with Controller class in Asp.net MVC 3.0

开发者 https://www.devze.com 2023-04-02 10:40 出处:网络
I am working with a CRM product that uses ASP.net MVC 3.0, Entity Framework and Windsor for IOC container.

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

  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)


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 ?

0

精彩评论

暂无评论...
验证码 换一张
取 消