2016-08-18 71 views
0

我有以下Processor类中定义:C#无法访问在另一个类的依赖注入的类的属性

public class MongoWrapper : IMongoRWrapper 
{ 
     private string _fileName; 

     public void Initialise(string path) 
     { 
      _fileName = path; 
     }; 

     public void Log() 
     { 
      IsInitialised(); 
     } 

     private void IsInitialised() 
     { 
      if (string.IsNullOrEmpty(_fileName)) 
       throw new InvalidOperationException(
        Resource.MongoRepositoryHelper_must_be_initialised); 
     } 
} 

public class Repository : IRepository 
{ 
    private IMongoWrapper _mongoWrapper; 
    public Repository(IMongoWrapper mongoWrapper) 
    { 
     _mongoWrapper = mongoWrapper; 
    }  

    public void Save() 
    { 
     _mongoWrapper.Log(); 
    } 
} 

public class Processor : IProcessor 
{ 
    private IRepository _repository; 
    private IMongoWrapper _mongoWrapper; 

    public Processor(IRepository repository, IMongoWrapper mongoWrapper) 
    { 
     _repository = repository; 
     _mongoWrapper = mongoWrapper; 
    } 

    public void Process() 
    { 
     _mongoWrapper.Initialise("path"); 

     _repository.Save(); 
    } 
} 

Processor类都在运行时有以下两类注入我正在使用Unity进行依赖注入。

财产,_fileNameMongoWrapper初始化在Processor.Process()方法不可用,当我访问属性在Repository类。

任何人都可以告诉我我在做什么错在这里?

它工作时,我把_fileNameMongoWrapper作为静态字段。这是正确的方式吗?

+0

为什么你需要'Process'来传递'MongoWrapper'的路径?该设计闻起来。 – Steven

+0

流程方法实际上是由Windows服务调用的,路径是服务从文件夹位置获取的文件名。所以它总是变化。 –

+0

因此,每次调用'Proces'方法时会有所不同,还是这个配置值在Windows服务运行时不会改变? – Steven

回答

2

的属性,在初始化 Processor.Process()方法的MongoWrapper_fileName不可用,当我在Repository类访问 属性。

当你说不可,我认为你的意思是字符串_fileName尚未分配的值。如果你的意思是别的,你可能会忽略这个答案。

它没有被分配的原因可能是因为你注入了两个不同的引用,并且使用了默认的TransientLifetimeManager

Understanding Lifetime Managers

当注册在结构的类型,或通过使用 RegisterType方法,默认行为是容器使用 瞬态寿命管理器。每次调用Resolve 或ResolveAll方法或依赖关系机制将 实例注入其他类时,它会创建一个新的 注册,映射或请求类型实例。

这意味着,当你解决Processor它会得到的MongoWrapper一个实例,当你解决Repository你会得到另一个。它在每次注射时都会出现。

把它看成是这样的:

var processor = new Processor(new Repository(new MongoWrapper()), new MongoWrapper()); 

正如你看到的,它创建了两个不同的MongoWrapper。有几种方法可以解决这个问题。

1.使用另一个LifetimeManagerPerResolveLifetimeManager可能是你想要的。

对于这个寿命管理器的行为是 像TransientLifetimeManager,而且还提供了一个信号给 默认的构建方案,标记的类型,使得实例重用 横跨积聚对象图。在递归的情况下, 单身行为适用于对象已注册到PerResolveLifetimeManager的 。

注册它像这样:它

container.RegisterType<IMongoWrapper, MongoWraper>(new PerResolveLifetimeManager()); 

这样想:

var mongoWrapper = new MongoWrapper(); 
var processor = new Processor(new Repository(mongoWrapper), mongoWrapper); 

2.使用ContainerControlledLifetimeManager,会让你的IMongoWrapper单身,因此使用始终是相同的参考。根据你使用IMongoWrapper的方式,这可能会或可能不会成为你想要的。在这种情况下,它可能会像设置_fileNamestatic(如您所述),但整个IMongoWrapperstatic

ContainerControlledLifetimeManager将现有对象 注册为单例实例。对于此生存期管理器,每次调用 Resolve或ResolveAll方法或当依赖关系机制将 实例注入到其他类中时,Unity管理器会返回 相同的注册类型或对象实例。

container.RegisterType<IMongoWrapper, MongoWraper>(new ContainerControlledLifetimeManager()); 

3.分配IMongoWrapper手动。但是,这会破坏使用IoC的全部目的。

public class Processor : IProcessor 
{ 
    private IRepository _repository; 
    private IMongoWrapper _mongoWrapper; 

