Cara: Migrasi Kode Terkelola DCOM ke WCF

Windows Communication Foundation (WCF) adalah pilihan yang direkomendasikan dan aman daripada Model Objek Komponen Terdistribusi (DCOM) untuk panggilan kode terkelola antara server dan klien di lingkungan terdistribusi. Artikel ini menunjukkan cara Anda melakukan migrasi kode dari DCOM ke WCF untuk skenario berikut ini.

  • Layanan jarak jauh mengembalikan objek berdasarkan nilai ke klien

  • Klien mengirimkan objek berdasarkan nilai ke layanan jarak jauh

  • Layanan jarak jauh mengembalikan objek dengan referensi ke klien

Untuk alasan keamanan, pengiriman objek dengan referensi dari klien ke layanan tidak diperbolehkan di WCF. Skenario yang membutuhkan percakapan bolak-balik antara klien dan server dapat dicapai di WCF menggunakan layanan duplex. Untuk informasi selengkapnya tentang layanan duplex, baca Layanan Duplex.

Untuk detail selengkapnya tentang membuat layanan dan klien WCF untuk layanan tersebut, baca Pemrograman WCF Dasar, Merancang dan Menerapkan Layanan, dan Membangun Klien.

kode contoh DCOM

Untuk skenario ini, antarmuka DCOM yang diilustrasikan menggunakan WCF memiliki struktur berikut:

[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  
{  
}  

Layanan mengembalikan objek berdasarkan nilai

Untuk skenario ini, Anda membuat panggilan ke layanan dan metode itu mengembalikan objek, yang diteruskan oleh nilai dari server ke klien. Skenario ini mewakili panggilan COM berikut ini:

public interface IRemoteService  
{  
    Customer GetObjectByValue();  
}  

Dalam skenario ini, klien menerima salinan deserialisasi objek dari layanan jarak jauh. Klien dapat berinteraksi dengan salinan lokal ini tanpa menelepon kembali ke layanan. Dengan kata lain, klien dijamin bahwa layanan tidak akan terlibat dengan cara apa pun ketika metode pada salinan lokal dipanggil. WCF selalu mengembalikan objek dari layanan berdasarkan nilai, jadi langkah-langkah berikut menjelaskan pembuatan layanan WCF biasa.

Langkah 1: Tentukan antarmuka layanan WCF

Tentukan antarmuka publik untuk layanan WCF dan tandai dengan atribut [ServiceContractAttribute]. Tandai metode yang ingin Anda tampilkan kepada klien dengan atribut [OperationContractAttribute]. Contoh berikut menunjukkan penggunaan atribut ini untuk mengidentifikasi antarmuka sisi server dan metode antarmuka yang dapat dipanggil klien. Metode yang digunakan untuk skenario ini ditampilkan dalam huruf tebal.

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

Langkah 2: Tentukan kontrak data

Selanjutnya Anda harus membuat kontrak data untuk layanan, yang akan menjelaskan cara data akan dipertukarkan antara layanan dan kliennya. Kelas yang dijelaskan dalam kontrak data harus ditandai dengan atribut [DataContractAttribute]. Masing-masing properti atau bidang yang Anda inginkan terlihat oleh klien dan server harus ditandai dengan atribut [DataMemberAttribute]. Jika Anda ingin jenis yang diturunkan dari kelas dalam kontrak data diizinkan, Anda harus mengidentifikasinya dengan atribut [KnownTypeAttribute]. WCF hanya akan membuat serial atau deserialisasi jenis di antarmuka layanan dan jenis yang diidentifikasi sebagai jenis yang diketahui. Jika Anda mencoba menggunakan jenis yang bukan jenis yang diketahui, pengecualian akan terjadi.

Untuk informasi selengkapnya tentang kontrak data, baca Kontrak Data.

[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;  
}  

Langkah 3: Terapkan layanan WCF

Selanjutnya, Anda harus mengimplementasikan kelas layanan WCF yang mengimplementasikan antarmuka yang Anda definisikan di langkah sebelumnya.

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

Langkah 4: Konfigurasikan layanan dan klien

Untuk menjalankan layanan WCF, Anda perlu mendeklarasikan titik akhir yang memperlihatkan antarmuka layanan tersebut pada URL tertentu menggunakan pengikatan WCF tertentu. Pengikatan menentukan transportasi, pengkodean dan rincian protokol untuk klien serta server untuk berkomunikasi. Anda biasanya menambahkan pengikatan ke file konfigurasi proyek layanan (web.config). Berikut ini menunjukkan entri yang mengikat untuk layanan contoh:

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

Selanjutnya, Anda perlu mengonfigurasi klien agar sesuai dengan informasi pengikatan yang ditentukan oleh layanan. Untuk melakukannya, tambahkan berikut ini ke file konfigurasi aplikasi (app.config) klien.

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

Langkah 5: Jalankan layanan

Terakhir, Anda dapat meng-host-nya sendiri di aplikasi konsol dengan menambahkan baris berikut ke aplikasi layanan, dan memulai aplikasi. Untuk informasi selengkapnya tentang cara lain untuk meng-host aplikasi layanan WCF, Layanan Hosting.

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

Langkah 6: Hubungi layanan dari klien

Untuk memanggil layanan dari klien, Anda perlu membuat pabrik saluran untuk layanan, dan meminta saluran, yang akan memungkinkan Anda untuk langsung memanggil metode GetCustomer langsung dari klien. Saluran mengimplementasikan antarmuka layanan dan menangani logika permintaan/balasan yang mendasarinya untuk Anda. Nilai pengembalian dari pemanggilan metode itu adalah salinan deserialisasi dari respons layanan.

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

Klien mengirimkan objek nilai ke server

Dalam skenario ini, klien mengirimkan objek ke server, berdasarkan nilai. Ini berarti bahwa server akan menerima salinan objek yang dideserialisasi. Server dapat memanggil metode pada salinan itu dan dijamin tidak ada panggilan balik ke kode klien. Seperti disebutkan sebelumnya, pertukaran data WCF normal adalah berdasarkan nilai. Ini menjamin bahwa metode pemanggilan pada salah satu objek ini hanya dieksekusi secara lokal – metode ini tidak akan memanggil kode pada klien.

Skenario ini mewakili panggilan metode COM berikut ini:

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

Skenario ini menggunakan antarmuka layanan dan kontrak data yang sama seperti yang ditunjukkan pada contoh pertama. Selain itu, klien dan layanan akan dikonfigurasi dengan cara yang sama. Dalam contoh ini, saluran dibuat untuk mengirim objek dan dijalankan dengan cara yang sama. Namun untuk contoh ini, Anda akan membuat klien yang memanggil layanan, meneruskan objek berdasarkan nilai. Metode layanan yang akan dipanggil klien dalam kontrak layanan ditampilkan dalam huruf tebal:

[ServiceContract]  
public interface ICustomerManager  
{  
    [OperationContract]     void StoreCustomer(Customer customer);  
  
    [OperationContract]  
    Customer GetCustomer(string firstName, string lastName);  
}  

Tambahkan kode ke klien yang mengirim objek berdasarkan nilai

Kode berikut menunjukkan cara klien membuat objek pelanggan berdasarkan nilai baru, membuat saluran untuk berkomunikasi dengan layanan ICustomerManager, dan mengirimkan objek pelanggan ke sana.

Objek pelanggan akan diserialisasi, lalu dikirim ke layanan, yang akan dideserialisasi oleh layanan menjadi salinan baru dari objek itu. Metode apa pun yang dipanggil layanan pada objek ini hanya akan dijalankan secara lokal di server. Penting untuk dicatat bahwa kode ini mengilustrasikan pengiriman jenis turunan (PremiumCustomer). Kontrak layanan mengharapkan objek Customer, tetapi kontrak data layanan menggunakan atribut [KnownTypeAttribute] untuk menunjukkan bahwa PremiumCustomer juga diizinkan. WCF akan gagal dalam upaya membuat serial atau deserialisasi jenis lain apa pun melalui antarmuka layanan ini.

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

Layanan mengembalikan objek dengan referensi

Untuk skenario ini, aplikasi klien membuat panggilan ke layanan jarak jauh dan metode mengembalikan objek, yang diteruskan dengan referensi dari layanan ke klien.

Seperti disebutkan sebelumnya, layanan WCF selalu mengembalikan objek berdasarkan nilai. Namun, Anda dapat mencapai hasil yang serupa dengan menggunakan kelas EndpointAddress10. EndpointAddress10 adalah objek berdasarkan nilai serial yang dapat digunakan oleh klien untuk mendapatkan objek referensi sesi penuh di server.

Perilaku objek berdasarkan-referensi di WCF yang ditampilkan dalam skenario ini berbeda dari DCOM. Di DCOM, server dapat mengembalikan objek referensi ke klien secara langsung, dan klien dapat memanggil metode objek itu, yang dijalankan di server. Akan tetapi, di WCF objek yang dikembalikan selalu berdasarkan nilai. Klien harus mengambil objek berdasarkan nilai, yang diwakili oleh EndpointAddress10 dan menggunakannya untuk membuat objek berdasarkan referensi penuh sesi. Metode klien memanggil objek penuh sesi yang dijalankan di server. Dengan kata lain, objek bedasarkan referensi oleh WCF ini adalah layanan WCF normal yang dikonfigurasi untuk menjadi penuh sesi.

Di WCF, sesi adalah cara menghubungkan beberapa pesan yang dikirim antara dua titik akhir. Ini berarti bahwa setelah klien mendapatkan koneksi ke layanan ini, sesi akan dibuat antara klien dan server. Klien akan menggunakan satu instans unik dari objek sisi server untuk semua interaksinya dalam sesi tunggal ini. Kontrak WCF sesi mirip dengan pola permintaan/respons jaringan berorientasi koneksi.

Skenario ini diwakili oleh metode DCOM berikut.

public interface IRemoteService  
{  
    IRemoteObject GetObjectByReference();  
}  

Langkah 1: Tentukan antarmuka dan implementasi layanan WCF Sesi

Pertama, tentukan antarmuka layanan WCF yang berisi objek penuh sesi.

Dalam kode ini, objek sesi ditandai dengan atribut ServiceContract, yang mengidentifikasinya sebagai antarmuka layanan WCF biasa. Selain itu, properti SessionMode diatur untuk menunjukkan bahwa properti akan menjadi layanan sesi.

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

Kode berikut menunjukkan implementasi layanan.

Layanan ditandai dengan atribut [ServiceBehavior], dan properti InstanceContextMode-nya diatur menjadi InstanceContextMode.PerSessions untuk menunjukkan bahwa instans unik dari jenis ini harus dibuat untuk setiap sesi.

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]  
    public class MySessionBoundObject : ISessionBoundObject  
    {  
        private string _value;  
  
        public string GetCurrentValue()  
        {  
            return _value;  
        }  
  
        public void SetCurrentValue(string val)  
        {  
            _value = val;  
        }  
  
    }  

Langkah 2: Tentukan layanan pabrik WCF untuk objek penuh sesi

Layanan yang membuat objek penuh sesi harus didefinisikan dan diimplementasikan. Kode berikut menunjukkan cara melakukannya. Kode ini membuat layanan WCF lain yang mengembalikan objek EndpointAddress10. Objek ini adalah bentuk serial dari titik akhir yang dapat digunakan untuk membuat objek penuh sesi.

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

Berikut ini adalah implementasi dari layanan tersebut. Implementasi ini mempertahankan pabrik saluran tunggal untuk membuat objek sesi. Saat GetInstanceAddress dipanggil akan membuat saluran dan membuat objek EndpointAddress10 yang menunjuk ke alamat jarak jauh yang terkait dengan saluran ini. EndpointAddress10 adalah jenis data yang dapat dikembalikan ke klien berdasarkan nilai.

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

Langkah 3: Konfigurasi dan mulai layanan WCF

Untuk meng-host layanan ini, Anda perlu membuat tambahan berikut ke file konfigurasi server (web.config).

  1. Tambahkan bagian <client> yang menjelaskan titik akhir untuk objek penuh sesi. Dalam skenario ini, server juga bertindak sebagai klien dan harus dikonfigurasi untuk mengaktifkannya.

  2. Di bagian <services>, nyatakan titik akhir layanan untuk objek pabrik dan penuh sesi. Ini memungkinkan klien untuk berkomunikasi dengan titik akhir layanan, memperoleh EndpointAddress10, dan membuat saluran penuh sesi.

Berikut ini adalah contoh file konfigurasi dengan pengaturan ini:

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

Tambahkan baris berikut ke aplikasi konsol, untuk menghosting sendiri layanan, dan memulai aplikasi.

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

Langkah 4: Konfigurasikan klien dan panggil layanan

Konfigurasikan klien untuk berkomunikasi dengan layanan WCF dengan membuat entri berikut dalam file konfigurasi aplikasi proyek (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>  

Untuk memanggil layanan, tambahkan kode ke klien untuk melakukan hal berikut:

  1. Buat saluran ke layanan ISessionBoundFactory.

  2. Gunakan saluran untuk memanggil layanan ISessionBoundFactory dan mendapatkan objek EndpointAddress10.

  3. Gunakan EndpointAddress10 untuk membuat saluran guna mendapatkan objek sesi.

  4. Panggil metode SetCurrentValue dan GetCurrentValue untuk menunjukkan bahwa instans objek yang sama digunakan di beberapa panggilan.

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

Lihat juga