Asp.net web api WebAPI:EFMVC+;MEF在每个web调用上提供相同的单例数据库上下文
我正在努力掌握WebAPI。我一直在关注codplex上的EFMVC项目:根据本博客,我选择使用MEF作为依赖项解析程序,但测试表明DBContext正在跨多个web请求重用。我曾经假设依赖解析程序会在每个web请求上为我提供一个新的容器,但似乎这并没有发生 我真的不知道如何进步。以下是我到目前为止的情况: MefConfig:Asp.net web api WebAPI:EFMVC+;MEF在每个web调用上提供相同的单例数据库上下文,asp.net-web-api,entity-framework-5,Asp.net Web Api,Entity Framework 5,我正在努力掌握WebAPI。我一直在关注codplex上的EFMVC项目:根据本博客,我选择使用MEF作为依赖项解析程序,但测试表明DBContext正在跨多个web请求重用。我曾经假设依赖解析程序会在每个web请求上为我提供一个新的容器,但似乎这并没有发生 我真的不知道如何进步。以下是我到目前为止的情况: MefConfig: public static class MefConfig { public static void RegisterMef() {
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;
}
}
在一篇web文章中,根据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(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;
}
}
公共抽象类RepositoryBase,其中T:class
{
私有ApiContext数据上下文;
专用只读IDbSet数据库集;
受保护的RepositoryBase(IDatabaseFactory databaseFactory)
{
DatabaseFactory=DatabaseFactory;
dbset=DataContext.Set();
}
受保护的IDatabaseFactory数据库工厂
{
得到;
私人设置;
}
受保护的ApiContext数据上下文
{
获取{return dataContext???(dataContext=DatabaseFactory.get());}
}
受保护IDbSet数据库集
{
得到
{
返回此.dbset;
}
}
公共虚拟空添加(T实体)
{
添加(实体);
}
公共虚拟无效更新(T实体)
{
附加数据集(实体);
dataContext.Entry(entity).State=EntityState.Modified;
}
公共虚拟作废删除(T实体)
{
删除(实体);
}
公共虚拟void Delete(表达式where)
{
IEnumerable objects=dbset.Where(Where).AsEnumerable();
foreach(对象中的对象)
dbset.Remove(obj);
}
公共虚拟T GetById(Guid id)
{
返回dbset.Find(id);
}
公共虚拟IQueryable Get()
{
返回dbset;
}
}
我想我理解为什么要实现DB工厂:它确保UnitOfWork和commandHandler获得相同的上下文,以便在命令处理程序中Execute方法可以工作
但是,由于似乎每个web请求都获得相同的MEF容器,因此每个web请求都获得相同的DB工厂,该工厂为单例上下文提供服务,从而迫使我在web调用之间共享上下文
是否有人可以指导我如何更改此设置,以确保每个web请求都在db上下文中获取?如果我选择MEF作为容器,EFMVC是否是一个糟糕的项目
[编辑]
进一步考虑这个问题,我担心如果相同的MEF容器在所有web请求中都出现,那么在100000个请求之后会发生什么,MEF容器现在是否会因为引用MEF为每个请求新创建的所有非共享控制器类而变得臃肿。据我所知,当我们在容器上调用dispose时,MEF将olny dispose它创建的对象
我显然误解了一些事情。对此的任何澄清都将不胜感激。在您引用的文章中说 如果每个请求确实需要有限的作用域,“BeginScope” 方法始终需要返回IDependencyScope类型的新对象
是的,显然需要对依赖解析程序进行更多的研究。因此,与其返回“this”;我是否应该返回“new MefDependencyResolver(ConfigureContainer());”好的,我已经将此标记为答案,但我仍然不知道如何利用MEF并根据存储库模式和工作单元为每个请求保证一个新的DBContext,所以我选择使用依赖项解析器,如上所示,在每个ApiController中创建一个新的DBContext,这种方式并不完美,但它将保证每个请求都有一个新的上下文。由于DBCOntext似乎是工作单元的一个实现,我想通过将ORM耦合到控制器实例并不是真正的失败。
[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;
}
}