    public Processor(IRepository repository, IMongoWrapper mongoWrapper) 
    { 
     _repository = repository; 
     _mongoWrapper = mongoWrapper; 
     _repository.SetWrapper(mongoWrapper); 
    } 

    public void Process() 
    { 
     _mongoWrapper.Initialise("path"); 

     _repository.Save(); 
    } 
} 

而且你的资料库:

public class Repository : IRepository 
{ 
    private IMongoWrapper _mongoWrapper; 
    public Repository() 
    { 
    } 

    public void SetWrapper(IMongoWrapper wrapper) 
    { 
     _mongoWrapper = wrapper; 
    } 

    public void Save() 
    { 
     _mongoWrapper.Log(); 
    } 
} 

与所有的说,我必须说,我Steven关于你的设计一致。你真的需要在两个类中注入相同的参考IMongoWrapper吗?并且Processor真的需要在IMongoWrapper中设置一个值才能使其可用于Repository?它造成了一种奇怪的依赖,可能会在以后再次出现。解决设计问题的答案可能会更好,但我选择专注于实际任务。

UPDATE:

是最好的解决方案,你认为哪一个?要将生存管理器设置为 单例,或者在存储库类中设置文件名的值?

嗯,对我来说听起来像_fileName是一个上下文变量,它在整个一个请求/线程/循环中使用。因此你可以相应地处理它。或者将MongoWrapper设置为单身,如果这就是您实际需要的。并让它负责保留_filePath

但是,如果您只是想保留_fileName作为特定范围(例如线程或请求)的上下文变量,我写了an answer for a similiar question,您可以从中使用该代码。该idéa是,而不是依靠另一个具体的参考IMongoWrapper,你可以共享一个容器_fileName。对于IOperationContext,请检查the answer I wrote中的代码,该代码依次基于this question

首先保存FilePath的类。它可能只是一个字符串。

public class ContextInfo : IContextInfo 
{ 
    public string FilePath {get; set;} 
} 

public interface IContextInfo 
{ 
    string FilePath {get; set;} 
} 

然后使用IOperationContext<IContextInfo>的包装。

public class RequestContext : IRequestContext 
{ 
    private readonly IOperationContext<IContextInfo> _operationContext; 

    public RequestContext(IOperationContext<IContextInfo> operationContext) 
    { 
     _operationContext = operationContext; 
    } 

    public IContextInfo ContextInfo 
    { 
     get 
     { 
      if (_operationContext.Items.ContainsKey("ContextInfoString")) 
      { 
       return _operationContext.Items["ContextInfoString"]; 
      } 
      return null; 
     } 
     set 
     { 
      _operationContext.Items["ContextInfoString"] = value; 
     } 
    } 
} 

IRequestContext注入您的处理器。

public class Processor : IProcessor 
{ 
    private IRepository _repository; 
    private IMongoWrapper _mongoWrapper; 
    private IRequestContext _requestContext 

    public Processor(IRepository repository, IMongoWrapper mongoWrapper, IRequestContext requestContext) 
    { 
     _requestContext = requestContext 
     _repository = repository; 
     _mongoWrapper = mongoWrapper; 
    } 

    public void Process() 
    { 
     // Set the context variable. 
     _requestContext.ContextInfo = new ContextInfo { FilePath = "path" }); 
     // Now it will be set for a specific lifetime. 
     _repository.Save(); 
    } 
} 

现在变量设置,您可以随时随地使用它...

public class MongoWrapper : IMongoRWrapper 
{ 
    private IRequestContext _requestContext; 
    public MongoWrapper(IRequestContext requestContext) 
    { 
     _requestContext = requestContext; 
    } 

    private void IsInitialised() 
    { 
     if (string.IsNullOrEmpty(_requestContext.ContextInfo.FilePath)) 
      throw new InvalidOperationException(
       Resource.MongoRepositoryHelper_must_be_initialised); 
    } 
} 

但同样,这一切都取决于你会如何使用fileName,以及它的生命周期应该是。考虑变量的所有权。它应该归IMongoWrapper所有?或者它是在整个应用程序中使用的东西。这些答案的问题应引导你朝着正确的方向发展。

+0

谢谢你对统一终身管理者的精彩解释。我从来没有遇到过这么好的解释。再次感谢。是的,正如史蒂文提到的,我可以看到一些代码味道。你认为哪种解决方案最好?要将lifetimemanager设置为单例或在存储库类中设置文件名的值?我对这些国际石油公司并不是很有经验 –

+0

我已经更新了我的答案,主要关注'fileName'的所有权。它包含了解决问题的另一种解决方案,但您可能需要重新考虑课程的责任。 – smoksnes