IoC in WCF hosted in Windows Service and container lifetime per call

I have several clients (web and desktop applications) that need to connect to several services, which are WCF services hosted on a Windows service. These services must receive data from one or more databases, but it is the client who determines which database one or more databases from the same type can be from.

I have a setup in a service with datacontexts, repositories, business managers and service implementations. The Datacontext is introduced by the SimpleInjector container (I also tried with Unity), and, of course, registration in the container occurs before the ServiceHosts are created, which are created in the Single context mode (it is also checked for a call and per session).

I wrote an implementation of IDispatchMessageInspector that intercepts all SOAP messages and reads the message header and sets the connection string to the datacontext database, depending on the values ​​in the message header. But this will lead to problems because it is not “thread safe”, or at least when one call is not finished yet, the next call may set another connection string to the same data file, ruining it all.

So, I'm trying to make this registered according to the call (async, wcf lifestyle), but since this is a Windows service, it will not close and the container will not be correctly marked.

What can I do in this scenario to make it work?

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

Creating and starting the container host:

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();
}

Register 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;
+4
1

ServiceHosts, Single

. :

. InstanceContextMode.PerCall WCF. , , WCF .

, ( , ) . , , objec.

, , . :

  • . . .
  • . , , , . WCF , .
  • Async. . . .

. , 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.

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.

0

Source: https://habr.com/ru/post/1696310/


All Articles