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 要求ごとに新しいコンテナーを提供すると想定していましたが、そうではないようです。

どのように進歩するかはよくわかりません。だからここに私がこれまで持っているものがあります:

MefConfig:

    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;
    }
}

私のコントローラークラスにはコンストラクターがあります:

    [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;

    }

Web 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();            
    }
}

Unit of Work は次のように実装されます。

[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リクエストが確実にdbコンテキストにあるようにするには、これをどのように変更する必要があるかについて、誰かが私を案内できますか? コンテナーとして MEF を選択した場合、EFMVC は悪いプロジェクトになりますか?

[編集] これについてさらに考えてみると、同じ MEF コンテナーがすべての Web 要求で具体化されている場合、100,000 要求の後に何が起こるか、MEF コンテナーが MEF が持っているすべての非共有コントローラー クラスへの参照で肥大化するのではないかと心配しています。リクエストごとに更新されます。私が理解しているように、コンテナで dispose を呼び出すと、MEF は作成したオブジェクトを破棄します。

私は明らかにここで何かを誤解しています。これに関する説明をいただければ幸いです。

4

1 に答える 1