简体   繁体   中英

StructureMap - How to register and resolve an open generic type

public interface IRepository<T> where T : Entity
{
    void Delete(T entity);
    T[] GetAll();
    T GetById(int id);
    void SaveOrUpdate(T enity);
    void Merge(T entity);
}

public interface ITeamEmployeeRepository : IRepository<TeamEmployee>
{
    PagedList<TeamEmployee> GetPagedTeamEmployees(int pageIndex, int pageSize);
}


public class Repository<T> : IRepository<T> where T : Entity
{
    private readonly ISession _session;

    protected Repository()
    {
        _session = GetSession();
    }

    public virtual void Delete(T entity)
    {
        _session.Delete(entity);
    }

    public virtual T[] GetAll()
    {
        return _session.CreateCriteria<T>().List<T>().ToArray();
    }

    public virtual T GetById(int id)
    {
        return _session.Get<T>(id);
    }

    public virtual void SaveOrUpdate(T enity)
    {
        _session.SaveOrUpdate(enity);
    }

    public void Merge(T entity)
    {
        _session.Merge(entity);
    }

    protected ISession GetSession()
    {
        return new SessionBuilder().GetSession();
    }
}

public class TeamEmployeeRepository : Repository<TeamEmployee>, ITeamEmployeeRepository
{
    public PagedList<TeamEmployee> GetPagedTeamEmployees(int pageIndex, int pageSize)
    {
        return GetSession().QueryOver<TeamEmployee>()
            .Fetch(x => x.Employee).Eager
            .Fetch(x => x.Team).Eager
            .ToPagedList(pageIndex, pageSize);
    }
}

For now I register the repository as follows:

For<ILoanedItemRepository>().Use<LoanedItemRepository>();
For<ITeamEmployeeRepository>().Use<TeamEmployeeRepository>();
For<IArticleRepository>().Use<ArticleRepository>();
For<ISalesmanRepository>().Use<SalesmanRepository>();
For<ISalesmanArticleRepository>().Use<SalesmanArticleRepository>();
For<IGoodsGroupRepository>().Use<GoodsGroupRepository>();
For<IEmployeeRepository>().Use<EmployeeRepository>();

This is really cumbersome, especially if there comes along new repositories.

An easier and better registration would be:

For(typeof(IRepository<>)).Use(typeof(Repository<>));

But this does not work. StructureMap is everytime saying me that no Default Instance defined for PluginFamily Core.Domain.Bases.Repositories.ITeamEmployeeRepository.

I searched on stackoverflow and found something new:

Scan(x =>
{
    x.AssemblyContainingType(typeof(TeamEmployeeRepository));
    x.AddAllTypesOf(typeof (IRepository<>));
    x.ConnectImplementationsToTypesClosing(typeof(IRepository<>));
});

But still the same error message.

How do I have to register my repositories with StructureMap 2.6.1.0?

I' ve found a solution.

Scan(x =>
{
    x.WithDefaultConventions();
    x.AssemblyContainingType(typeof(TeamEmployeeRepository));
    x.AddAllTypesOf(typeof(Repository<>));
    x.ConnectImplementationsToTypesClosing(typeof(IRepository<>));
});

WithDefaultConventions is the important part of the shown code, because with this setting you say StructureMap to use the convention of mappping ITeamEmployeeRepository to TeamEmployeeRepository. So StructureMap proceed from the assumption that the class is named like the name of the interface without the prefix I .

I found this question googling "structuremap resolve generic". The existing answers are good but complex. For those seeking simple answer: for interface ISome and implementing class Some we write

c.For<ISome>().Use<Some>()

While for generic ISome<T> and implementing class Some<T> we write

c.For(typeof(ISome<>)).Use(typeof(Some<>))

And that's all

I recently solved something similar by doing a small redesign, which made everything so much simpler. This might work for you as well. You might try removing the specific interfaces such as ITeamEmployeeRepository and ILoanedItemRepository from your design. The way I did this was by using extension methods. Here is an example:

public static class RepositoryExtensions
{
    public static TeamEmployee GetById(
        this IRepository<TeamEmployee> repository, int id)
    {
        return repository.Single(e => e.TeamEmployeeId == id);
    }

    public static IQueryable<Salesman> GetActiveSalesmen(
        this IRepository<ISalesmanRepository> repository)
    {
        return repository.Where(salesman => salesman.Active);
    }

    // etc
}

After that I created an IRepositoryFactory that allowed me to create repositories of a certain type:

public interface IRepositoryFactory
{
    IRepository<T> CreateNewRepository<T>();
}

When having this interface in place it is easy to create an implementation of this factory that asks the container to create a concrete Repository<T> . The RepositoryFactory could look like this:

public class RepositoryFactory : IRepositoryFactory
{
    public IRepository<T> CreateNewRepository<T>()
    {
        return ObjectFactory.GetInstance(typeof(Repository<T>));
    }
}

With this design you only have to register the concrete RepositoryFactory by its IRepositoryFactory interface and you are done. Instead of injecting IRepository<ITeamEmployeeRepository> in the old design, you now inject an IRepositoryFactory and let the client call the CreateNewRepository<T> method. Because of the use of extension methods you can call type specific methods on the repository.

Another advantage of this is that you don't need to re-implement the methods that you initially defined on ITeamEmployeeRepository on every implementation.

This design worked very well in my situation, especially because my IRepository<T> interfaces make use of expression trees. Of course it is not possible for me to see if such design works for you but I hope it will.

Good luck.

You will need to create your own ITypeScanner and register it in your Scan() call. Look at the source code of GenericConnectionScanner as a starting point. Instead of searching the types to see if they implement IRepository<T> , you will look to see if they implement any interface that implements IRepository<T> , and then register the type for that interface.

UPDATE: All the talk of IRepository<T> had me over-thinking this, when it's really an irrelevant details. Just use the DefaultConventions scanner as suggested by Rookian.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM