ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

在WebApi项目上使用Unity依赖注入时,将处置DbContext

2019-10-25 18:07:27  阅读:325  来源: 互联网

标签:asp-net-web-api2 dependency-injection unity-container c


我在使用依赖注入方面还很陌生,我认为我必须忽略一些非常简单的事情.

我有一个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,如herehere所述.

标签:asp-net-web-api2,dependency-injection,unity-container,c
来源: https://codeday.me/bug/20191025/1930224.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有