關於已部署的WCF服務升級的問題


在日常的開發過程中,我們會經常迭代發布不同的版本,所以WCF服務的接口也會經常處於變動的狀態,比如在傳遞實體類中新加一個字段、修改參數名稱等等關於服務升級的問題。但是我們不可能讓已發布的版本重新引用新的服務,這是不現實的,所以我們在升級WCF服務時,一定要讓服務兼容以前的版本。現在我們分別介紹關於服務升級的幾個常用情況。

一、參數變動

我們來實現最初的版本1.0,新建一個服務接口,服務實現很簡單,在Output窗口中簡單輸出服務接收到的參數值。

    [ServiceContract]
    public interface ITestingService
    {
        [OperationContract]
        void AddUser(string id, string username, int age);
    }
    public class TestingService : ITestingService
    {
        public void AddUser(string id, string username, int age)
        {
            System.Diagnostics.Debug.WriteLine(string.Format("id = {0}", id));

            System.Diagnostics.Debug.WriteLine(string.Format("username = {0}", username));

            System.Diagnostics.Debug.WriteLine(string.Format("age = {0}", age));
        }
    }

在客戶端引用1.0版本的服務,調用服務接口

    class Program
    {
        static void Main(string[] args)
        {
            var testingServiceClient = new TestingServiceClient();

            testingServiceClient.AddUser("001", "James", 18);

            Console.ReadKey();
        }
    }

在服務端的Output輸出結果如下

1.添加參數

考慮一種特別常用的情形,我需要添加一個新的字段,以便得到更多的信息,我們需要升級服務至2.0,所以我們需要修改接口和實現。最終的結果當然是不影響引用1.0服務的客戶端的繼續使用。

讓我們修改接口和實現。

    [ServiceContract]
    public interface ITestingService
    {
        [OperationContract]
        void AddUser(string id, string username, int age, string city);
    }
    public class TestingService : ITestingService
    {
        public void AddUser(string id, string username, int age, string city)
        {
            System.Diagnostics.Debug.WriteLine(string.Format("id = {0}", id));

            System.Diagnostics.Debug.WriteLine(string.Format("username = {0}", username));

            System.Diagnostics.Debug.WriteLine(string.Format("age = {0}", age));

            System.Diagnostics.Debug.WriteLine(string.Format("city = {0}", city));
        }
    }

首先我們需要保證引用1.0服務的客戶端的繼續使用,所以我們先測試引用1.0服務的客戶端。

從測試結果可以看到,在引用舊的服務的客戶端在調用新的服務時,可以正常調用,只是新添加的字段是默認值。這是正確的結果,因為在舊的客戶端傳遞過來的數據中不包含新添加的字段的信息,自然新添加的字段的值是默認的值。

2.刪除不再需要的參數

緊接1.0版本,假設在新的2.0版本中不再需要age的值,所以需要在接口中刪除這個參數,所以需要修改接口和實現。

   [ServiceContract]
    public interface ITestingService
    {
        [OperationContract]
        void AddUser(string id, string username);
    }
    public class TestingService : ITestingService
    {
        public void AddUser(string id, string username)
        {
            System.Diagnostics.Debug.WriteLine(string.Format("id = {0}", id));

            System.Diagnostics.Debug.WriteLine(string.Format("username = {0}", username));
        }
    }

首先我們需要保證引用1.0服務的客戶端的繼續使用,所以我們先測試引用1.0服務的客戶端。

從測試結果可以看到,在引用舊的服務的客戶端在調用新的服務時,可以正常調用。

3.修改參數名(重構)

緊接1.0版本,假設在新的2.0版本中需要修改username名為name,所以需要修改接口和實現。

   [ServiceContract]
    public interface ITestingService
    {
        [OperationContract]
        void AddUser(string id, string name);
    }
    public class TestingService : ITestingService
    {
        public void AddUser(string id, string name)
        {
            System.Diagnostics.Debug.WriteLine(string.Format("id = {0}", id));

            System.Diagnostics.Debug.WriteLine(string.Format("username = {0}", name));
        }
    }

在保證客戶端不引用新的服務的前提下,我們測試客戶端的服務調用情況。

