我在服务中设置了datacontexts,存储库,业务经理和服务实现. Datacontext由SimpleInjector容器注入(我也尝试使用Unity),当然,容器中的注册在创建ServiceHosts之前进行,ServiceHosts是在上下文模式Single(也是每个调用和每个会话尝试)中创建的.
我编写了IdispatchMessageInspector的实现,它将拦截所有SOAP消息并读取消息头并根据消息头中的值设置datacontext的数据库连接字符串.
但是这会导致问题,因为它不是“线程安全的”,或者至少当一个调用尚未完成时,下一个调用可能会设置另一个连接字符串到同一个datacontext,搞乱一切.
所以,我试图按照每个调用(async,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());
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
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)来设置数据库.
另一种选择是使用对象图流动数据.在这种情况下,您将可变状态存储为上下文类中的私有字段(例如您的SecurityContext并将它们注册为Scoped.这样您可以在请求开始时设置它们的值,并且这些值可以在请求中的任何位置重用,其中注入ISecuriryContext,而另一个请求获取不同的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并设置数据库:
container.GetInstance<SecurityContext>().Database = db;
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。