我定义了以下Processor
类:
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
类在运行时注入以下两个类:
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();
}
}
我使用Unity进行依赖注入。
当我访问_fileName
类中的属性时,MongoWrapper
方法中初始化的Processor.Process()
属性Repository
不可用。
有谁能告诉我我在这里做错了什么?
当我在_fileName
中将MongoWrapper
作为静态字段时,它起了作用。这是正确的方法吗?
答案 0 :(得分:2)
_fileName
初始化的属性MongoWrapper
当我访问时,Processor.Process()
方法不可用Repository
类中的属性。
如果您说不可用,我认为您的意思是字符串_fileName
尚未分配值。如果你的意思是别的,你可以忽略这个答案。
之所以没有分配,可能是因为你注入了两个不同的引用,并使用TransientLifetimeManager
,这是默认的。
Understanding Lifetime Managers
在配置中注册类型时,或使用 RegisterType方法,默认行为是容器使用 一个短暂的终身经理。它创建了一个新的实例 每次调用Resolve时都会注册,映射或请求类型 或ResolveAll方法或依赖机制注入时 实例进入其他类。
这意味着当您解决Processor
时,它会获得MongoWrapper
的一个实例,当您解决Repository
时,您将获得另一个实例。它在每次注射时都会被新化。
这样想:
var processor = new Processor(new Repository(new MongoWrapper()), new MongoWrapper());
如您所见,它创建了两个不同的MongoWrapper
。有几种方法可以解决这个问题。
1。使用其他LifetimeManager
。 PerResolveLifetimeManager
可能就是你想要的那个。
对于这个终身经理而言,行为是 像一个TransientLifetimeManager,但也提供了一个信号 默认构建计划,标记类型以便重用实例 整个构建对象图。在递归的情况下, 单例行为适用于已注册对象的位置 PerResolveLifetimeManager。
像这样注册:
container.RegisterType<IMongoWrapper, MongoWraper>(new PerResolveLifetimeManager());
这样想:
var mongoWrapper = new MongoWrapper();
var processor = new Processor(new Repository(mongoWrapper), mongoWrapper);
2。使用ContainerControlledLifetimeManager
,会使您的IMongoWrapper
成为单身,因此始终使用相同的参考。根据您使用IMongoWrapper的方式,这可能或可能不是您想要的。在这种情况下,它实际上就像将_fileName
设置为static
(正如您所提到的),而是整个IMongoWrapper
为static
。
ContainerControlledLifetimeManager,用于注册现有对象 作为单例实例。对于这个终生经理Unity返回 每次调用时注册类型或对象的相同实例 Resolve或ResolveAll方法或依赖机制注入时 实例进入其他类。
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
?它创造了一种奇怪的依赖,后来可能会再次困扰你。解决设计的答案可能会更好,但我选择专注于实际任务。
<强>更新强>
您认为哪种解决方案最好?将lifemanager设置为 单例或设置存储库类中文件名的值?
嗯,对我来说听起来是_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
所有吗?或者它是整个应用程序中使用的东西。这些答案的问题应该引导您朝着正确的方向前进。