C#无法访问另一个类中依赖注入类的属性

时间:2016-08-18 13:27:34

标签: c#-4.0 dependency-injection unity-container

我定义了以下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作为静态字段时,它起了作用。这是正确的方法吗?

1 个答案:

答案 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。使用其他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的方式,这可能或可能不是您想要的。在这种情况下,它实际上就像将_fileName设置为static(正如您所提到的),而是整个IMongoWrapperstatic

  

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所有吗?或者它是整个应用程序中使用的东西。这些答案的问题应该引导您朝着正确的方向前进。