我正在尝试掌握 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 将处理它创建的对象。
我显然在这里误解了一些东西。对此的任何澄清将不胜感激。