C# [Wcf] [Spring.Net] Merkwürdiges Verhalten des Wcf-Instanzmanagements

Thaxll'ssillyia

Captain
Registriert
Dez. 2007
Beiträge
3.501
Hallo Community!

Ich habe einen Wcf-Service konfiguriert von Spring.Net. Dahinter liegen ein paar Services und DAOs, welche per Dependency Injection dem WCF mitgegeben werden. Verwendete Technologien:

- .NET Framework 4.6.1
- Spring.Net 2.0.1.45000

Mein Problem: Spring generiert wahllos neue Wcf-Instanzen, aber nicht nur für den Wcf-Service, sondern auch für alle Services und Daos. Dies braucht etwa 4 Sekunden, was natürlich in meiner Client-Anwendung für enorme Lags sorgt (hab noch kein async eingebaut). Gehostet wird der Wcf im Visual-Studio-eigenen IIS Express. Ich greife auf den Wcf-Service über eine Singeton-WcfServiceClient-Instanz zu.

Wenn ich den Client starte, dann erstellt Spring richtigerweise eine neue Wcf-Instanz (das gleiche passiert wenn ich im Browser die .svc aufrufe).
Ich rufe danach innerhalb von 50 ms den Wcf-Service erneut auf -> hier wird noch keine neue Instanz erstellt.
Dann rufe ich den Wcf-Service 5 Sekunden später getriggert über eine Schaltfläche auf -> eine neue Wcf-Instanz wird erstellt. Wenn ich den Button erneut drücke oder andere Funktionen des Wcf-Services aufrufe, wird ebenfalls eine neue Instanz erstellt.
Auf Client-Seite existiert definitiv nur eine WcfServiceClient-Instanz. Ich hab echt kein Plan aus welchem Grund oder nach welchem Muster neue Wcf-Instanzen erstellt werden.

Configs
svc Markup:
Code:
<%@ ServiceHost Language="C#" Debug="true" Service="Kassensystem.Server.WcfService.WcfService" CodeBehind="WcfService.svc.cs" Factory="Spring.ServiceModel.Activation.ServiceHostFactory" %>

web-config:
Code:
<?xml version="1.0" ?>
<configuration>
  <configSections>
    <sectionGroup name="spring">
      <section name="context"
               type="Spring.Context.Support.ContextHandler, Spring.Core" />
      <section name="parsers"
               type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core" />
    </sectionGroup>
  </configSections>
  <spring>
    <context name="spring.root"
             id="connectorConfigFiles">
      <resource uri="~/bin/Config/Spring.Persistence.xml" />
      <resource uri="~/bin/Config/Spring.Service.xml" />
      <resource uri="~/bin/Config/Spring.Database.xml" />
      <resource uri="~/bin/Config/Spring.TestData.xml" />
      <resource uri="~/bin/Config/Spring.WcfService.xml" />
    </context>
    <parsers>
      <parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />
      <parser type="Spring.Transaction.Config.TxNamespaceParser, Spring.Data" />
    </parsers>
  </spring>
  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext"
         value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true"
                 targetFramework="4.6.1" />
    <httpRuntime targetFramework="4.6.1"
                 maxRequestLength="204800"
                 executionTimeout="7200" />
  </system.web>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="true"
                           httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <protocolMapping>
      <add binding="basicHttpsBinding"
           scheme="https" />
    </protocolMapping>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"
                               multipleSiteBindingsEnabled="true" />
    <bindings>
      <basicHttpBinding>
        <binding name="httpBinding"
                 maxBufferSize="2147483647"
                 maxReceivedMessageSize="2147483647"
                 receiveTimeout="00:40:00"
                 openTimeout="00:40:00"
                 closeTimeout="00:40:00"
                 sendTimeout="00:40:00">
          <readerQuotas maxDepth="2147483647"
                        maxStringContentLength="2147483647"
                        maxArrayLength="2147483647"
                        maxBytesPerRead="2147483647"
                        maxNameTableCharCount="2147483647" />
          <security mode="None" />
        </binding>
      </basicHttpBinding>
    </bindings>
    <services>
      <service name="Kassensystem.Server.WcfService.WcfService">
        <endpoint address=""
                  binding="basicHttpBinding"
                  bindingConfiguration="httpBinding"
                  contract="Kassensystem.Server.WcfService.IWcfService" />
        <endpoint address="mex"
                  binding="mexHttpBinding"
                  contract="IMetadataExchange" />
      </service>
    </services>
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true" />
    <directoryBrowse enabled="true" />
  </system.webServer>
</configuration>

Spring.WcfService.xml:
Code:
<?xml version="1.0"
  encoding="utf-8" ?>

<objects xmlns="http://www.springframework.net"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.springframework.net 
             http://www.springframework.net/xsd/spring-objects.xsd"
         xmlns:db="http://www.springframework.net/database"
         xmlns:tx="http://www.springframework.net/tx">

  <object id="Kassensystem.Server.WcfService.WcfService"
          type="Kassensystem.Server.WcfService.WcfService, Kassensystem.Server">
    <property name="ArticleGroupService"
              ref="articleGroupService" />
    <property name="ArticleHistoryBaseService"
              ref="articleHistoryBaseService" />
    <property name="ArticleService"
              ref="articleService" />
    <property name="ClearingService"
              ref="clearingService" />
    <property name="CustomerCheckinService"
              ref="customerCheckinService" />
    <property name="CustomerService"
              ref="customerService" />
    <property name="ExtraordinaryPaymentService"
              ref="extraordinaryPaymentService" />
    <property name="PaymentHistoryService"
              ref="paymentHistoryService" />
    <property name="ShoppingCartHistoryService"
              ref="shoppingCartHistoryService" />
    <property name="TariffClassService"
              ref="tariffClassService" />
    <property name="UserService"
              ref="userService" />
    <property name="TestData"
              ref="testData" />
  </object>
</objects>

Laut Internet ist das Standard-Instanzverhalten bei Wcf-Services PerSession. In der Spring-Dokumentation steht nichts dazu, ob Spring daran was verändert. Auch hätte ich gedacht, dass Spring nur den Wcf-Service neu instanziiert, und die bereits angelegten Services wieder in die Properties injected anstatt alles neu zu erstellen...

Im Internet hab ich auch Hinweise darauf gefunden, dass der IIS die Instanzen erzeugt und dabei das InstanceBehaviour des WcfServices ignoriert. Aber dazu war nichts detailliertes erklärt.

Kann mir jemand helfen? Bin für jede Hilfe dankbar!

VG, Thax
 
Hallo,

ich kenne Spring nicht nur Wcf aber das Verhalten passt dazu, dass die Service Instanz defaultmässig pro Session erstellt wird. Es gibt noch pro Request und nur einmal als Singleton.

Du musst die Wcf Service Instanz als Singleton betreiben. Wenn die Service Instanz alle Verweise enthält und recycelt wird, werden natürlich auch alle verweisten Objekte recycelt.

Meiner Meinung nach ist das auch die einzige sinnvolle Einstellung. Dabei musst Du nur beachten, dass jeder Request als einzelner Thread reinkommt, d.h. Du musst Dich um das Multithreading kümmern. Ich vermute das ist deshalb nicht die default Einstellung, damit der unerfahrenen Programmierer damit nicht auf die Nase fällt.

Ebenfalls macht das Hosten im IIS auch stellenweise Probleme, wir hätten da auch das Problem, dass der Service ab und an mal recycelt wird und uns deswegen einen eigenen Service Host geschrieben.

https://msdn.microsoft.com/de-de/library/ms731193(v=vs.110).aspx

https://www.codeproject.com/Articles/86007/ways-to-do-WCF-instance-management-Per-call-Per
 
Hi, Danke für die Antwort.

ich kenne Spring nicht nur Wcf aber das Verhalten passt dazu, dass die Service Instanz defaultmässig pro Session erstellt wird

Naja nicht wirklich. Pro Session heißt für mich: Mach pro Client eine Session auf, solange bis der Client sie zumacht oder der Timeout kommt. Derzeit scheint der aber wahllos irgendwelche Instanzen aufzumachen.

Das Problem ist halt, dass der IIS Express jedwedes InstanceBehaviour über dem Service ignoriert (getestet). Eine Lösung wäre allerdings wirklich, den Service nicht im IIS, sondern im Rahmen einer extra App (zB Konsolenanwendung) hochzufahren und sich selber ums Session Management zu kümmern. Ich probiers mal und meld mich dann wieder.

Singleton-Instanz kommt leider nicht in Frage, weil später mehrere Clients auf den WCF zugeifen müssen.
 
Zuletzt bearbeitet:
Auch mit mehreren Clients geht eine Singleton Instanz. Wie gesagt jeder Request kommt über einen eigenen Thread rein, dann müssen entweder Deine Klassen Multithreadingfähig sein oder Du erstellt für jeden Request eigene Instanzen Deiner Klassen oder Du arbeitest mit Locks etc. an den kritischen Stellen. Das kann sehr sinnvoll sein, wenn die Service lange zum Hochfahren brauchte, bei uns hats 45 Min gedauert. Parallele Request in einer Singleton Instanz dann.

Aber der IIS ist unberechenbar, zum Entwickeln hatte ich auch einen Konsolenhost, produktiv hatten wir einen Windows Dienst der belobige WCF Services hosten könnte.
 
Klappt erstmal mit Singleton-Attribut und Applicaation-Hosting mit der Instanz. Danke.
Bin leider nicht so zufrieden mit dem Singleton. Wenn ich auf PerSession umschalten, dann erstellt er wieder wild irgendwelche Instanzen ohne erkennbare Struktur. Ich find bloß keine Config dazu wo man was am Sessionhandling ändern könnte...
 
Ich finde gerade den Singleton Ansatz am besten, weil Du da volle Kontrolle über die Sessions hast und sowas einen State im Service verwalten kannst, Caching Mechanismen einbauen kannst etc. Du hast einfach die volle Kontrolle über alles was passiert.
 
Wahrscheinlich hast du recht. Aber im Gegensatz zu dir hab ich noch nicht mit Singleton-Wcf-Instanzen gearbeitet und somit wenig Erfahrung mit dem SessionHandling und ThreadSafe-Mechanismen ;)
 
Zurück
Oben