gpt4 book ai didi

c# - 在 WebApi 项目上使用 Unity 依赖注入(inject)时 DbContext 被释放

转载 作者:行者123 更新时间:2023-11-30 15:17:26 24 4
gpt4 key购买 nike

我在使用依赖项注入(inject)方面相当陌生,我想我一定是忽略了一些非常简单的事情。

我有一个 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();
}
}

最后,这是给我带来麻烦的 Controller 的一部分:

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 找不到我的 Controller 的单参数构造函数。

根据下面的评论,我想我真正需要的是一种在每个请求的基础上实例化 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 也被注册为“scoped”。

这样做的好处是,一旦范围(网络请求)结束,Unity 现在将处理您的 AnimalEntities。这使您不必在 AnimalEntities 的使用者上实现 IDisposable,如 here 所述。和 here .

关于c# - 在 WebApi 项目上使用 Unity 依赖注入(inject)时 DbContext 被释放,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46573441/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com