我在使用依赖注入方面还很陌生,我认为我必须忽略一些非常简单的事情.
我有一个Web API项目,在这里注册通用存储库.存储库在其构造函数中将dbContext作为参数.
我发现很奇怪的行为是,我可以对服务进行一次成功调用,但是随后的任何调用都告诉我dbcontext已被处置.我确实有一个using语句,但这应该不是问题,因为DI应该为每个Web请求创建依赖项的新实例(尽管我可能错了).
这是我的通用存储库:
public class GenericRepository<T> : IGenericRepository<T> where T : class{ internal DbContext _context; internal DbSet<T> _dbSet; private bool disposed; public GenericRepository(DbContext context) { _context = context; _dbSet = _context.Set<T>(); } /// <summary> /// This constructor will set the database of the repository /// to the one indicated by the "database" parameter /// </summary> /// <param name="context"></param> /// <param name="database"></param> public GenericRepository(string database = null) { SetDatabase(database); } public void SetDatabase(string database) { var dbConnection = _context.Database.Connection; if (string.IsNullOrEmpty(database) || dbConnection.Database == database) return; if (dbConnection.State == ConnectionState.Closed) dbConnection.Open(); _context.Database.Connection.ChangeDatabase(database); } public virtual IQueryable<T> Get() { return _dbSet; } public virtual T GetById(object id) { return _dbSet.Find(id); } public virtual void Insert(T entity) { _dbSet.Add(entity); } public virtual void Delete(object id) { T entityToDelete = _dbSet.Find(id); Delete(entityToDelete); } public virtual void Delete(T entityToDelete) { if (_context.Entry(entityToDelete).State == EntityState.Detached) { _dbSet.Attach(entityToDelete); } _dbSet.Remove(entityToDelete); } public virtual void Update(T entityToUpdate) { _dbSet.Attach(entityToUpdate); _context.Entry(entityToUpdate).State = EntityState.Modified; } public virtual void Save() { _context.SaveChanges(); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposed) return; if (disposing) { //free managed objects here _context.Dispose(); } //free any unmanaged objects here disposed = true; } ~GenericRepository() { Dispose(false); }}
这是我的通用存储库接口:
public interface IGenericRepository<T> : IDisposable where T : class{ void SetDatabase(string database); IQueryable<T> Get(); T GetById(object id); void Insert(T entity); void Delete(object id); void Delete(T entityToDelete); void Update(T entityToUpdate); void Save();}
这是我的WebApiConfig:
public static class WebApiConfig{ public static void Register(HttpConfiguration config) { // Web API configuration and services var container = new UnityContainer(); container.RegisterType<IGenericRepository<Cat>, GenericRepository<Cat>>(new HierarchicalLifetimeManager(), new InjectionConstructor(new AnimalEntities())); container.RegisterType<IGenericRepository<Dog>, GenericRepository<Dog>>(new HierarchicalLifetimeManager(), new InjectionConstructor(new AnimalEntities())); config.DependencyResolver = new UnityResolver(container); config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html")); // Web API routes config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); }}
这是我的DependencyResolver(非常标准):
public class UnityResolver : IDependencyResolver{ protected IUnityContainer container; public UnityResolver(IUnityContainer container) { this.container = container ?? throw new ArgumentNullException(nameof(container)); } public object GetService(Type serviceType) { try { return container.Resolve(serviceType); } catch (ResolutionFailedException) { return null; } } public IEnumerable<object> GetServices(Type serviceType) { try { return container.ResolveAll(serviceType); } catch (ResolutionFailedException) { return new List<object>(); } } public IDependencyScope BeginScope() { var child = container.CreateChildContainer(); return new UnityResolver(child); } public void Dispose() { Dispose(true); } protected virtual void Dispose(bool disposing) { container.Dispose(); }}
最后,这是给我带来麻烦的控制器的一部分:
public class AnimalController : ApiController{ private readonly IGenericRepository<Cat> _catRepo; private readonly IGenericRepository<Dog> _dogPackRepo; public AnimalController(IGenericRepository<Cat> catRepository, IGenericRepository<Dog> dogRepository) { _catRepo = catRepository; _dogRepo = dogRepository; } [HttpGet] public AnimalDetails GetAnimalDetails(int tagId) { var animalDetails = new animalDetails(); try { var dbName = getAnimalName(tagId); if (dbName == null) { animalDetails.ErrorMessage = $"Could not find animal name for tag Id {tagId}"; return animalDetails; } } catch (Exception ex) { //todo: add logging Console.WriteLine(ex.Message); animalDetails.ErrorMessage = ex.Message; return animalDetails; } return animalDetails; } private string getAnimalName(int tagId) { try { //todo: fix DI so dbcontext is created on each call to the controller using (_catRepo) { return _catRepo.Get().Where(s => s.TagId == tagId.ToString()).SingleOrDefault(); } } catch (Exception e) { //todo: add logging Console.WriteLine(e); throw; } } }
_catRepo对象周围的using语句行为不符合预期.在我进行第一个服务调用后,_catRepo被处理掉了.在随后的调用中,我希望实例化一个新的_catRepo.但是,情况并非如此,因为我遇到的错误是关于dbcontext被处置的.
我试图将LifeTimeManager更改为其他可用的功能,但这无济于事.
我也开始沿着另一条路线走,通用存储库将采用第二个通用类,并从中实例化其自己的dbcontext.但是,当我这样做时,Unity找不到控制器的单参数构造函数.
我想,根据下面的评论,我真正需要的是一种基于每个请求实例化DbContext的方法.我不知道该怎么做.
任何提示将不胜感激.
解决方法:
让我们来看看您的注册:
container.RegisterType<IGenericRepository<Cat>, GenericRepository<Cat>>( new HierarchicalLifetimeManager(), new InjectionConstructor(new AnimalEntities()));container.RegisterType<IGenericRepository<Dog>, GenericRepository<Dog>>( new HierarchicalLifetimeManager(), new InjectionConstructor(new AnimalEntities()));
您将在启动时创建两个AnimalEntities实例,但是这些实例在整个应用程序期间将被重用.这是一个terrible idea.您可能打算拥有one DbContext per request,但是InjectionConstructor包装的实例是一个常量.
您应该将配置更改为以下内容:
container.RegisterType<IGenericRepository<Cat>, GenericRepository<Cat>>( new HierarchicalLifetimeManager());container.RegisterType<IGenericRepository<Dog>, GenericRepository<Dog>>( new HierarchicalLifetimeManager());// Separate 'scoped' registration for AnimalEntities.container.Register<AnimalEntities>( new HierarchicalLifetimeManager() new InjectionFactory(c => new AnimalEntities()));
这要简单得多,现在AnimalEntities也已注册为“作用域”.
这样做的好处是,一旦作用域(Web请求)结束,Unity现在将处置AnimalEntities.这样可以避免您必须对AnimalEntities的使用者实施IDisposable,如here和here所述.
来源:https://www.icode9.com/content-4-521101.html联系客服