從結果可以看到,如果修改了參數的名稱則會影響到舊版本客戶端的使用,難道沒有別的什么方法可以解決這個問題么?答案是有的,如果想要重構但是不想改動客戶端代碼的話,那么可以給參數加上一個MessageParameter的屬性,代碼如下:

    [ServiceContract]
    public interface ITestingService
    {
        [OperationContract]
        void AddUser(string id, [MessageParameter(Name = "username")]string name);
    }

重新測試舊版本的客戶端,可以看到最終的結果又恢復正常。

 

二、實體的變動

在第一部分我們談論的是參數的變動,在第二部分我們變化實體中屬性的變動。先來定義最初的1.0服務版本。

    [DataContract]
    public class User
    {
        [DataMember]
        public string Id
        {
            get;
            set;
        }

        [DataMember]
        public string Name
        {
            get;
            set;
        }

        [DataMember]
        public int Age
        {
            get;
            set;
        }

    }
   [ServiceContract]
    public interface ITestingService
    {
        [OperationContract]
        void AddUser(User user);
    }
    public class TestingService : ITestingService
    {
        public void AddUser(User user)
        {
            System.Diagnostics.Debug.WriteLine(string.Format("id = {0}", user.Id));

            System.Diagnostics.Debug.WriteLine(string.Format("username = {0}", user.Name));

            System.Diagnostics.Debug.WriteLine(string.Format("age = {0}", user.Age));
        }
    }

客戶端引用最初的服務

    class Program
    {
        static void Main(string[] args)
        {
            var testingServiceClient = new TestingServiceClient();

            var user = new User() { Id = "001", Name = "Tommy", Age = 25};

            testingServiceClient.AddUser(user);

            Console.ReadKey();
        }
    }

測試結果如下

1.在實體類中添加屬性

在User類中添加City屬性,以便在新的服務版本中獲取更多的用戶信息。服務接口不變,只需修改實體類和服務實現。

    [DataContract]
    public class User
    {
        [DataMember]
        public string Id { get; set; }

        [DataMember]
        public string Name { get; set; }

        [DataMember]
        public int Age { get; set; }

        [DataMember]
        public string City { get; set; }

    }
        public void AddUser(User user)
        {
            System.Diagnostics.Debug.WriteLine(string.Format("id = {0}", user.Id));

            System.Diagnostics.Debug.WriteLine(string.Format("username = {0}", user.Name));

            System.Diagnostics.Debug.WriteLine(string.Format("age = {0}", user.Age));

            System.Diagnostics.Debug.WriteLine(string.Format("city = {0}", user.City));
        }

在保證客戶端不引用新的服務的前提下,測試客戶端的服務調用情況。從結果可以看出,舊的客戶端可以正常調用新的服務,只是新添加的字段沒有顯式賦值。

2.在實體類中刪除屬性

在User類中刪除Age屬性。服務接口不變,只需修改實體類和服務實現。

    [DataContract]
    public class User
    {
        [DataMember]
        public string Id { get; set; }

        [DataMember]
        public string Name { get; set; }

    }
    public class TestingService : ITestingService
    {
        public void AddUser(User user)
        {
            System.Diagnostics.Debug.WriteLine(string.Format("id = {0}", user.Id));

            System.Diagnostics.Debug.WriteLine(string.Format("username = {0}", user.Name));

        }
    }

在保證客戶端不引用新的服務的前提下,測試客戶端的服務調用情況。從結果可以看出,舊的客戶端可以正常調用新的服務。

3.在實體類中修改屬性名

在User類中修改Name屬性的名稱為UserName。服務接口不變,只需修改實體類和服務實現。

    [DataContract]
    public class User
    {
        [DataMember]
        public string Id { get; set; }

        [DataMember]
        public string UserName { get; set; }

        [DataMember]
        public int Age { get; set; }

    }
    public class TestingService : ITestingService
    {
        public void AddUser(User user)
        {
            System.Diagnostics.Debug.WriteLine(string.Format("id = {0}", user.Id));

            System.Diagnostics.Debug.WriteLine(string.Format("username = {0}", user.UserName));

        }
    }

在保證客戶端不引用新的服務的前提下,測試客戶端的服務調用情況。

從結果可以看到,如果修改了實體屬性的名稱則會影響到舊版本客戶端的使用,可以在DataMemeber中設定Name屬性的值,代碼如下

  [DataMember(Name = "Name")]
  public string UserName { get; set; }

重新測試客戶端,發現可以繼續正常使用。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM