Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/330.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 装潢师和IDisposable_C#_Decorator_Ioc Container_Simple Injector - Fatal编程技术网

C# 装潢师和IDisposable

C# 装潢师和IDisposable,c#,decorator,ioc-container,simple-injector,C#,Decorator,Ioc Container,Simple Injector,我有一个子类DbContext public class MyContext : DbContext { } 我有一个IUnitOfWork关于MyContext的抽象,它实现了IDisposable,以确保像MyContext这样的引用在适当的时候被处理掉 public interface IUnitOfWork : IDisposable { } public class UnitOfWork : IUnitOfWork { private readonly MyContext

我有一个子类
DbContext

public class MyContext : DbContext { }
我有一个
IUnitOfWork
关于
MyContext
的抽象,它实现了
IDisposable
,以确保像
MyContext
这样的引用在适当的时候被处理掉

public interface IUnitOfWork : IDisposable { }

public class UnitOfWork : IUnitOfWork 
{
    private readonly MyContext _context;

    public UnitOfWork()
    {
        _context = new MyContext();
    }

    ~UnitOfWork()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    private bool _disposed;

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed) return;

        if (disposing)
        {
            if (_context != null) _context.Dispose();
        }

        _disposed = true;
    }
}
My
UnitOfWork
以每个(web)请求的生存期范围注册。我有
IUnitOfWork
的装饰器,它们可以注册为瞬态或生命周期范围,我的问题是它们应该如何实现
IDisposable
——具体是应该还是不应该传递对
Dispose()
的调用

我看到两个选项(我猜选项2是正确的答案):

  • 每个装饰器都应该知道它是暂时的还是生命周期范围的。如果装饰器是暂时的,那么它不应该对装饰实例调用
    Dispose()
    。如果它是生命周期范围的,则应该
  • 每个decorator应该只关心自身的处理,并且应该从不将调用传递给修饰实例。容器将在适当的时间为调用链中的每个对象管理对
    Dispose()
    的调用。对象应该只封装实例的
    Dispose()
    ,并且修饰不是封装

  • 就我个人而言,我怀疑你需要逐案处理这件事。一些装饰师可能有充分的理由理解范围界定;对于大多数人来说,简单地传递它可能是一个很好的默认值。很少有人不应该明确地处理这个链——我所看到的主要情况是,它专门用来抵消另一个应该考虑范围界定的装饰者:没有(总是处理)

    作为一个相关的例子-考虑诸如<代码> GZIPStase之类的东西,对于大多数人来说,它们只处理一个逻辑块——因此默认为“处理流”是好的;但是这个决定可以通过一个让你告诉它如何行动的。在带有可选参数的C#的最新版本中,这可以在单个构造函数中完成

    选项2有问题,因为它要求您(或容器)跟踪所有中间对象;如果您的容器可以方便地这样做,那么很好-但也要注意,它们必须按照正确的顺序进行处理(从外到内)。因为在decorator链中,可能会有一些挂起的操作-计划在请求时向下游刷新,或者(作为最后手段)在dispose期间刷新

    对于实现IDisposable,[装饰者]应该做些什么

    这又回到了所有权的一般原则。问问自己:“谁拥有那种一次性的?”。这个问题的答案是:拥有这个类型的人负责处理它

    由于一次性类型是从外部传递给装饰者的,装饰者没有创建该类型,通常不应该负责清理它。decorator无法知道是否应该处理该类型(因为它不控制其生存期),这在您的案例中非常清楚,因为decorator可以注册为transient,而decoratee的生存期要长得多。在您的情况下,如果您从decorator中处理decoree,您的系统将简单地中断

    因此,装饰者永远不应该处置被装饰者,仅仅因为它不拥有被装饰者。处理那个被装饰者是你的责任。在这种情况下,我们谈论的是装饰师并不重要;它仍然可以归结为所有权的一般原则

    每个装潢师应该只关心自己的处理,并且应该 永远不要将调用传递给装饰实例

    对。尽管装饰程序应该处理它拥有的所有东西,但是由于您使用的是依赖项注入,它本身通常不会创建太多东西,因此不拥有这些东西

    另一方面,您的
    UnitOfWork
    创建了一个新的
    MyContext
    类,因此拥有该实例的所有权,它应该处理掉它

    这条规则也有例外,但归根结底还是所有权的问题。有时您确实会将类型的所有权传递给其他人。例如,当使用工厂方法时,按照约定,工厂方法将所创建对象的所有权传递给调用方。有时所有权会传递给创建的对象,例如.NET的
    StreamReader
    类。API文档对此很清楚,但由于设计非常不直观,开发人员一直被这种行为绊倒。NET framework中的大多数类型都不是这样工作的。例如,
    SqlCommand
    类没有处理
    SqlConnection
    ,如果它确实处理了连接,那将非常恼人

    另一种看待这个问题的方式是从政治的角度。通过让
    IUnitOfWork
    实现
    IDisposable
    您违反了,因为“抽象不应该依赖于细节;细节应该依赖于抽象”。通过实现
    IDisposable
    您正在将实现细节泄漏到
    IUnitOfWork
    接口中。实现
    IDisposable
    意味着该类具有需要处理的非托管资源,例如文件句柄和连接字符串。这些都是实现细节,因为这种接口的每个实现实际上都不可能需要处理。您只需要为您的单元测试创建一个假的或模拟的实现,并且您有一个不需要处理的实现的证明

    因此,当您通过从
    IUnitOfWork
    中删除
    IDisposable
    接口并将其移动到实现来修复此DIP冲突时,装饰程序将无法处理装饰对象,因为它无法知道
    public class UnitOfWorkDecorator : IUnitOfWork
    {
        private readonly IUnitOfWork _decorated;
    
        public UnitOfWorkDecorator(IUnitOfWork decorated)
        {
            _decorated = decorated;
        }
    
        public void Dispose()
        {
            //do we pass on the call?
            _decorated.Dispose();
        }
    }    
    
    public sealed class UnitOfWork : IUnitOfWork, IDisposable
    {
        private readonly MyContext _context;
    
        public UnitOfWork()
        {
            _context = new MyContext();
        }
    
        public void Dispose()
        {
            _context.Dispose();
        }
    }
    
    public sealed class UnitOfWork : IUnitOfWork 
    {
        private readonly MyContext _context;
    
        public UnitOfWork(MyContext context)
        {
            _context = context;
        }
    }