Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/294.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# 使用同一接口调用多个类_C#_Unit Testing_Dependency Injection_Structuremap - Fatal编程技术网

C# 使用同一接口调用多个类

C# 使用同一接口调用多个类,c#,unit-testing,dependency-injection,structuremap,C#,Unit Testing,Dependency Injection,Structuremap,我有一个像 public interface IAddressProvider { string GetAddress(double lat, double long); } 在我的消费类中,我希望循环遍历具体的提供者,直到得到一个结果,如(简化): 然后,我可以模拟每个提供程序进行单元测试,将null设置为返回值以适当地测试所有代码路径 如何将接口注入到我的消费类中(最好使用StructureMap),以便正确解析每个具体实现?我不太熟悉StructureMap,但据我所知,有两种解

我有一个像

public interface IAddressProvider
{
    string GetAddress(double lat, double long);
}
在我的消费类中,我希望循环遍历具体的提供者,直到得到一个结果,如(简化):

然后,我可以模拟每个提供程序进行单元测试,将null设置为返回值以适当地测试所有代码路径


如何将接口注入到我的消费类中(最好使用StructureMap),以便正确解析每个具体实现?

我不太熟悉StructureMap,但据我所知,有两种解决方案

1) 命名实例-将StructureMap注册为命名实例,然后配置构造函数参数

StructureMap命名实例配置:

在构造函数注入中使用命名参数:

2) 更多接口-假设只有少数
IAddressProvider
实现,而不是数百个,您可以创建一个
ICachedAddressProvider
ilocaldaddressprovider
,和
iexternaladressprovider
,它们实现
IAddressProvider
,然后在消费类的构造函数中使用它们


如果可能有更具体的实现,那么您可能希望按照抽象工厂的思路进行研究。

您是否可以不使用容器。GetAllInstances

var address = new List<string>();
foreach (var provider in container.GetAllInstances<IAddressProvider>())
{
    address.add(provider.GetAddress(lat, long));
}

您有多个地址提供程序这一事实并不是调用代码必须处理的。因此,创建一个特定的提供者代理来处理这些多个提供者

像这样

public interface IAddressProvider {
    string GetAddress(double lat, double long);
}

public class AddressProviderProxy: IAddressProvider {
    public AddressProviderProxy(IAddressProvider[] providers) {
        _providers = providers; // TODO: Add a NULL guard
    }

    private readonly IAddressProvider[] _providers;

    string IAddressProvider.GetAddress(double lat, double long) {
        foreach (var provider in _providers) {
            string address = provider.GetAddress(lat, long);
            if (address != null)
                return address;
        }
        return null;
    }
}

// Wire up using DI
container.Register<IAddressProvider>(
    () => new AddressProviderProxy(
        new IAddressProvider[3] {
            cachedAddressProvider,
            localDbAddressProvider,
            externalAddressProvider
        }
    )
);

// Use it
IAddressProvider provider = ...from the container, injected..
string address = provider.GetAddress(lat, long) ?? "no address found";
公共接口提供程序{
字符串GetAddress(双lat,双long);
}
公共类AddressProviderProxy:IAddressProvider{
公共地址ProviderProxy(IAddressProvider[]提供程序){
_providers=providers;//TODO:添加空保护
}
私有只读提供程序[]\u提供程序;
字符串IAddressProvider.GetAddress(双lat,双long){
foreach(变量提供程序在_提供程序中){
字符串地址=provider.GetAddress(lat,long);
if(地址!=null)
回信地址;
}
返回null;
}
}
//使用DI连接
集装箱。登记(
()=>新地址提供者代理(
新供应商[3]{
缓存提供程序,
localDbAddressProvider,
外部地址提供者
}
)
);
//使用它
IAddressProvider=…来自容器,已注入。。
字符串地址=provider.GetAddress(lat,long)??“找不到地址”;

我想返回第一个非空地址,这样我就不会花费访问每个提供程序的费用(因此,请先缓存,如果没有缓存,则本地db,如果没有本地db条目,则外部提供程序)。我已更新了我的答案,希望它会更有帮助。这甚至可以在不需要IContainer依赖项的情况下完成。SM支持注入集合,因此您只需在构造函数中添加依赖项:
public AddressProviderBuilder(IAddressProvider[]providers)
,就可以获得所有已注册的IAddressProvider实现。我喜欢这种方法,但我现在如何模拟我的具体提供程序来测试AddressProviderProxy首先调用缓存,如果缓存为空,则为本地数据库添加一个带有多个模拟的测试。您可以分别测试每个提供程序(包括代理)的逻辑。代理的逻辑是,它生成生成地址的第一个提供程序的地址,因此在第一个提供程序生成地址时添加单元测试,在第二个提供程序生成地址时添加单元测试,在多个提供程序生成地址时添加单元测试,等等。您不应该测试实际提供者与代理的组合。
public class AddressProviderBuilder : IInstanceBuilder
{
    private readonly IContainer container;

    public AddressProviderBuilder(IContainer container)
    {
        this.container = container;
    }

    public IAddressProvider Build()
    {
        foreach (var provider in this.container.GetAllInstances<IAddressProvider>())
        {
            if (provider.GetAddress(lat, long) != null)
            {
                return provider;
            }
        }

        return null;
    }
}
public interface IAddressProvider {
    string GetAddress(double lat, double long);
}

public class AddressProviderProxy: IAddressProvider {
    public AddressProviderProxy(IAddressProvider[] providers) {
        _providers = providers; // TODO: Add a NULL guard
    }

    private readonly IAddressProvider[] _providers;

    string IAddressProvider.GetAddress(double lat, double long) {
        foreach (var provider in _providers) {
            string address = provider.GetAddress(lat, long);
            if (address != null)
                return address;
        }
        return null;
    }
}

// Wire up using DI
container.Register<IAddressProvider>(
    () => new AddressProviderProxy(
        new IAddressProvider[3] {
            cachedAddressProvider,
            localDbAddressProvider,
            externalAddressProvider
        }
    )
);

// Use it
IAddressProvider provider = ...from the container, injected..
string address = provider.GetAddress(lat, long) ?? "no address found";