25

我目前正在使用以下代码:

public class MyProvider
{
    public MyProvider()
    {
    }

    public void Fetch()
    {
        using (PopClient popClient = new PopClient())
        {
            ....
        }
    }
}

因为我希望能够对 Fetch 方法进行单元测试,并且由于我无法模拟 PopClient,所以我创建了一个接口和一个调用 PopClient 的包装类。我更新的代码如下所示:

public class MyProvider
{
    private readonly IPopClient popClient;

    public MyProvider(IPopClient popClient)
    {
        this.popClient = popClient;
    }

    public void Fetch()
    {
        using (var pop3 = popClient)
        {
            ....
        }
    }
}

我正在使用 Ninject 进行依赖注入,我不太确定 using 语句在更新的代码中会产生什么样的影响,因为 Ninject 已经创建了 PopClient 的一个实例并将其注入到构造函数中。

using 语句会处理 pop3 对象并单独保留 popClient 对象以便 Ninject 可以处理它,还是 using 语句会干扰 Ninject?

在这种情况下,正确的方法是什么?任何见解都会非常有帮助。

4

3 回答 3

18

pop3变量将被赋予对具有的IPopClient对象的相同引用popClient,因此当using语句结束时,本地变量和实例变量所引用的对象都将是 Dispose()d,可能会将其置于不一致的状态以供进一步使用。

如果你想使用多个实例IPopClient,每次Fetch()调用一个,你应该做的是注入一个“工厂方法”:

public class MyProvider
{
    private readonly Func<IPopClient> createPopClient;

    public MyProvider(Func<IPopClient> popClientFactory)
    {
        this.createPopClient = popClientFactory;
    }

    public void Fetch()
    {
        using (var pop3 = createPopClient())
        {
            ....
        }
    }
}

现在,当您调用 时Fetch(),它将执行工厂方法,该方法将返回对 an 的新引用IPopClient,可以使用然后将其处理掉,而不会影响对该方法的任何其他调用。

AutoFac 支持为已注册的类型注入工厂方法,无需任何额外设置(我认为这就是它的名字);我相信在配置 Ninject 容器时,您需要显式注册“getter”作为给定返回类型的工厂方法(可以像 lambda 一样简单,()=>new PopClient()也可以使用对容器解析方法的调用)。

于 2012-09-10T22:31:26.790 回答
1

设置绑定时,声明范围:

https://github.com/ninject/ninject/wiki/Object-Scopes

Ninject 将对它为您创建的对象调用 dispose,因此请确保在您提供给 Ninject 处理的任何对象中编写您的 dispose 方法。

于 2012-09-10T22:26:04.407 回答
0

我喜欢@KeithS 的答案,但我想添加一些更新ConfigureServices,包括 .NET Core 依赖注入和MailKit.Net.Smtp.

https://docs.microsoft.com/en-us/dotnet/core/extensions/dependency-injection

鉴于以下课程:

public class MailService
{
    private readonly SmtpSettings _settings;
    private readonly Func<ISmtpClient> _smtpClientFactory;

    public MailService(SmtpSettings settings, Func<ISmtpClient> smtpClientFactory)
    {
        _settings = settings;
        _smtpClientFactory = smtpClientFactory;
    }

    public async Task SendEmailAsync(string to, string subject, MimeEntity body, CancellationToken cancellationToken = default)
    {
        var message = new MimeMessage();
        message.From.Add(MailboxAddress.Parse(_settings.UserName));
        message.To.Add(MailboxAddress.Parse(to));
        message.Subject = subject;
        message.Body = body;

        using (var smtpClient = _smtpClientFactory())
        {
            await smtpClient.ConnectAsync(_settings.Server, _settings.Port, SecureSocketOptions.StartTls, cancellationToken);
            await smtpClient.AuthenticateAsync(_settings.UserName, _settings.Password, cancellationToken);
            await smtpClient.SendAsync(message, cancellationToken);
            await smtpClient.DisconnectAsync(true, cancellationToken);
        }
    }
}

它可以像这样添加到ConfigureServices(IServiceCollection services)

services.AddTransient<ISmtpClient, SmtpClient>();
services.AddSingleton(provider =>
                 new Func<ISmtpClient>(() => provider.GetService<ISmtpClient>()));

Azure 函数的完整Program.cs示例:

public static void Main()
{
    var host = new HostBuilder()
        .ConfigureFunctionsWorkerDefaults()
        .ConfigureAppConfiguration((hostContext, builder) =>
        {
            if (hostContext.HostingEnvironment.IsDevelopment())
            {
                builder.AddJsonFile("local.settings.json");
                //This will override any values added to local.settings.json - We will however follow the recommended approach for keeping secrets in dev 
                //https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-5.0&tabs=windows#register-the-user-secrets-configuration-source
                //builder.AddJsonFile("secret.settings.json");
                builder.AddUserSecrets<Program>();
            }

        })
        .ConfigureServices((hostContext, services) =>
        {
            var connectionString = hostContext.Configuration.GetConnectionString("DefaultConnection");
            services.AddDbContext<ApplicationDbContext>(options =>
                    options.UseSqlServer(
                    connectionString,
                    sqlServerOptions => sqlServerOptions.CommandTimeout(600)));

            services.AddHttpClient();

            var configuration = hostContext.Configuration;

            var smtpSettings = new SmtpSettings();
            configuration.Bind("Smtp", smtpSettings);
            services.AddSingleton(smtpSettings);

            services.AddTransient<ISmtpClient, SmtpClient>();

            services.AddSingleton(provider =>
                 new Func<ISmtpClient>(() => provider.GetService<ISmtpClient>()));

            services.AddTransient<MailService>();
        })
        .Build();

    host.Run();
}

Func<T>注册和解决的来源:

https://stackoverflow.com/a/43763284/3850405

于 2021-07-30T15:43:19.670 回答