Útmutató: Managed-Code DCOM migrálása WCF-be

A Windows Communication Foundation (WCF) az elosztott komponensobjektum-modell (DCOM) ajánlott és biztonságos választása a kiszolgálók és ügyfelek közötti felügyelt kódhívásokhoz elosztott környezetben. Ez a cikk bemutatja, hogyan migrálhatja a kódot a DCOM-ból a WCF-be az alábbi helyzetekben.

  • A távoli szolgáltatás objektumonkénti értéket ad vissza az ügyfélnek

  • Az ügyfél objektumonkénti értéket küld a távoli szolgáltatásnak

  • A távoli szolgáltatás egy objektumot ad vissza az ügyfélre mutató hivatkozással

Biztonsági okokból a WCF nem engedélyezi, hogy objektumot referencia szerint küldjön az ügyféltől a szolgáltatás részére. Az ügyfél és a kiszolgáló közötti oda-vissza beszélgetést igénylő forgatókönyv kétoldalas szolgáltatással érhető el a WCF-ben. További információért a duplex szolgáltatásokról, lásd: Duplex Services.

A WCF-szolgáltatások és -ügyfelek ezen szolgáltatásokhoz való létrehozásáról további információt az alapszintű WCF-programozás, a szolgáltatások tervezése és megvalósítása, valamint az ügyfelek létrehozása című témakörben talál.

DCOM-példakód

Ezekben a forgatókönyvekben a WCF használatával illusztrált DCOM-felületek a következő struktúrával rendelkeznek:

[ComVisible(true)]
[Guid("AA9C4CDB-55EA-4413-90D2-843F1A49E6E6")]
public interface IRemoteService
{
   Customer GetObjectByValue();
   IRemoteObject GetObjectByReference();
   void SendObjectByValue(Customer customer);
}

[ComVisible(true)]
[Guid("A12C98DE-B6A1-463D-8C24-81E4BBC4351B")]
public interface IRemoteObject
{
}

public class Customer
{
}

A szolgáltatás objektumonkénti értéket ad vissza

Ebben a forgatókönyvben hívást kezdeményez egy szolgáltatáshoz, és a metódus egy objektumot ad vissza, amelyet a kiszolgáló átad az ügyfélnek. Ez a forgatókönyv a következő COM-hívást jelenti:

public interface IRemoteService
{
    Customer GetObjectByValue();
}

Ebben az esetben az ügyfél megkapja egy objektum deszerializált másolatát a távoli szolgáltatásból. Az ügyfél anélkül használhatja ezt a helyi példányt, hogy visszahívna a szolgáltatásba. Más szóval az ügyfél garantáltan semmilyen módon nem vesz részt a szolgáltatásban a helyi példány metódusainak meghívásakor. A WCF mindig érték szerint ad vissza objektumokat a szolgáltatásból, ezért az alábbi lépések egy normál WCF-szolgáltatás létrehozását írják le.

1. lépés: A WCF szolgáltatás felületének meghatározása

Definiáljon egy nyilvános felületet a WCF szolgáltatáshoz, és jelölje meg a [ServiceContractAttribute] attribútummal. Jelölje meg az ügyfelek számára elérhetővé tenni kívánt metódusokat a [OperationContractAttribute] attribútummal. Az alábbi példa ezeket az attribútumokat mutatja be az ügyfél által meghívható kiszolgálóoldali felületi és felületi metódusok azonosításához. Az ehhez a forgatókönyvhöz használt módszer félkövér színben jelenik meg.

using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
. . .
[ServiceContract]
public interface ICustomerManager
{
    [OperationContract]
    void StoreCustomer(Customer customer);

    [OperationContract]     Customer GetCustomer(string firstName, string lastName);

}

2. lépés: Az adatszerződés meghatározása

Ezután létre kell hoznia egy adatszerződést a szolgáltatáshoz, amely leírja, hogyan lesznek kicserélve az adatok a szolgáltatás és ügyfelei között. Az adatszerződésben leírt osztályokat [DataContractAttribute] attribútummal kell megjelölni. Az ügyfél és a kiszolgáló számára is látható egyes tulajdonságokat vagy mezőket [DataMemberAttribute] attribútummal kell megjelölni. Ha engedélyezni szeretné az adatszerződés egyik osztályából származó típusokat, akkor azokat a [KnownTypeAttribute] attribútummal kell azonosítania. A WCF csak szerializálja vagy deszerializálja a szolgáltatási felületen lévő típusokat, és az ismert típusokként azonosított típusokat. Ha olyan típust próbál használni, amely nem ismert típus, kivétel történik.

