Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/wcf/4.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# Windows服务中承载的WCF中的IoC和每次调用的容器生存期_C#_Wcf_Unity Container_Soa_Simple Injector - Fatal编程技术网

C# Windows服务中承载的WCF中的IoC和每次调用的容器生存期

C# Windows服务中承载的WCF中的IoC和每次调用的容器生存期,c#,wcf,unity-container,soa,simple-injector,C#,Wcf,Unity Container,Soa,Simple Injector,我有一些客户端(web和桌面应用程序)必须连接到一些服务,这些服务是托管在Windows服务中的WCF服务。这些服务必须从一个或多个数据库获取数据,但由客户机确定从哪个数据库可以定义同一类型的一个或多个数据库 我在服务中设置了数据上下文、存储库、业务管理器和服务实现。Datacontext由SimpleInjector容器注入(我也尝试了Unity),当然,容器中的注册在创建ServiceHosts之前发生,ServiceHosts是在单一上下文模式下创建的(也尝试了每次调用和每次会话) 我已经

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

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

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

因此,我试图将其注册为按调用(异步,wcf生活方式),但由于这是一个Windows服务,它将不会关闭,并且容器的作用域也不正确

在这个场景中,我可以做些什么来让它工作

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

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());
container.RegisterInstance(新的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;
ISecurityContext securityContext=m_Container.GetInstance();
var sqlConn=新的SqlConnectionStringBuilder
{
数据源=@“开发测试服务器\SQL2017”,
InitialCatalog=“COMMON”,
IntegratedSecurity=true,
连接超时=30
};
securityContext.Database.Connection.ConnectionString=sqlConn.ConnectionString;
在上下文模式下创建的ServiceHosts

这是个坏主意。集成页面:

提示:对所有WCF服务使用InstanceContextMode.PerCall。这可以防止WCF服务在单个请求之后产生任何难以检测的问题。

在将上下文数据(例如受请求影响的连接字符串)提供给应用程序的某些部分时,有两种选择。您可以使用环境状态,也可以使用objec图形来传递数据

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

  • 全局静态。应用程序中的所有线程和请求都可以访问这些字段。这不太适合您的场景
  • 线程处于静态。无论从哪个线程访问字段,都会返回相同的值,但其他线程会获得自己的值。由于WCF请求可以在多个线程上异步执行,因此此选项也不适用
  • 异步作用域状态。这允许使用相同的数据限定单个逻辑异步操作流的作用域。在该范围内,数据随处可见。此选项最适合您的需要
注意:对于这个答案,我假设
ISecurityContext
的定义如下:

public interface ISecurityContext
{
    public Database Database { get; }
}
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;
}
container.Register<SecurityContext>(Lifestyle.Scoped);
container.Register<ISecurityContext, SecurityContext>(Lifestyle.Scoped);
使用第三个选项,您可以实现
SecurityContext
,如下所示:

public interface ISecurityContext
{
    public Database Database { get; }
}
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;
}
container.Register<SecurityContext>(Lifestyle.Scoped);
container.Register<ISecurityContext, SecurityContext>(Lifestyle.Scoped);
登记如下:

public interface ISecurityContext
{
    public Database Database { get; }
}
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;
}
container.Register<SecurityContext>(Lifestyle.Scoped);
container.Register<ISecurityContext, SecurityContext>(Lifestyle.Scoped);
应用程序的其余部分可以简单地依赖于
ISecurityContext
并检索其
数据库