gpt4 book ai didi

c# - 托管在 Windows 服务中的 WCF 中的 IoC 和每次调用的容器生命周期

转载 作者:行者123 更新时间:2023-11-30 12:38:47 29 4
gpt4 key购买 nike

我有一些客户端(Web 和桌面应用程序)必须连接到一些服务,这些服务是 Windows 服务中托管的 WCF 服务。这些服务必须从一个或多个数据库中获取数据,但由客户端决定从哪个数据库中获取数据,可以有一个或多个来自同一类型定义的数据库。

我在服务中设置了数据上下文、存储库、业务管理器和服务实现。 Datacontext 由 SimpleInjector 容器注入(inject)(我也尝试过使用 Unity),当然,容器中的注册发生在创建 ServiceHosts 之前,它是在上下文模式 Single 中创建的(也尝试过每次调用和每次 session )。

我已经编写了 IDispatchMessageInspector 的实现,它将拦截所有 SOAP 消息并读取消息 header 并根据消息 header 中的值设置数据上下文的数据库连接字符串。但这会导致问题,因为它不是“线程安全的”,或者至少当一个调用尚未完成时,下一个调用可能会将另一个连接字符串设置为相同的数据上下文,从而将一切搞砸。

因此,我试图根据调用(异步、wcf 生活方式)注册它,但因为这是一个 Windows 服务,它不会关闭并且容器的范围不正确。

在这种情况下我该怎么做才能让它发挥作用?

Case 1: no problem, cases 2 and 3: it must not interfere with each other

容器创建和服务主机启动:

var container = new Container();
//container.Options.DefaultLifestyle = new AsyncScopedLifestyle();
DIContainer.SetDI(container);

serviceHosts.Add(new ServiceHost(typeof(LoginService)));
serviceHosts.Add(new ServiceHost(typeof(IdentityService)));

foreach (var serviceHost in serviceHosts)
{
serviceHost.Open();
}

DbContext 的注册:

container.RegisterInstance<CSI.AuthServices.DataAccess.EF.Interfaces.ISecurityContext>(new CSI.AuthServices.DataAccess.EF.SecurityContext());

用于读取 SOAP 消息头和设置数据库连接字符串的拦截器:

ISecurityContext securityContext = m_Container.GetInstance<ISecurityContext>();
var sqlConn = new SqlConnectionStringBuilder
{
DataSource = @"DEV_TEST_SERVER\SQL2017",
InitialCatalog = "COMMON",
IntegratedSecurity = true,
ConnectTimeout = 30
};
securityContext.Database.Connection.ConnectionString = sqlConn.ConnectionString;

最佳答案

the ServiceHosts, which are created in context mode Single

这是个坏主意。整合页面states :

TIP: Use InstanceContextMode.PerCall for all WCF services. This prevents any hard to detect problems caused by WCF services outliving a single request.

在使上下文数据(例如受请求影响的连接字符串)可供应用程序的某些部分使用时,有两种选择。您可以使用环境状态,也可以使用流动数据和对象图。

环境状态意味着您将数据存储在某种可用于特定上下文的可变状态中。这里有几个选项:

  • 全局静态。应用程序中的所有线程和请求都可以访问这些字段。这不太适合您的场景。
  • 线程静态。从单个线程访问该字段的任何地方,都会返回相同的值,但其他线程会获得自己的值。由于 WCF 请求可以在多个线程上异步执行,因此此选项也不适合。
  • 异步范围状态。这允许单个逻辑异步操作流以相同数据为范围。数据在该范围内随处可用。此选项最适合您的需求。

注意:对于这个答案,我假设 ISecurityContext 定义如下:

public interface ISecurityContext
{
public Database Database { get; }
}

使用第三个选项,您可以按如下方式实现您的SecurityContext:

public sealed class SecurityContext : ISecurityContext
{
private static readonly AsyncLocal<Database> db =
new AsyncLocal<Database>();

Database ISecurityContext.Database => db.Value;

internal void SetDatabase(Database database) => db.Value = database;
}

因为使用了System.Threading.AsyncLocal ,您可以在整个应用程序中重用同一个 SecurityContext 实例,注册为 Singleton

在您的 IDispatchMessageInspector 中,您可以通过调用 SecurityContext.SetDatabase(db) 来设置数据库。

另一种选择是使用对象图来流动数据。在这种情况下,您将可变状态作为私有(private)字段存储在上下文类中(例如您的 SecurityContext 并将它们注册为 Scoped。这样您就可以在请求时设置它们的值开始,这些值可以在请求中的任何地方重复使用,其中 ISecuriryContext 被注入(inject),而另一个请求获得不同的 SecurityContext 实例。

您可以将 SecurityContext 更改为以下内容:

public sealed class SecurityContext : ISecurityContext
{
// Just get/set with a private backing field. No ambient state
public Database Database { get; set; }
}

注册如下:

container.Register<SecurityContext>(Lifestyle.Scoped);
container.Register<ISecurityContext, SecurityContext>(Lifestyle.Scoped);

在您的 IDispatchMessageInspector 中,您可以解析 SecurityContext 并设置 Database:

container.GetInstance<SecurityContext>().Database = db;

您的应用程序的其余部分可以简单地依赖于 ISecurityContext 并检索其 Database 值。

关于c# - 托管在 Windows 服务中的 WCF 中的 IoC 和每次调用的容器生命周期,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49919939/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com