• DinnerNow中的WCF应用 首页数据加载


      继上一篇(初尝dinnernow)之后,通过配置并驱动起了web应用。从今天起本系列文章将以一个购物流程为主线,
    介绍一下DinnerNow是如何使用WCF,LINQ,ASP.NET Ajax Extensions等技术来架构应用的。

      首先请用VS2008打开下面两个解决方案:
         安装目录下\solution\DinnerNow - Web\DinnerNow - Web.sln
                      \solution\DinnerNow - ServicePortfolio2\DinnerNow - ServicePortfolio2.sln

         这是关于DinnerNow - Web.sln中项目的说明:
    DinnerNow.WebUX 项目包括表示层(UI)的应用逻辑,WCF客户端调用的CS文件(CODE文件夹下)
         DinnerNow.Web 项目则提供了一些简单的变量声明和定义,相关的CS代码并不多.
      Microsoft.DPE.Samples.CardSpace 是一些关于Card Space数据访问和操作的封装和实例代码.


         因此目前网站上的主要代码和功能实现都集中在了DinnerNow.WebUX这个项目.

      为了完整的演示一个购买流程,本人将会以执行页面为单位.逐个说明相关页面的程序执行逻辑和功能实现.

      在介绍之前,请大家先看一下DinnerNow的系统架构图.相信这会对我们从整体上把握这个产品提供一个切入点.相关图示如下:

       
         

         首先运行网站的首页http://localhost/dinnernow/default.aspx,如下图:
     

      上图中红框标记部分的部分页面页容如下(SearchBar.ascx):
                          
     

    <table border="0" cellspacing="2" cellpadding="2">
        
    <tr>
            
    <td align="right" nowrap="nowrap" class="boldWhite">Food Type </td>
            
    <td align="left">
                
    <asp:ObjectDataSource ID="RestaurantCategoryDataSource" runat="server" SelectMethod="SelectAll" TypeName="DinnerNow.RestaurantCategoryDataSource"/>
                
    <asp:DropDownList ID="restaurantCategoryList" runat="server" 
                    DataSourceID
    ="RestaurantCategoryDataSource" DataTextField="Description" 
                    DataValueField
    ="RestaurantId"/>                        
            
    </td>
        
    </tr>
    </table>

    <table border="0" cellspacing="2" cellpadding="2">
        
    <tr>
            
    <td align="right" class="boldWhite">Meal</td>
            
    <td align="left">
                
    <asp:ObjectDataSource ID="MenuTypeDataSource" runat="server" SelectMethod="SelectAll" TypeName="DinnerNow.MenuTypeDataSource"/>
                
    <asp:DropDownList ID="menuTypeList" runat="server" 
                    DataSourceID
    ="MenuTypeDataSource" DataTextField="MenuTypeName" 
                    DataValueField
    ="MenuTypeName" />                        
            
    </td>
        
    </tr>
    </table>


         可以看出菜单下拉框选项使用ObjectDataSource方式进行加载,而页面代码中的下列两条语句是所加载类型的说明:
         TypeName="DinnerNow.RestaurantCategoryDataSource'
         TypeName="DinnerNow.MenuTypeDataSource"

       这两个类型我们可以在下列路径下找到:
         DinnerNow.WebUX\Code\DataSources\RestaurantCategoryDataSource.cs
         DinnerNow.WebUX\Code\DataSources\MenuTypeDataSource.cs

         它们两个的功能就是调用相应的SelectAll方法如下(仅以MenuTypeDataSource.cs为例):

         MenuTypeDataSource.cs

        

    public IEnumerable<RestaurantCategory> SelectAll()
    {
        
    try
        
    {
            
    using (MenuSearchServiceClient client = new MenuSearchServiceClient("WSHttpBinding_IMenuSearchService"))
            
    {
                
    return client.GetRestaurantCategories();
            }

        }

        
    catch (Exception)
        
    {
           
    //@TODO: Need to put some error handling in here
        }

        
    return null;
    }


          因为代码太简单没什么可说的,下面就根据其所请求的服务绑定项"WSHttpBinding_IMenuSearchService", 在web.config
    中查找到如下配置节:
        

    <endpoint address="http://localhost/DinnerNow/service/MenuSearch.svc" binding="wsHttpBinding"
      bindingConfiguration
    ="WSHttpBinding_IMenuSearchService" contract="MenuSearchService.IMenuSearchService"
      name
    ="WSHttpBinding_IMenuSearchService">
      
    <identity>
        
    <servicePrincipalName value="host" />
      
    </identity>
    </endpoint> 

         而相关的MenuSearch.svc(执行)文件就是其所引用的服务地址.好的,看清了这一块之后,我们切换到刚才所说的第二
    个解决方案中(DinnerNow - ServicePortfolio2.sln),看一下这个SVC中是如何执行相应逻辑的:)

        在DinnerNow - ServicePortfolio2.sln中的DinnerNow.ServiceHost项目是服务配置站点,我们可从该站点的web.config
    文件中找出如下内容:
         ...... 

    <service behaviorConfiguration="DinnerNow.Services.MenuSearchServiceBehavior"
       name
    ="DinnerNow.Services.MenuSearchService">
       
    <endpoint address="" binding="wsHttpBinding" contract="DinnerNow.Services.IMenuSearchService" />
       
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
       
    <endpoint address="ajax" behaviorConfiguration="DinnerNow.Services.MenuSearchServiceAjax" 
       binding
    ="webHttpBinding" bindingConfiguration="AjaxBinding" 
       contract
    ="DinnerNow.Services.IMenuSearchService" />
    </service>

         ......

         这里定义了当前服务所使用的contract接口(MenuSearchService)以及所使用的服务MenuSearchService(业务逻辑),
    而有关这两部分内容定义如下:

       

    [ServiceContract(Namespace = "DinnerNow.Services")]
    public interface IMenuSearchService
    {
        [OperationContract]
        [WebGet]
        IEnumerable
    <MenuType> GetMenuTypes();

        [OperationContract]
        [WebGet]
        IEnumerable
    <RestaurantCategory> GetRestaurantCategories();

        [OperationContract]
        [WebGet]
        IEnumerable
    <RestaurantHeader> FindRestaurant(string postalCode, string menuType, string restaurantCategoryId, string deadline);

        [OperationContract]
        [WebGet]
        IEnumerable
    <RestaurantMenuItem> GetMenuItemsForMenu(string restaurantId, string menuType);
    }


        该接口定义了搜索菜单的数据获取方法,相信大家通过字面就能看出个大概了,所以我就不多说什么了.
        下面主要说一下MenuSearchService.cs文件(DinnerNow.Services项目下):

       

    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class MenuSearchService : IMenuSearchService
    {
        
    IMenuSearchService Members
    }

       因为我们在网站客户端调用的是如下方法:    

    using (MenuSearchServiceClient client = new MenuSearchServiceClient("WSHttpBinding_IMenuSearchService"))
    {
        
    return client.GetMenuTypes();
    }

        所以对这个方法的使用应该就是对菜单类型数据的加载,而相应的方法定义在DinnerNow.Business\Menu.cs文件中:       

    public IEnumerable<DinnerNow.Business.Data.MenuType> GetMenuTypes()
    {
        var s 
    = (from m in db.Menus
                select 
    new DinnerNow.Business.Data.MenuType()
                
    {
                    MenuTypeName 
    = m.MenuType.Trim() 
                }
    ).Distinct();
        
    return s.ToList();
    }

      这里使用了linq to sql来执行数据的操作。可以这么说, DinnerNow的数据访问和操作基本上都是使用LINQ
    语法完成的.

         它的作用相当于如下语句(即找出不重复的菜单类型):

      SELECT DISTINCT [t1].[value] AS [MenuTypeName] FROM (SELECT LTRIM(RTRIM([t0].[MenuType])) AS [value]
       FROM [dbo].[Menu] AS [t0]) AS [t1]
       

         这样,对首页的整个数据加载过程就完成了,当然页面上的数据查询操作又是如何进行的呢?

         下面就来说明一下这方面的业务执行流程:

        请再切换回DinnerNow - Web.sln解决方案,还是刚才的那个SearchBar.ascx页面,下面的代码即是完成了搜索提交
    以及查询操作(详情见注释):

    <asp:ScriptManagerProxy ID="ScriptManagerProxy1" runat="server">
        
    <services>
            
    <asp:ServiceReference Path="~/service/MenuSearch.svc/ajax" />
        
    </services>
    </asp:ScriptManagerProxy>

       

    <script type="text/javascript">
    function searchButton_Click()
    {   
        var DeadLine 
    = $get("<%= deadlineSelect.ClientID %>").value;
        
    if (DeadLine=="-1" || DeadLine==null)
        {
            DeadLine
    ="90";
        }
        
        var MenuType 
    = $get("<%= menuTypeList.ClientID %>").value.trim();
        var PostalCode 
    = $get("<%= postalCodeTextBox.ClientID %>").value;
        var RestaurantCategory 
    = $get("<%= restaurantCategoryList.ClientID %>").value;
        
        var searchUrl 
    = "search.aspx";
        var path 
    = document.location.pathname.toLowerCase();
        var isInSearchAspx 
    = path.length>=searchUrl.length && path.substr(path.length - searchUrl.length,searchUrl.length) == searchUrl;

        
    if (!isInSearchAspx) //当前页面是否为搜索页(search.aspx)
        {
            var href 
    = "search.aspx?PostalCode="+PostalCode+"&MenuType="+MenuType+"&RestaurantCategory="+RestaurantCategory+"&DeadLine="+DeadLine;
            document.location.href 
    = href;     //当不在搜索页面则将查询参数绑定后跳转到搜索页面   
        }
        
    else
        {
            var service 
    = new DinnerNow.Services.IMenuSearchService();
    //如果在搜索页面,则调用下面的JS方法来查找相当的记录            
            service.FindRestaurant(PostalCode, MenuType, RestaurantCategory, DeadLine, restaurantSearch_onSuccess, restaurantSearch_onFailed, null);
        }
        
    return false;
    }                    
    function restaurantSearch_onSuccess(result) 
    //查询成功
    {
        
    if (typeof(onRestaurantSeachSuccess)!="#ff0000")
        {
            onRestaurantSeachSuccess(result);
        }
    }
    function restaurantSearch_onFailed(result) 
    //查询失败
    {
        alert(
    "The search has failed");
    }



    </script>

         因为使用了ASP.NET Ajax Extensions,所以上面的代码段里的service.FindRestaurant(PostalCode, MenuType, RestaurantCategory,
     DeadLine, restaurantSearch_onSuccess, restaurantSearch_onFailed, null);
         写法很接近于我们习惯的C#。

         而实际的JS方法如下:

     

    FindRestaurant:function(postalCode,menuType,restaurantCategoryId,deadline,succeededCallback, failedCallback, userContext) {
    /// <param name="postalCode" type="String">System.String</param>
    /// <param name="menuType" type="String">System.String</param>
    /// <param name="restaurantCategoryId" type="String">System.String</param>
    /// <param name="deadline" type="String">System.String</param>
    /// <param name="succeededCallback" type="Function" optional="true" mayBeNull="true"></param>
    /// <param name="failedCallback" type="Function" optional="true" mayBeNull="true"></param>
    /// <param name="userContext" optional="true" mayBeNull="true"></param>
    return this._invoke(this._get_path(), 'FindRestaurant',true,{postalCode:postalCode,menuType:menuType,restaurantCategoryId:restaurantCategoryId,deadline:deadline},succeededCallback,failedCallback,userContext); }

      

         上面代码中的_invoke就是完成一个ajax请求的方法.而succeededCallback和succeededCallback方法分别是ajax成功或失败
    后的回调函数参数,也是本例中的方法restaurantSearch_onSuccess,restaurantSearch_onFailed.

        而最终ajax请求会成为对如下方法的调用(DinnerNow.Business\Menu.cs文件中):

        

    public IEnumerable<DinnerNow.Business.Data.RestaurantHeader> FindRestaurant(string postalCode, string menuType, Guid restaurantCategoryId, int deadline)
    {
        var results 
    = from r in db.Restaurants
                      join m 
    in db.Menus on r.RestaurantId equals m.RestaurantId
                      
    where m.MenuType == menuType
                      
    && r.PostalCode == postalCode
                      
    && r.RestaurantCategoryId == restaurantCategoryId
                      select 
    new Business.Data.RestaurantHeader()
                      {
                          LogoImageLocation 
    = r.LogoImageLocation,
                          Name 
    = r.Name,
                          RestaurantId 
    = r.RestaurantId
                      };

        
    return results.ToList();
    }

         这个LINQ语句相当于如下SQL语句(Restaurant,RestaurantId联表查询):
    SELECT [t0].[RestaurantId], [t0].[Name], [t0].[LogoImageLocation] FROM [dbo].[Restaurant] AS [t0]
    INNER JOIN [dbo].[Menu] AS [t1] ON [t0].[RestaurantId] = [t1].[RestaurantId]
    WHERE ([t1].[MenuType] = @p0) AND ([t0].[PostalCode] = @p1) AND ([t0].[RestaurantCategoryId] = @p2)

      
         在搜索这个地方使用了AJAX,主要是为了UE(用户体验).当然在DinnerNow中还有一些地方如选餐, 支付等也使用
    了AJAX,相信也是出于这方面的考虑)

         说到了这里,今天的内容就要告一段落了.纵观DinnerNow的架构,可以说访问数据库的操作基本上都以LINQ To
    Sql实现方式。而业务流程(服务)则采用WCF的方式进行封装和调用.而网站上只保留了显示逻辑及AJAX请求操作.这
    样可以说做到了将数据访问层与业务逻辑层的分离.同时也便于团队开发并进行相应分工。

         因为本人认为可以将开发小组成员分为三组:

      A组负责数据访问接口和相关数据操作(采用LINQ)
    B组负责设计业务流程组织(采用WCF, 后面的购买流程中使用了WWF,将会在下文中详加说明)
         C组负责前台程序逻辑设计包括ajax调用等等

      当然这种分工的好处是让小组成员的长处都能得到发挥,必定有专攻数据操作访问,也有专攻SOA的.有专功LINQ,
    也有熟练WCF和WF的。当然这只是我的一面之词,目前也只是猜测,如果大家有什么意见,欢迎在回复中进行讨论:)

  • 相关阅读:
    HttpModule
    phpcms(1)
    ajax,json
    ajax 参数 小记
    PHP中文件操作基础:目录操作,文件操作
    PHP,获取文件夹下所有文件数量的方法。
    PHP中文件操作基础:文件路径基础
    js,jquery基础使用方法
    PHP基础知识测试题
    PHP中单例模式与工厂模式,
  • 原文地址:https://www.cnblogs.com/daizhj/p/1202600.html
Copyright © 2020-2023  润新知