0

我正在尝试掌握 WebAPI。我一直在查看 codplex 上的 EFMVC 项目:http ://efmvc.codeplex.com/ 根据此博客http://blogs.realdolmen.com/experts/2012/08/ ,我选择使用 MEF 作为我的依赖解析器31/mef-in-asp-net-mvc-4-and-webapi/,但测试表明 DBContext 在多个 Web 请求中被重用。我假设依赖解析器会在每个 Web 请求上为我提供一个新容器,但似乎这并没有发生。

我不确定如何进步。所以这是我到目前为止所拥有的:

配置:

    public static class MefConfig
{
    public static void RegisterMef()
    {
        var container = ConfigureContainer();

        ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(container));

        var dependencyResolver = System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver;
        System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver = new MefDependencyResolver(container);
    }

    private static CompositionContainer ConfigureContainer()
    {
        var assemblyCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
        var container = new CompositionContainer(assemblyCatalog);

        return container;
    }
}

public class MefDependencyResolver : IDependencyResolver
{
    private readonly CompositionContainer _container;

    public MefDependencyResolver(CompositionContainer container)
    {
        _container = container;
    }

    public IDependencyScope BeginScope()
    {
        return this;
    }

    public object GetService(Type serviceType)
    {
        var export = _container.GetExports(serviceType, null, null).SingleOrDefault();

        return null != export ? export.Value : null;
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        var exports = _container.GetExports(serviceType, null, null);
        var createdObjects = new List<object>();

        if (exports.Any())
        {
            foreach (var export in exports)
            {
                createdObjects.Add(export.Value);
            }
        }

        return createdObjects;
    }

    public void Dispose()
    {
        ;
    }
}

public class MefControllerFactory : DefaultControllerFactory
{
    private readonly CompositionContainer _compositionContainer;

    public MefControllerFactory(CompositionContainer compositionContainer)
    {
        _compositionContainer = compositionContainer;
    }

    protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
    {
        var export = _compositionContainer.GetExports(controllerType, null, null).SingleOrDefault();

        IController result;

        if (null != export)
        {
            result = export.Value as IController;
        }
        else
        {
            result = base.GetControllerInstance(requestContext, controllerType);
            _compositionContainer.ComposeParts(result);
        }

        return result;
    }
}

我的 Controller 类有它的构造函数,因此:

    [ImportingConstructor]
    public MyApiController(ICommandBus commandBus, IMyRepository repository)
    {
        if (repository == null)
            throw new ArgumentException("My Repository cannot be null!");

        if (commandBus == null)
            throw new ArgumentException("CommandBus cannot be null");

        this._repository = repository;
        this._commandBus = commandBus;

    }

在网络 POST 上,根据 EFMVC 项目,我创建了一个 CreateItemCommand,并通过在 commandBus 上调用 Submit(CreateItemCommand),CommandBus 的 Submit 方法如下。

    public void Submit<TCommand>(TCommand command) where TCommand : ICommand
    {
        var handler =    System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(ICommandHandler<TCommand>)) as ICommandHandler<TCommand>;
        if (!((handler != null) && handler is ICommandHandler<TCommand>))
        {
            throw new CommandHandlerNotFoundException(typeof(TCommand));
        }
        handler.Execute(command);
    }

命令处理程序的一个示例是:

[Export(typeof(ICommandHandler<CreateCommand>))]
public class CreateCommandHandler : ICommandHandler<CreateCommand>
{
    private readonly IMyRepository _myRepository;
    private readonly IUnitOfWork _unitOfWork;

    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="myRepository">implementation of MyRepository</param>
    /// <param name="unitOfWork">implementation of UnitOfWork</param>
    [ImportingConstructor]
    public CreateCommandHandler(IMyRepository myRepository, IUnitOfWork unitOfWork)
    {
        if (myRepository == null)
            throw new ArgumentException("MyRepository cannot be NULL!");

        if (unitOfWork == null)
            throw new ArgumentException("UnitOfWork cannot be NULL!");

        this._myRepository = myRepository;
        this._unitOfWork = unitOfWork;
    }

