Unity注册相互覆盖

时间:2016-03-29 12:48:12

标签: c# unity-container

我在C#中使用Unity。我有一个我称之为IConnectionStringLoader的接口,它有两个派生接口。

public interface IConnectionStringLoader
{
    string Get();
    void Write();
}

public interface IDbConnectionStringLoader : IConnectionStringLoader
{
}

public interface IMetaDataConnectionStringLoader : IConnectionStringLoader
{
}

它只有一个实现:

public class ConnectionStringLoader : IDbConnectionStringLoader, IMetaDataConnectionStringLoader
{
    private readonly string _connectionStringName;

    public ConnectionStringLoader(string connectionStringName)
    {
        _connectionStringName = connectionStringName;
    }

    public string Get()
    {
        var cs = ConfigurationManager.ConnectionStrings[_connectionStringName];
        if (cs != null)
        {
            return cs.ConnectionString;
        }
        return null;
    }

    public void Write()
    {
        Console.WriteLine(_connectionStringName);
    }
}

我的注册如下:

container.RegisterType<IMetaDataConnectionStringLoader, ConnectionStringLoader>(new InjectionConstructor("MetaConnection"));
container.RegisterType<IDbConnectionStringLoader, ConnectionStringLoader>(new InjectionConstructor("DbConnection"));

接口的意思是我可以在我的类中注入不同的接口,并为每个实现获取正确的连接字符串。 但问题是,最后完成的任何注册都将覆盖之前的注册。

var foo = _container.Resolve<IDbConnectionStringLoader>();
var bar = _container.Resolve<IMetaDataConnectionStringLoader>();
foo.Write();
bar.Write();

输出是:

DbConnection 
DbConnection

如果我反转注册顺序,输出将为MetaConnection两次。所以我到目前为止的结论是,最后一次注册会覆盖前一次注册。但是,如果我将实现更改为派生类,则它可以工作:

public class SomeOtherConnectionStringLoader : ConnectionStringLoader
{
    public ConnectionStringLoaderImpl(string connectionStringName) : base(connectionStringName)
    {
    }
}

并更改注册:

container.RegisterType<IMetaDataConnectionStringLoader, ConnectionStringLoader>(new InjectionConstructor("MetaConnection"));
container.RegisterType<IDbConnectionStringLoader, SomeOtherConnectionStringLoader >(new InjectionConstructor("DbConnection"));

现在一切正常,但我不明白为什么。我尝试过不同的终身管理者,但结果相同。我认为Unity会尝试使用基于接口的“正确”注入参数来创建ConnectionStringLoader的实例,但是这里似乎还有一些其他逻辑。

为什么注册会互相覆盖的任何建议?

3 个答案:

答案 0 :(得分:1)

老实说,你使用接口的方式对我来说很奇怪,因为有两个接口只能由同一个类实现。我会发现更自然地使用注册名称来遵循下一个方法:

// If it is a loader the Write method makes no sense (IConnectionStringRepository?)
public interface IConnectionStringLoader
{
    string Get();
    void Write();
}

public class ConnectionStringLoader : IConnectionStringLoader
{
    private readonly string _connectionStringName;

    public ConnectionStringLoader(string connectionStringName)
    {
        _connectionStringName = connectionStringName;
    }

    public string Get()
    {
        var cs = ConfigurationManager.ConnectionStrings[_connectionStringName];
        if (cs != null)
        {
            return cs.ConnectionString;
        }
        return null;
    }

    public void Write()
    {
        Console.WriteLine(_connectionStringName);
    }
}

注册记忆:

container.RegisterType<IConnectionStringLoader, ConnectionStringLoader>("Database", new InjectionConstructor("MetaConnection"));
container.RegisterType<IConnectionStringLoader, ConnectionStringLoader>("Metadata", new InjectionConstructor("DbConnection"));

分辨率:

var foo = _container.Resolve<IConnectionStringLoader>("Database");
var bar = _container.Resolve<IConnectionStringLoader>("Metadata");
foo.Write();
bar.Write();

答案 1 :(得分:0)

我不熟悉Unity。但似乎他们正在映射到同一个实例。所以你应该改变ConnectionStringLoader(Per dependency)的生命周期。

如果您不分享实例,为什么要将所有内容放在一个类中? ConnectionStringLoader方法= IDbConnectionStringLoader方法+ IMetaDataConnectionStringLoader方法。

当您解决IDbConnectionStringLoader时,它将不会使用已在实例中的IMetaDataConnectionStringLoader方法(反之亦然)。

此时装箱两个不同的派生类更好:

抽象类:

public abstract class ConnectionStringLoader : IConnectionStringLoader
{
    private readonly string _connectionStringName;

    public ConnectionStringLoader(string connectionStringName)
    {
        _connectionStringName = connectionStringName;
    }

    public string Get()
    {
        var cs = ConfigurationManager.ConnectionStrings[_connectionStringName];
        if (cs != null)
        {
            return cs.ConnectionString;
        }
        return null;
    }

    public void Write()
    {
        Console.WriteLine(_connectionStringName);
    }
}

派生类:

public sealed class DbConnectionStringLoader : ConnectionStringLoader, IDbConnectionStringLoader
{
    public DbConnectionStringLoader(string connectionStringName):base(connectionStringName)
    {

    }
    //Implement methods here just belongs to IDbConnectionStringLoader
}

public sealed class MetaDataConnectionStringLoader : ConnectionStringLoader, IMetaDataConnectionStringLoader
{
    public MetaDataConnectionStringLoader(string connectionStringName):base(connectionStringName)
    {

    }
    //Implement methods here just belongs to IMetaDataConnectionStringLoader
}

答案 2 :(得分:0)

令人惊讶的是它确实两次调用ConnectionStringLoader ctor,但使用相同的注射成员。如果您查看container.Registrations,确实有两个注册,因此不会覆盖其他注册。我确实看过RegisterType的实现,但没有理解它。

另一种方法是为您的注册命名,不确定它是否适合您的整体统一引导策略。

container.RegisterType<IMetaDataConnectionStringLoader, ConnectionStringLoader>("bar", new InjectionConstructor("MetaConnection"));
container.RegisterType<IDbConnectionStringLoader, ConnectionStringLoader>("foo", new InjectionConstructor("DbConnection"));

var foo = container.Resolve<IDbConnectionStringLoader>("foo");
var bar = container.Resolve<IMetaDataConnectionStringLoader>("bar");