程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> DinnerNow中的WCF應用 - 首頁數據加載

DinnerNow中的WCF應用 - 首頁數據加載

編輯:關於.NET

繼上一篇(初嘗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#region IMenuSearchService Members
  public IEnumerable<Business.Data.MenuType> GetMenuTypes()
  {
    Business.Menu menu = new DinnerNow.Business.Menu();
    return menu.GetMenuTypes();
  }
  public IEnumerable<Business.Data.RestaurantCategory> GetRestaurantCategories()
  {
    Business.Menu menu = new DinnerNow.Business.Menu();
    return menu.GetRestaurantCategories();
  }
  public IEnumerable<DinnerNow.Business.Data.RestaurantHeader> FindRestaurant(string postalCode,
      string menuType, string restaurantCategoryId, string deadline)
  {
    Business.Menu menu = new DinnerNow.Business.Menu();
    Trace.Write("");
    return menu.FindRestaurant(postalCode, menuType, new Guid(restaurantCategoryId),
        int.Parse(deadline,CultureInfo.CurrentCulture));
  }
  public IEnumerable<DinnerNow.Business.Data.RestaurantMenuItem> GetMenuItemsForMenu(string restaurantId, string menuType)
  {
    Business.Menu menu = new DinnerNow.Business.Menu();
    return menu.GetMenuItemsForMenu(new Guid(restaurantId), menuType);
  }
  #endregion
}

因為我們在網站客戶端調用的是如下方法:

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 ToSql實現方式。而業務流程(服務)則采用WCF的方式進行封裝和調用.而網站上只保留了顯示邏輯及AJAX請求操作.這樣可以說做到了將數據訪問層與業務邏輯層的分離.同時也便於團隊開發並進行相應分工。

因為本人認為可以將開發小組成員分為三組:

A組負責數據訪問接口和相關數據操作(采用LINQ)B組負責設計業務流程組織(采用WCF, 後面的購買流程中使用了WWF,將會在下文中詳加說明)C組負責前台程序邏輯設計包括ajax調用等等當然這種分工的好處是讓小組成員的長處都能得到發揮,必定有專攻數據操作訪問,也有專攻SOA的.有專功LINQ,也有熟練WCF和WF的。當然這只是我的一面之詞,目前也只是猜測,如果大家有什麼意見,歡迎在回復中進行討論:)

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved