Bir SOAP servisine küçük bir alan eklemek, yalnız sunucu projesindeki sınıfı değiştirmek değildir. Karşı tarafta bu sözleşmeden üretilmiş bir istemci bulunabilir. WCF ile çalışırken önce ağdan hangi operasyonların ve hangi alanların geçtiğini açık tutarım. Arşivdeki sözleşme tabanlı servis yaklaşımını burada iki alanlı bir yanıt üzerinden anlatıyorum.
Servisin arayüzü ile taşıdığı veriyi ayırmak
IItemService hangi işlemin çağrılabileceğini, ItemReply ise o işlemin yanıtını tanımlar. GetItem(7) çağrısının kavramsal sonucu yalnız Id ve Title alanlarını taşıyan bir cevaptır. Veritabanı entity'sini doğrudan yanıt yapmak yerine ayrı bir taşıma modeli seçmek, iç tablo değişikliklerinin istemci sözleşmesine rastgele yayılmasını önler.
using System.Runtime.Serialization;
using System.ServiceModel;
[ServiceContract]
public interface IItemService
{
[OperationContract] ItemReply GetItem(int id);
}
[DataContract]
public sealed class ItemReply
{
[DataMember(Order=1)] public int Id { get; set; }
[DataMember(Order=2)] public string Title { get; set; } = "";
}
ServiceContract, arayüzün bir servis sözleşmesi olduğunu; OperationContract, GetItem metodunun dışarı açılan operasyon olduğunu belirtir. DataContract ve DataMember ise serileştirilen veriyi açıklar. Sınıfa yeni bir iç özellik eklemekle o alanı ağ sözleşmesine eklemek farklı kararlardır. Bu açıklık, servisi çağıracak tarafın neye bağımlı olduğunu görmeyi kolaylaştırır.
Bu kod neden tek başına servis başlatmıyor?
Örnekte host, endpoint adresi, binding ve servis uygulaması yok. Sözleşmenin derlenmesi çalışan bir SOAP sunucusu kanıtı değildir. Klasik WCF sunucusu .NET Framework bağlamında değerlendirilir; modern .NET tarafındaki istemci paketleri veya CoreWCF ile sunucu barındırma ayrı konulardır. Bir System.ServiceModel paketi ekleyince bütün eski host yapılandırmasının taşınmış olduğunu varsaymam.
Binding seçimi taşıma, kodlama ve güvenlik beklentilerini etkiler. İstemci ile sunucunun yalnız metot adlarında değil, adres ve iletişim seçeneklerinde de uzlaşması gerekir. Büyük yanıt, mesaj boyutu sınırı veya timeout problemi DTO adını değiştirerek çözülmez; o katmanın ölçümü ve yapılandırmasıyla ele alınır.
Sözleşmeye alan eklerken eski istemciyi düşünmek
DataMember üzerindeki Order değerleri serileştirme sırasını görünür kılıyor. Ancak sıraları yazmak bütün değişiklikleri geriye uyumlu yapmaz. Alan adını, namespace'i, zorunluluğunu veya tipini değiştirmek üretilmiş istemciyi etkileyebilir. Ek alanın eksik geldiği durumda sunucunun ve istemcinin hangi varsayımı kullanacağını da belirlemek gerekir.
Bu nedenle örneğin sonraki sürümünü yalnız yeni proxy ile denemem. Eski sözleşmeyle oluşturulmuş istemcinin yeni sunucuyla nasıl davrandığını da kontrol ederim. Bir alanın görünmemesi bazen beklenen sürüm uyumudur; bazen sözleşme veya namespace eşleşmesinin bozulduğunu gösterir. Ağda gerçekten hangi XML'in taşındığını küçük ve anonim bir örnekle incelemek yararlıdır.
Bulunamayan kayıt ile erişilemeyen servis aynı şey değil
GetItem için bulunamayan kimlik, iş kuralı reddi ve iletişim hatası ayrı sonuçlardır. Boş ItemReply döndürerek hepsini birleştirmek istemcinin karar vermesini zorlaştırır. Beklenen servis hataları gerekiyorsa FaultContract ile tiplenebilir; timeout veya bağlantı kopması ise taşıma katmanının başarısızlığıdır. İç exception metnini veya SQL ayrıntısını fault içeriğine gelişigüzel koymam.
Kimlik doğrulaması da sözleşme özniteliklerinden kendiliğinden oluşmaz. Endpoint koruması, taşıma güvenliği ve operasyon yetkisi gerçek host üzerinde yapılandırılmalı. Bir kullanıcının servise bağlanabilmesi her kimliğe ait veriyi okuyabileceği anlamına gelmez.
Entegrasyon kontrolünü dar bir sözleşmeyle yapmak
Önce geçerli kimliğin iki alanlı yanıtını doğrularım. Ardından bulunamayan kayıt, süresi aşan çağrı ve eski istemci senaryolarını denerim. Bu örnek sözleşme biçimini anlatır; çalıştırılmış bir kurum entegrasyonu veya üretim SLA ölçümü iddiası taşımaz. Küçük sözleşmenin faydası, bu testlerin her birinde hangi katmanın sorumlu olduğunu daha net görmektir.