私は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 は作成したオブジェクトを破棄します。
私は明らかにここで何かを誤解しています。これに関する説明をいただければ幸いです。