    /// <summary>
    /// Actions the Add Item to db
    /// </summary>
    /// <param name="command">CreateCommand pertaining to the given item</param>
    /// <returns>CommandResult indicating success or otherwise</returns>
    public void Execute(CreateCommand command)
    {
        //CommandResult result = null;
        this._myRepository.Add(command.Item);
        this._unitOfWork.SaveChanges();            
    }
}

工作单元是这样实现的:

[Export(typeof(IUnitOfWork))]
[PartCreationPolicy(System.ComponentModel.Composition.CreationPolicy.NonShared)]
public class UnitOfWork : IUnitOfWork
{
    private readonly IDatabaseFactory databaseFactory;
    private ApiContext dataContext;

    [ImportingConstructor]
    public UnitOfWork(IDatabaseFactory databaseFactory)
    {
        this.databaseFactory = databaseFactory;
    }

    protected ApiContext DataContext
    {
        get { return dataContext ?? (dataContext = databaseFactory.Get()); }
    }

    public void SaveChanges()
    {
        DataContext.SaveChanges();
    }
}

存储库是这样实现的:

    public abstract class RepositoryBase<T> where T : class
{
    private ApiContext dataContext;
    private readonly IDbSet<T> dbset;
    protected RepositoryBase(IDatabaseFactory databaseFactory)
    {
        DatabaseFactory = databaseFactory;
        dbset = DataContext.Set<T>();
    }

    protected IDatabaseFactory DatabaseFactory
    {
        get;
        private set;
    }

    protected ApiContext DataContext
    {
        get { return dataContext ?? (dataContext = DatabaseFactory.Get()); }
    }

    protected IDbSet<T> DbSet
    {
        get
        {
            return this.dbset;
        }
    }

    public virtual void Add(T entity)
    {
        dbset.Add(entity);
    }
    public virtual void Update(T entity)
    {
        dbset.Attach(entity);
        dataContext.Entry(entity).State = EntityState.Modified;
    }
    public virtual void Delete(T entity)
    {
        dbset.Remove(entity);
    }
    public virtual void Delete(Expression<Func<T, bool>> where)
    {
        IEnumerable<T> objects = dbset.Where<T>(where).AsEnumerable();
        foreach (T obj in objects)
            dbset.Remove(obj);
    }
    public virtual T GetById(Guid id)
    {
        return dbset.Find(id);
    }
    public virtual IQueryable<T> Get()
    {
        return dbset;
    }
}

我想我理解为什么要实现 DB Factory:它确保 UnitOfWork 和 commandHandler 获得相同的上下文,以便在 Command 处理程序中 Execute 方法起作用。

但是由于似乎每个 Web 请求都获得了相同的 MEF 容器,因此每个 Web 请求都获得了相同的 DB 工厂,该工厂提供单例上下文,因此迫使我在 Web 调用之间共享上下文。

谁能指导我如何更改它以确保每个 Web 请求都在数据库上下文中?如果我选择 MEF 作为容器,EFMVC 是一个糟糕的项目吗?

[编辑] 对此进行更多思考,我担心如果同一个 MEF 容器在所有 Web 请求中实现,在 100,000 个请求之后会发生什么,MEF 容器现在是否会因引用 MEF 拥有的所有非共享控制器类而变得臃肿为每个请求更新。据我了解,当我们在容器上调用 dispose 时,MEF 将处理它创建的对象。

我显然在这里误解了一些东西。对此的任何澄清将不胜感激。

4

1 回答 1

1

在您引用的文章中,它说,

如果您确实需要每个请求的有限范围,“BeginScope”方法总是需要返回一个 IDependencyScope 类型的新对象。

于 2013-06-04T17:08:04.753 回答