StructureMap configuration for DI with asp.net MVC

2019-07-23 13:00发布

问题:

Here is my incomplete StructureMap configuration:

PS: Big apologies for asking you to write my app for me but I'm finding the StructureMap api to be a bit confusing as nearly everything I find when I do google searches refers to the older api.

    public static void Configure(IContainer container)
    {
        container.Configure(c =>
        {
            string connectionString = ConfigurationManager.ConnectionStrings["ConnString"].ConnectionString;
            SQLDataContext dataContext = new SQLDataContext(connectionString);

            c.For<SQLDataContext>().HttpContextScoped();

            c.For<IAdminRepository>().Use<SQLAdminRepository_v2>().Ctor<SQLDataContext>().Is(dataContext);
            c.For<IMemberRepository>().Use<SQLMemberRepository_v2>().Ctor<SQLDataContext>().Is(dataContext);
            c.For<IUtilityRepository>().Use<SQLUtilityRepository_v2>().Ctor<SQLDataContext>().Is(dataContext);

            c.For<IAdminService>().Use<AdminService_v2>();
            c.For<IMemberService>().Use<MemberService_v2>();
            c.For<IUtilityService>().Use<UtilityService_v2>();

            c.For<ResourcePool>().Singleton();

        });
    }

Each Service has a dependency on the corresponding Repository which is passed in via its constructor. e.g. :

public class ContactService_v2 : IContactService
{
    IContactRepository contactRepository;

    public ContactService_v2(IContactRepository contactRepository)
    {
        this.contactRepository = contactRepository;
    }

    public IQueryable<Contact> Get_Contacts()
    {
        return contactRepository.GetContacts();
    }

    public void Save_Contact(Contact contact)
    {
        contactRepository.Save_Contact(contact);
    }
}

So the Service layer is just facade with the real Repository lying behind it. [and before you ask or suggest - No I won't be changing that in a hurry because initially I just want to get the DI working to make the code testable before I start doing anything dramatic.]

Q1: The application uses Linq2Sql data access. It is essential that all the Services sent to each controller have, in their Repository dependency a dependency on the same data context instance, because they may need to join to each other in complex queries made across Repositories. Therefore I want to use a HttpContext lifecycle for the data context. Is this (above) how I do that?

Q2: The ResourcePool used to follow a singleton pattern but I have refactored it to a ordinary class taking a single service as its constructor parameter. Is that right? In this case, will it behave as if it came from the asp.net application cache?

Q3: What are the auto-registration equivalents for the Repository and Service configurations? [PS: Yes I know that the "_v2" suffix is unconventional but there already exist legacy equivalents of each XService and XRepository which I wish to refactor away at my leisure after the DI has been added and is working seamlessly].

Asp.Net MVC allows one to have strong model binding like so :

[HttpPost]
public ActionResult List(AdminDisplay admin)
{
    admin.GetAdminPage();
    return View(admin);
}

In such a case every class (such as AdminDisplay) requires a default parameterless constructor so that the MVC framework can create it. Furthermore any class created inside such a model bound class also needs such a parameterless constructor.

Q4: If I want to replace these parameterless constructors, is it sufficient to just add the entries for them in the StructurMap configuration code? - or do I not need to do that provided that all these model binding classes and their dependents have contructors which only use parameters what StructureMap is able to resolve.

Apologies for the bad formatting above but how do I turn off wysiwyg editing?

PS: It is permissible to provide answer any of the questions Q1, Q2, Q3, Q4 without answering all of them!

回答1:

Q1: Change the code for the data context from:

SQLDataContext dataContext = new SQLDataContext(connectionString);
c.For<SQLDataContext>().HttpContextScoped();

to:

c.For<SQLDataContext>().HybridHttpOrThreadLocalScoped()
.Use(() => new SQLDataContext(connectionString));

Change the code for each repository from:

c.For<IAdminRepository>().Use<SQLAdminRepository_v2>()
.Ctor<SQLDataContext>().Is(dataContext);

to:

c.For<IAdminRepository>().Use<SQLAdminRepository_v2>()
.Ctor<SQLDataContext>();