zoukankan      html  css  js  c++  java
  • WebService又一个不爽的地方

    昨天在做项目时,发现了WebService又一个不人性化的地方,记录于此,希望能帮到遇到类似问题的同学们。
    很多大型b/s项目,通常会分成几层,为了重现问题,这里我简化为三层:(以下代码仅出于演示,也许并无太大的实际用途)

    1、Model层
    放置一些业务需要的实体类(通常这些类要求是可序列化的,以方便后面提到的“服务层"中能被序列化后传递),这里为了演示,弄了三个类:
    1.1 Person类
    using System;
    
    namespace Model
    {
        [Serializable]
        public class Person
        {
            public Person() { }
    
            private int _Salary = 1280;//上海最低工资
    
            /// <summary>
            /// 收入
            /// </summary>
            public int Salary
            {
                get { return _Salary; }
                set { _Salary = value; }
            }
    
            private string _Name = "No Name";
    
            /// <summary>
            /// 姓名
            /// </summary>
            public string Name
            {
                get { return _Name; }
                set { _Name = value; }
            }
    
    
            private DateTime _Birthday = new DateTime(1900, 1, 1);
    
            /// <summary>
            /// 生日
            /// </summary>
            public DateTime Birthday
            {
                get { return _Birthday; }
                set { _Birthday = value; }
            }
    
    
            public override string ToString()
            {
                return string.Format("Name={0},Birthday={1},Salary={2}", this.Name, this.Birthday, this.Salary);
            }
        }
    }
    
    1.2、PersonQueryParameters类
    对于Person类的集合(比如DataTable,List<Person>),通常会有搜索需求,而且搜索的字段要求能动态变化,为了方便起见,把一些常用的搜索参数封装在这个类里
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace Model
    {
        [Serializable]
        public class PersonQueryParameters
        {
    
            public PersonQueryParameters() { }
    
            private int _Salary_Min = Consts.SalaryMin;
    
            /// <summary>
            /// 工资范围(最小值)
            /// </summary>
            public int Salary_Min
            {
                get { return _Salary_Min; }
                set { _Salary_Min = value; }
            }
    
    
    
            private int _Salary_Max = Consts.SalaryMax;
    
            /// <summary>
            /// 工资范围(最大值)
            /// </summary>
            public int Salary_Max
            {
                get { return _Salary_Max; }
                set { _Salary_Max = value; }
            }
    
    
            private DateTime _Birthday_Min = Consts.BirthdayMin;
    
            /// <summary>
            /// 生日范围(最小值)
            /// </summary>
            public DateTime Birthday_Min
            {
                get { return _Birthday_Min; }
                set { _Birthday_Min = value; }
            }
    
            private DateTime _Birthday_Max = Consts.BirthdayMax;
    
            /// <summary>
            /// 生日范围(最大值)
            /// </summary>
            public DateTime Birthday_Max
            {
                get { return _Birthday_Max; }
                set { _Birthday_Max = value; }
            }
    
    
    
            public override string ToString()
            {
                return string.Format("Salary_Min={0},Salary_Max={1},Birthday_Min={2},Birthday_Max={3}", this.Salary_Min, this.Salary_Max, this.Birthday_Min, this.Birthday_Max);
            }
    
        }
    }
    
    注:其中用了一个类Consts,下面马上会提到
    1.3、Consts类
    这个类只是为了方便,定义一些常量而已
    using System;
    
    namespace Model
    {
        [Serializable]
        public static class Consts
        {
            public static readonly DateTime BirthdayMin = new DateTime(1900, 1, 1);
    
            public static readonly DateTime BirthdayMax = new DateTime(2012, 12, 24);
    
            public static readonly int SalaryMin = 1280;
    
            public static readonly int SalaryMax = 1000000;
        }
    }
    
    
    2、WebService层
    其它子系统需要的业务功能,以服务的形式在这一层对外公开。我们创建一个Query.asmx来提供“查询Person”的服务。(注:当然,这一层必须引用Model层)
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web.Services;
    using Model;
    
    namespace WebService
    {
        /// <summary>
        /// Summary description for Query
        /// </summary>
        [WebService(Namespace = "http://yjmyzz.cnblogs.com/")]
        [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
        [System.ComponentModel.ToolboxItem(false)]
       
        public class Query : System.Web.Services.WebService
        {
    
            [WebMethod(Description="查询Person信息")]
            public List<Person> QueryPerson(PersonQueryParameters pars)
            {
                //这里出于演示目的,直接构造一个List<T>做为搜索源
                List<Person> lstSrc = new List<Person>(){
                    new Person(){ Birthday=DateTime.Parse("1980-3-5"), Name="张三", Salary=5000},
                    new Person(){ Birthday=DateTime.Parse("1985-6-1"), Name="李四", Salary=10000},
                    new Person(){ Birthday=DateTime.Parse("1975-12-8"), Name="王五", Salary=8000},
                };
    
                IEnumerable<Person> result = lstSrc.AsEnumerable();
    
                #region 处理搜索
                if (pars.Birthday_Min != Consts.BirthdayMin) 
                {
                    result = result.Where(c => c.Birthday >= pars.Birthday_Min);
                }
    
                if (pars.Birthday_Max != Consts.BirthdayMax)             
                {
                    result = result.Where(c => c.Birthday <= pars.Birthday_Max);
                }
    
                if (pars.Salary_Min != Consts.SalaryMin) 
                {
                    result = result.Where(c => c.Salary >= pars.Salary_Min);
                }
    
                if (pars.Salary_Max != Consts.SalaryMax) 
                {
                    result = result.Where(c => c.Salary <= pars.Salary_Max);
                }
                #endregion
    
                return result.ToList();
            }
        }
    }
    

     3、Website (UI) 层
    这一层只负责UI呈现,业务功能通过请求WebService层实现。添加对WebService层Query.asmx的服务引用后,我们创建一个Default.aspx页来测试一下QueryPerson服务
    using System;
    using Website_ASMX_No_Ref_Model.WSLayer;
    using Website_ASMX_No_Ref_Model.Properties;
    
    namespace Website_ASMX_No_Ref_Model
    {
        public partial class Default : System.Web.UI.Page
        {
            protected void Page_Load(object sender, EventArgs e)
            {
                using (Query service = new Query())
                {
                    //动态设置webService的asmx路径
                    service.Url = Settings.Default.Website_ASMX_No_Ref_Model_WSLayer_Query;
    
                    PersonQueryParameters pars = new PersonQueryParameters();
    
                    //查询工资在3-5k之间的人
                    pars.Salary_Min = 3000;
                    pars.Salary_Max = 5000;
    
                    //同时出生时间在1979-1-1以后的人
                    pars.Birthday_Min = DateTime.Parse("1979-1-1");
    
                    Person[] result = service.QueryPerson(pars);
                } 
                
            }
        }
    }
    
    啰嗦了一堆,总算把背景交待完了,现在问题才刚开始,从UI层的代码来看,貌似一切都很完美,Model层的各种实体类定义,在UI层引用asmx服务后,被自动带到UI层了。如果我们在上面代码的"PersonQueryParameters"上右击,转到定义,会看到vs.net自动为我们生成的代码:
        /// <remarks/>
        [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.225")]
        [System.SerializableAttribute()]
        [System.Diagnostics.DebuggerStepThroughAttribute()]
        [System.ComponentModel.DesignerCategoryAttribute("code")]
        [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://yjmyzz.cnblogs.com/")]
        public partial class PersonQueryParameters {
            
            private int salary_MinField;
            
            private int salary_MaxField;
            
            private System.DateTime birthday_MinField;
            
            private System.DateTime birthday_MaxField;
            
            /// <remarks/>
            public int Salary_Min {
                get {
                    return this.salary_MinField;
                }
                set {
                    this.salary_MinField = value;
                }
            }
            
            /// <remarks/>
            public int Salary_Max {
                get {
                    return this.salary_MaxField;
                }
                set {
                    this.salary_MaxField = value;
                }
            }
            
            /// <remarks/>
            public System.DateTime Birthday_Min {
                get {
                    return this.birthday_MinField;
                }
                set {
                    this.birthday_MinField = value;
                }
            }
            
            /// <remarks/>
            public System.DateTime Birthday_Max {
                get {
                    return this.birthday_MaxField;
                }
                set {
                    this.birthday_MaxField = value;
                }
            }
        }
    
    好吧,如果你已经迫不及待的想按F5运行一下,会发现UI层的代码始终是搜索不到任何Person记录的。问题在于:Website中的PersonQueryParameters类,已经不是Model层中的PersonQueryParameters了!(哪怕这哥俩"类名称"以及"类属性成员的名字"都完全相同)观察Model层中的PersonQueryParameters定义与Website中vs.net自动为我们生成的PersonQueryParameters定义,会发现:原来Model层中私有成员赋初始值的代码,比如
    private int _Salary_Min = Consts.SalaryMin;
    
    已经变成了
    private int salary_MinField;
    
    换句话说,属性的初始赋值丢失了!(或者说被改变了) 因此WebService中搜索部分的 if 判断语句
    if (pars.Birthday_Max != Consts.BirthdayMax) 
    
    应该变成
    if (pars.Birthday_Max!=default(DateTime))
    
    这是一个比较隐藏的问题,编译期不会出现任何问题,运行时也不会报错,只能在运行时,通过调试断点才能发现。
    知道了问题所在,解决办法就有了:
    方法1
    model层对于“搜索参数实体类”不要给私有成员赋任何初始值。这样后面写webservice层的人,也自然不会想到用if (pars.xxx == Consts.xxx)来判断了
    方法2
    如果model层的代码不允许修改,也可以修改webservice中的if语句代码,考虑到兼容性,以前类似
    if (pars.Birthday_Max!=Consts.BirthdayMax)
    
    这种代码,应该变成
    if (!(pars.Birthday_Max == Consts.BirthdayMax || pars.Birthday_Max==default(DateTime)))
    

    继续唠叨:vs.net这种“自动重复生成asmx中业务实体类定义”代码的行为,即使是UI层添加了Model层项目引用后,依然如此。但是在后续测试中发现,如果把asmx换成用wcf(.svc)来实现,在UI层添加了Model引用后,vs.net不会再重复生成相应的类定义。

    有图有真相:

    文中所述问题示例源代码:https://files.cnblogs.com/yjmyzz/website_test.7z

    其实WebService还有其它不爽的地方,见webservice今日遇到的二个问题:DataTable + Namespace

    "青山遮不住,毕竟东流去",正如IE6会被其它浏览器取代一样,asmx技术也会慢慢淡出历史舞台,建议大家对于新项目,大胆的用wcf来代替asmx吧,我会在下一篇博文中,写一个"wcf10分钟速成",帮助对于从没接触过wcf的asmx迷们,消除对wcf的恐惧,快速上手wcf.

    作者:菩提树下的杨过
    出处:http://yjmyzz.cnblogs.com
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    Asp.net mvc项目分页功能
    asp.net mvc邮箱激活
    4. Spring 如何通过 XML 文件配置Bean,以及如何获取Bean
    3. Spring 核心之 IOC(控制反转) & DI(依赖注入)
    2. Spring 的 HelloWorld
    1. Spring 简介以及关于 Eclipse 的 Spring Tool Suite 插件安装
    早睡早起身体好,早睡早起有力量,晚安~~
    睡前写几句,缓解一下刷题的心情。。。
    ACM 杭电HDU 2084 数塔 [解题报告]
    ACM HDU 2041--超级楼梯题解
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/2043974.html
Copyright © 2011-2022 走看看