Az adatszerződésekről további információt az Adatszerződések című témakörben talál.

[DataContract]
[KnownType(typeof(PremiumCustomer))]
public class Customer
{
    [DataMember]
    public string Firstname;
    [DataMember]
    public string Lastname;
    [DataMember]
    public Address DefaultDeliveryAddress;
    [DataMember]
    public Address DefaultBillingAddress;
}
 [DataContract]
public class PremiumCustomer : Customer
{
    [DataMember]
    public int AccountID;
}

 [DataContract]
public class Address
{
    [DataMember]
    public string Street;
    [DataMember]
    public string Zipcode;
    [DataMember]
    public string City;
    [DataMember]
    public string State;
    [DataMember]
    public string Country;
}

3. lépés: A WCF szolgáltatás implementálása

Ezután implementálnia kell a WCF szolgáltatásosztályt, amely megvalósítja az előző lépésben definiált felületet.

public class CustomerService: ICustomerManager
{
    public void StoreCustomer(Customer customer)
    {
        // write to a database
    }
    public Customer GetCustomer(string firstName, string lastName)
    {
        // read from a database
    }
}

4. lépés: A szolgáltatás és az ügyfél konfigurálása

WCF-szolgáltatás futtatásához deklarálnia kell egy végpontot, amely egy adott URL-címen teszi elérhetővé ezt a szolgáltatásfelületet egy adott WCF-kötés használatával. A kötés az ügyfelek és a kiszolgáló kommunikációjának átviteli, kódolási és protokolladatait határozza meg. Általában kötéseket ad hozzá a szolgáltatásprojekt konfigurációs fájljába (web.config). Az alábbiakban a példaszolgáltatáshoz tartozó kötési bejegyzés látható:

<configuration>
  <system.serviceModel>
    <services>
      <service name="Server.CustomerService">
        <endpoint address="http://localhost:8083/CustomerManager"
                  binding="basicHttpBinding"
                  contract="Shared.ICustomerManager" />
      </service>
    </services>
  </system.serviceModel>
</configuration>

Ezután konfigurálnia kell az ügyfelet, hogy megfeleljen a szolgáltatás által megadott kötési adatoknak. Ehhez adja hozzá a következőket az ügyfél alkalmazáskonfigurációs (app.config) fájljába.

<configuration>
  <system.serviceModel>
    <client>
      <endpoint name="customermanager"
                address="http://localhost:8083/CustomerManager"
                binding="basicHttpBinding"
                contract="Shared.ICustomerManager"/>
    </client>
  </system.serviceModel>
</configuration>

5. lépés: A szolgáltatás futtatása

Végül önállóan üzemeltetheti egy konzolalkalmazásban azáltal, hogy hozzáadja az alábbi sorokat a szolgáltatásalkalmazáshoz, és elindítja az alkalmazást. További információ a WCF-szolgáltatásalkalmazások üzemeltetésének egyéb módjairól, a Hosting Servicesről.

ServiceHost customerServiceHost = new ServiceHost(typeof(CustomerService));
customerServiceHost.Open();

6. lépés: A szolgáltatás meghívása az ügyfélen keresztül

Ha az ügyféltől szeretné meghívni a szolgáltatást, létre kell hoznia egy csatorna-előállítót a szolgáltatáshoz, és egy csatornát kell kérnie, amely lehetővé teszi a GetCustomer metódus közvetlen meghívását közvetlenül az ügyféltől. A csatorna implementálja a szolgáltatás felületét, és kezeli az alapul szolgáló kérés/válasz logikát. A metódushívás visszatérési értéke a szolgáltatás válaszának deszerializált másolata.

ChannelFactory<ICustomerManager> factory =
     new ChannelFactory<ICustomerManager>("customermanager");
ICustomerManager service = factory.CreateChannel();
Customer customer = service.GetCustomer("Mary", "Smith");

Az ügyfél egy értékenkénti objektumot küld a kiszolgálónak

Ebben a forgatókönyvben az ügyfél egy objektumot küld a kiszolgálónak, érték szerint. Ez azt jelenti, hogy a kiszolgáló megkapja az objektum deszerializált másolatát. A kiszolgáló meghívhat metódusokat ezen a példányon, és garantáltan nincs visszahívás az ügyfélkódba. Ahogy korábban említettük, a WCF normál adatcseréi értékenkéntiek. Ez garantálja, hogy a metódusok meghívása ezen objektumok egyikén csak helyileg fut – nem fogja meghívni a kódot az ügyfélen.

Ez a forgatókönyv a következő COM-metódushívást jelenti:

public interface IRemoteService
{
    void SendObjectByValue(Customer customer);
}

Ez a forgatókönyv ugyanazt a szolgáltatási felületet és adatszerződést használja, mint az első példában. Emellett az ügyfél és a szolgáltatás ugyanúgy lesz konfigurálva. Ebben a példában egy csatorna jön létre, amely elküldi az objektumot, és ugyanúgy fut. Ebben a példában azonban létre fog hozni egy klienst, amely meghívja a szolgáltatást, és egy objektumot érték szerint ad át. Az ügyfél által a szolgáltatási szerződésben meghívandó szolgáltatásmetódus félkövéren jelenik meg.

[ServiceContract]
public interface ICustomerManager
{
    [OperationContract]     void StoreCustomer(Customer customer);

    [OperationContract]
    Customer GetCustomer(string firstName, string lastName);
}

Kód hozzáadása a by-value objektumot küldő ügyfélhez

Az alábbi kód bemutatja, hogyan hoz létre az ügyfél egy új, értékalapú ügyfélobjektumot, hogyan hoz létre egy csatornát a ICustomerManager szolgáltatással való kommunikációhoz, és hogyan küldi el az ügyfélobjektumot.

A rendszerben az ügyfélobjektumot szerializálják és elküldik a szolgáltatásnak, ahol azt a szolgáltatás deszerializálja az objektum egy új példányává. Az objektumon a szolgáltatás által meghívt metódusok csak helyileg lesznek végrehajtva a kiszolgálón. Fontos megjegyezni, hogy ez a kód egy származtatott típus (PremiumCustomer) küldését szemlélteti. A szolgáltatási szerződés objektumot Customer vár, de a szolgáltatási adat szerződés a [KnownTypeAttribute] attribútummal jelzi, hogy PremiumCustomer szintén megengedett. A WCF nem próbálja szerializálni vagy deszerializálni bármely más típust ezen a szolgáltatási felületen keresztül.

PremiumCustomer customer = new PremiumCustomer();
customer.Firstname = "John";
customer.Lastname = "Doe";
customer.DefaultBillingAddress = new Address();
customer.DefaultBillingAddress.Street = "One Microsoft Way";
customer.DefaultDeliveryAddress = customer.DefaultBillingAddress;
customer.AccountID = 42;

ChannelFactory<ICustomerManager> factory =
   new ChannelFactory<ICustomerManager>("customermanager");
ICustomerManager customerManager = factory.CreateChannel();
customerManager.StoreCustomer(customer);

A szolgáltatás egy objektumot ad vissza hivatkozás alapján

Ebben az esetben az ügyfélalkalmazás hívást kezdeményez a távoli szolgáltatáshoz, a metódus pedig egy objektumot ad vissza, amelyet a szolgáltatás hivatkozása továbbít az ügyfélnek.

Ahogy korábban említettük, a WCF-szolgáltatások mindig érték szerint adnak vissza objektumot. Azonban a EndpointAddress10 osztály használatával hasonló eredményt érhet el. Ez EndpointAddress10 egy szerializálható érték szerinti objektum, amelyet az ügyfél használhat, hogy egy munkamenet-alapú referencia szerinti objektumot szerezzen be a kiszolgálón.

Az ebben a forgatókönyvben bemutatott, a WCF-ben található by-reference objektum viselkedése eltér a DCOM-tól. A DCOM-ban a kiszolgáló közvetlenül vissza tud adni egy referenciaobjektumot az ügyfélnek, és az ügyfél meghívhatja az objektum metódusát, amely a kiszolgálón fut. A WCF-ben azonban a visszaadott objektum mindig érték szerint adódik vissza. Az ügyfélnek meg kell szereznie azt a by-value objektumot, amelyet a EndpointAddress10 jelöl, majd azt a saját munkamenet-alapú hivatkozási objektumának létrehozására kell használnia. Az ügyfélmetódus meghívja a kiszolgálón végrehajtott munkamenet-objektumot. Más szóval a WCF-ben ez a referencia-objektum egy normál WCF-szolgáltatás, amely munkamenet-alapúra van konfigurálva.

A WCF-ben a munkamenet a két végpont között küldött több üzenet korrelációjának módja. Ez azt jelenti, hogy ha egy ügyfél kapcsolatot szerez ezzel a szolgáltatással, létrejön egy munkamenet az ügyfél és a kiszolgáló között. Az ügyfél a kiszolgálóoldali objektum egyetlen egyedi példányát fogja használni az egyetlen munkameneten belüli összes interakcióhoz. A munkamenet-alapú WCF-szerződések hasonlóak a kapcsolatorientált hálózati kérés-/válaszmintákhoz.

Ezt a forgatókönyvet a következő DCOM-metódus képviseli.

public interface IRemoteService
{
    IRemoteObject GetObjectByReference();
}

1. lépés: A munkamenet-alapú WCF szolgáltatás felületének és implementációjának meghatározása

Először definiáljon egy WCF-szolgáltatásfelületet, amely tartalmazza a munkamenet-alapú objektumot.

Ebben a kódban a munkamenet-objektumot az ServiceContract attribútum jelöli meg, amely normál WCF-szolgáltatási felületként azonosítja azt. Emellett a SessionMode tulajdonság úgy van beállítva, hogy azt jelezze, hogy munkamenet-alapú szolgáltatás lesz.

[ServiceContract(SessionMode = SessionMode.Allowed)]
public interface ISessionBoundObject
{
    [OperationContract]
    string GetCurrentValue();

    [OperationContract]
    void SetCurrentValue(string value);
}

Az alábbi kód a szolgáltatás implementálását mutatja be.

A szolgáltatás a [ServiceBehavior] attribútummal van megjelölve, az InstanceContextMode.PerSessions értékre állított InstanceContextMode tulajdonság pedig azt jelzi, hogy minden munkamenethez létre kell hozni egy ilyen típusú egyedi példányt.

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
    public class MySessionBoundObject : ISessionBoundObject
    {
        private string _value;

        public string GetCurrentValue()
        {
            return _value;
        }

        public void SetCurrentValue(string val)
        {
            _value = val;
        }

    }

2. lépés: A munkamenetes objektum WCF-gyárszolgáltatásának definiálása

A munkamenet-objektumot létrehozó szolgáltatást meg kell határozni és végre kell hajtani. Az alábbi kód bemutatja, hogyan teheti ezt meg. Ez a kód létrehoz egy másik WCF-szolgáltatást, amely egy objektumot EndpointAddress10 ad vissza. Ez egy olyan végpont szerializálható formája, amellyel létrehozhatja a munkamenet-teljes objektumot.

[ServiceContract]
    public interface ISessionBoundFactory
    {
        [OperationContract]
        EndpointAddress10 GetInstanceAddress();
    }

A következő a szolgáltatás implementálása. Ez az implementáció egy singleton csatorna gyárat tart fenn munkamenet-objektumok létrehozásához. Amikor GetInstanceAddress meghívják, létrehoz egy csatornát, és létrehoz egy EndpointAddress10 objektumot, amely a csatornához társított távoli címre mutat. EndpointAddress10 olyan adattípus, amely visszaadható az ügyfélnek érték szerint.

public class SessionBoundFactory : ISessionBoundFactory
    {
        public static ChannelFactory<ISessionBoundObject> _factory =
            new ChannelFactory<ISessionBoundObject>("sessionbound");

        public SessionBoundFactory()
        {
        }

        public EndpointAddress10 GetInstanceAddress()
        {
            IClientChannel channel = (IClientChannel)_factory.CreateChannel();
            return EndpointAddress10.FromEndpointAddress(channel.RemoteAddress);
        }
    }

3. lépés: A WCF-szolgáltatások konfigurálása és elindítása

A szolgáltatások üzemeltetéséhez a következő kiegészítéseket kell elvégeznie a kiszolgáló konfigurációs fájljában (web.config).

  1. Adjon hozzá egy szakaszt <client> , amely leírja a munkamenet-objektum végpontját. Ebben az esetben a kiszolgáló ügyfélként is működik, és konfigurálva kell lennie ennek engedélyezéséhez.

  2. A szakaszban deklarálja a <services> gyári és a munkamenet-alapú objektum szolgáltatásvégpontjait. Ez lehetővé teszi az ügyfél számára a szolgáltatásvégpontokkal való kommunikációt, a EndpointAddress10 munkamenet-alapú csatorna beszerzését és létrehozását.

Az alábbi példakonfigurációs fájl az alábbi beállításokkal rendelkezik:

<configuration>
  <system.serviceModel>
    <client>
      <endpoint name="sessionbound"
                address="net.tcp://localhost:8081/SessionBoundObject"
                binding="netTcpBinding"
                contract="Shared.ISessionBoundObject"/>
    </client>

    <services>
      <service name="Server.MySessionBoundObject">
        <endpoint address="net.tcp://localhost:8081/SessionBoundObject"
                  binding="netTcpBinding"
                  contract="Shared.ISessionBoundObject" />
      </service>
      <service name="Server.SessionBoundFactory">
        <endpoint address="net.tcp://localhost:8081/SessionBoundFactory"
                  binding="netTcpBinding"
                  contract="Shared.ISessionBoundFactory" />
      </service>
    </services>
  </system.serviceModel>
</configuration>

Adja hozzá a következő sorokat egy konzolalkalmazáshoz a szolgáltatás önkiszolgáló üzemeltetéséhez és az alkalmazás elindításához.

ServiceHost factoryHost = new ServiceHost(typeof(SessionBoundFactory));
factoryHost.Open();

ServiceHost sessionBoundServiceHost = new ServiceHost(
typeof(MySessionBoundObject));
sessionBoundServiceHost.Open();

4. lépés: Az ügyfél konfigurálása és a szolgáltatás meghívása

Konfigurálja az ügyfelet a WCF-szolgáltatásokkal való kommunikációra a projekt alkalmazáskonfigurációs fájljának következő bejegyzéseivel (app.config).

<configuration>
  <system.serviceModel>
    <client>
      <endpoint name="sessionbound"
                address="net.tcp://localhost:8081/SessionBoundObject"
                binding="netTcpBinding"
                contract="Shared.ISessionBoundObject"/>
      <endpoint name="factory"
                address="net.tcp://localhost:8081/SessionBoundFactory"
                binding="netTcpBinding"
                contract="Shared.ISessionBoundFactory"/>
    </client>
  </system.serviceModel>
</configuration>

A szolgáltatás meghívásához adja hozzá a kódot az ügyféloldali alkalmazáshoz a következők végrehajtásához:

  1. Hozzon létre egy csatornát a ISessionBoundFactory szolgáltatáshoz.

  2. A csatornát használva meghívhatja a ISessionBoundFactory szolgáltatást, és megszerezhet egy EndpointAddress10 objektumot.

  3. Használja a EndpointAddress10 egy csatorna létrehozásához egy munkamenethez kötődő objektum beszerzéséhez.

  4. Hívja meg a SetCurrentValue és GetCurrentValue metódusokat annak bemutatására, hogy ugyanaz az objektumpéldány kerül felhasználásra több hívás során.

ChannelFactory<ISessionBoundFactory> factory =
        new ChannelFactory<ISessionBoundFactory>("factory");

ISessionBoundFactory sessionBoundFactory = factory.CreateChannel();

EndpointAddress10 address = sessionBoundFactory.GetInstanceAddress();

ChannelFactory<ISessionBoundObject> sessionBoundObjectFactory =
    new ChannelFactory<ISessionBoundObject>(
        new NetTcpBinding(),
        address.ToEndpointAddress());

ISessionBoundObject sessionBoundObject =
        sessionBoundObjectFactory.CreateChannel();

sessionBoundObject.SetCurrentValue("Hello");
if (sessionBoundObject.GetCurrentValue() == "Hello")
{
    Console.WriteLine("Session-full instance management works as expected");
}

Lásd még