我们大概了解了一些关于DinnerNow的基本项目结构,以及其中比较主要的两个解决方案文件。接下来我会继续以实际网上选餐流程来说明关于DinnerNow中的ASP.NET Ajax Extensions应用场景及其设计方案。
首先请大家看一下这张图,它标明了在订餐这一业务流程中"查询餐馆"这一用例 DinnerNow所实际执行的方法顺序,因为下文中的一些主要的js方法调用也是以这张图中所标明的流程来顺序处理的.
当然我们还要再次用VS2008打开上文中所说的两个解决方案文件:
安装目录下\solution\DinnerNow - Web\DinnerNow - Web.sln
\solution\DinnerNow - ServicePortfolio2\DinnerNow - ServicePortfolio2.sln
不过这一回要说的重点内容集中在了DinnerNow - Web.sln下的DinnerNow.WebUX项目中.
请看一下search.aspx页面的运行效果图:
上图中的数据请求在上一篇文章中已说过,就是:
service.FindRestaurant(PARAMETERS.map.PostalCode,
PARAMETERS.map.MenuType,
PARAMETERS.map.RestaurantCategory,
PARAMETERS.map.DeadLine,
onRestaurantSeachSuccess, //当操作请求成功后的回调方法
onRestaurantSeachFailed, null);
上面的回调方法的内容如下:
{
var restaurantContainer = document.getElementById( " restaurantList " );
restaurantContainer.innerHTML = "" ;
for (var i = 0 ; i < searchResult.length; i ++ ) // 绑定数据并进行显示
{
var restaurantHtml = " <a href=\ " javascript:restaurantSelection_Click( ' "
+ searchResult[i].RestaurantId + " ', ' "
+ searchResult[i].LogoImageLocation + " ', ' "
+ searchResult[i].Name + " ');\ " >< img src = \ ""
+ searchResult[i].LogoImageLocation + " \ " alt = \ ""
+ searchResult[i].Name + " \ " width = \ " 154\ " height = \ " 90\ " class = \ " thingreenline\ " /></ a > " ;
var restaurantElement = document.createElement( " span " );
restaurantElement.innerHTML = restaurantHtml;
restaurantContainer.appendChild(restaurantElement);
}
DisplayDiv( " SearchResultsDivision " );
}
通过这个方法的调用实现了上面图中的显示效果,当我们单击了其中某个餐馆的图标之后.
会显示下面的页面:
而单击事件的执行方法如下:
{
/* ********* RestaurantSelected ********** */ 生成餐馆的信息,如LOGO,餐馆名称,说明等
document.getElementById( " restaurantImage " ).src = logo;
document.getElementById( " restaurantName " ).innerHTML = name;
document.getElementById( " restaurantDescription " ).innerHTML = " Since 1923, the offering fas, friendly and courteous service. We use only the best ingredients and maintain a skilled staff to answer your questions. We have built our reputation on our commitment to providing quality service, which has earned us many valuable customers. " ;
document.getElementById( " restaurantMenuFeed " ).href = " service/syndication.svc/rss/restaurants/ " + name;
var restaurant = document.getElementById( " restaurantID " );
restaurant.innerHTML = identifier;
/* ********* RestaurantSelected ********** */
var service = new DinnerNow.Services.IMenuSearchService(); // 加载菜单列表
var menuType = return_MenuType();
var selectedMenuType = document.getElementById( " selectedMenuItemCategory " );
selectedMenuType.value = menuType;
service.GetMenuItemsForMenu(identifier,menuType,restaurantSelection_onSuccess,restaurantSelection_onFailed, null ); // 请求并加载菜单列表
service.GetMenuTypes(getMenuTypes_onSuccess, getMenuTypes_onFailed, null ); // 加载菜单类型(上图中的属性页:Breakfast,Dinner,Lunch)
}
其中的GetMenuItemsForMenu,GetMenuTypes方法最终会去调用MenuSearchService类中的同名方法(MenuSearchService.cs文件在DinnerNow.ServicesDinnerNow - ServicePortfolio2.sln解决方案),所以这里我们还要再切换到ServicePortfolio2.sln下,找到位于DinnerNow.Services项目下的MenuSearchService.cs文件。其中的GetMenuItemsForMenu方法定义如下:
{
Business.Menu menu = new DinnerNow.Business.Menu();
return menu.GetMenuItemsForMenu( new Guid(restaurantId), menuType); // 获取指定类型的菜单数据
}
代码段中的menu.GetMenuItemsForMenu方法定义如:
{
var results = from mi in db.MenuItems
join m in db.Menus on mi.MenuId equals m.MenuId
where m.RestaurantId == restaurantId
&& m.MenuType == menuType
select new Business.Data.RestaurantMenuItem()
{
Description = mi.Description,
ImageLocation = mi.ImageLocation,
MenuId = mi.MenuId,
MenuItemId = mi.MenuItemId,
Name = mi.Name,
PreparationTime = mi.PreparationTime,
Price = mi.Price
};
return results.ToList();
}
上面的LINQ查询相当于下面的SQL脚本:
FROM [ dbo ] . [ MenuItem ] AS [ t0 ]
INNER JOIN [ dbo ] . [ Menu ] AS [ t1 ] ON [ t0 ] . [ MenuId ] = [ t1 ] . [ MenuId ]
WHERE ( [ t1 ] . [ RestaurantId ] = @p0 ) AND ( [ t1 ] . [ MenuType ] = @p1 )
而前面所说的GetMenuTypes方法大家也可以找到它最终要去访问的LINQ代码如下:
{
var s = (from m in db.Menus
select new DinnerNow.Business.Data.MenuType()
{
MenuTypeName = m.MenuType.Trim()
}).Distinct();
return s.ToList();
}
这里因为代码很简单,就不多说了.
通过这个业务流程可以看出DinnerNow基本架构思想:
ajax 请求数据 ---> wcf 服务配置 ---> linq 数据访问.
这样架构让整个软件的架构,流程及开发层次非常清楚。另外因为使用了 Ajax Extensions,使得开发和阅读JS代码感觉就像是在写C#代码,使得软件的可读性和可维护性上也有很好提升和扩展空间.另外就是在UE上也使在我们可以在一个页面上完成挑选餐馆,选择食物并进行订餐的整个流程(接下来将会依次说明).避免了频繁提交页面请求而导致的操作繁锁和服务器访问超时问题,以及用户等待时间过长(体验差)和其它易于出错的问题.
下面接着上面的JS代码中的GetMenuItemsForMenu请求的回调方法restaurantSelection_onSuccess来继续我们的操作流程:
function restaurantSelection_onSuccess(result)
{
var menuItemContainer = document.getElementById( " menuList " );
menuItemContainer.innerHTML = "" ;
/* ******* MenuItems ********* */
for ( var i = 0 ; i < result.length; i ++ )
{
var menuItem = result[i];
var menuItemHtml = " <table width='100%' border='0' align='center' cellpadding='8' cellspacing='0' class='thinblueline'><tr><td width='150' align='center' valign='top'><div class='hoverarea'><div><a href=\ ""
+ menuItem.ImageLocation + " \ " target='_blank'> <img id=\ " MenuItemImage\ " src=\ ""
+ menuItem.ImageLocation + " \ " alt=\ ""
+ menuItem.Name + " \ " /><img id=\ " Img1\ " src=\ ""
+ menuItem.ImageLocation+ " \ " alt=\ ""
+ menuItem.Name+ " \ " class=\ " hoverimage_preview\ " /></a></div></div></td><td valign='top'><strong>Item # "
+ i + " </strong><br/><strong> "
+ menuItem.Name + " </strong><br/> "
+ menuItem.Description + " <br/><br/><div align='left'><strong>Estimated Delivery Time: "
+ menuItem.PreparationTime + " minutes</strong></div></td><td width='80' align='right' valign='top'><strong>$ "
+ menuItem.Price + " </strong><br/><br/><a class=\ " noUnderline\ " href=\ " javascript:AddItemToShoppingCart( ' "
+ menuItem.Description + " ' , ' "
+ menuItem.ImageLocation + " ' , ' "
+ menuItem.MenuId + " ' , ' "
+ menuItem.MenuItemId + " ' , ' "
+ menuItem.Name + " ' , ' "
+ menuItem.PreparationTime + " ' , ' "
+ menuItem.Price + " ' );\ " ><img src=\ " images / selectbutton.gif\" border=\"0\" / >< / a>< / td >< / tr>< / table > " ;
var menuItemElement=document.createElement( " span " );
menuItemElement.innerHTML = menuItemHtml;
menuItemContainer.appendChild(menuItemElement);
}
/******** MenuItems **********/
DisplayDivContent4( " shoppingCart " );
DisplayDiv( " MenuDivision " );
}
看到这里,我们在回到页面上看一下当我们单击菜单旁边的"select"按钮之后所显示的页面内容:
而单击所执行的JS方法如下(该方法用于将订餐数据加载到购物车中):
{
var menuItem = new DinnerNow.Business.Data.RestaurantMenuItem();
menuItem.Description = description;
menuItem.ImageLocation = imageLocation;
menuItem.MenuId = menuId;
menuItem.MenuItemId = menuItemId;
menuItem.Name = name;
menuItem.PreparationTime = preparationTime;
menuItem.Price = price;
var restaurant = new DinnerNow.Business.Data.RestaurantHeader();
restaurant.RestaurantId = document.getElementById( " restaurantID " ).innerHTML;
restaurant.Name = document.getElementById( " restaurantName " ).innerHTML;
restaurant.LogoImageLocation = document.getElementById( " restaurantImage " ).src;
DinnerNow.ShoppingCartService.AddItem(menuItem,restaurant,menuSort,addItemToShoppingCart_onSuccess,addItemToShoppingCart_onFailed, null );
}
其中的DinnerNow.ShoppingCartService.AddItem调用会生成如下的ajax请求:
// / <param name="selectedItem" type="DinnerNow.Business.Data.RestaurantMenuItem">DinnerNow.WebUX.MenuSearchService.RestaurantMenuItem</param>
// / <param name="restaurant" type="DinnerNow.Business.Data.RestaurantHeader">DinnerNow.WebUX.MenuSearchService.RestaurantHeader</param>
// / <param name="selectedSortOption" type="Number">System.Int32</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(), ' AddItem ' , false ,{selectedItem:selectedItem,restaurant:restaurant,selectedSortOption:selectedSortOption},succeededCallback,failedCallback,userContext); }
而最终ajax请求会成为对如下方法的调用(DinnerNow.WebUX\Code\ShoppingCartService.cs文件中):
public List < ShoppingCartItem > AddItem(RestaurantMenuItem selectedItem, RestaurantHeader restaurant, int selectedSortOption)
{
ShoppingCartItem shoppingCartItem = new ShoppingCartItem()
{
DeliveryTime = selectedItem.PreparationTime,
MenuItemIdentifier = selectedItem.MenuItemId.ToString(),
MenuItemName = selectedItem.Name,
PreparationTime = selectedItem.PreparationTime,
Price = selectedItem.Price,
Quantity = 1 ,
RestaurantIdentifier = restaurant.RestaurantId.ToString(),
RestaurantImageLocation = restaurant.LogoImageLocation,
RestaurantName = restaurant.Name,
RestaurantItem = selectedItem,
Restaurant = restaurant
};
shoppingCart.AddItem(shoppingCartItem);
return this .RefreshItems(selectedSortOption);
}
通过这个方法我们可以实现将选定的食物放入购物车,并将购物车中已有商品的类型,价格,数据等信息返回到请求页面中.并通过下面的JS方法来显示相应的数据信息.
{
var shoppingCartContainer = document.getElementById( " shoppingCartList " );
shoppingCartContainer.innerHTML = "" ;
var restaurantId = "" ;
var firsth = true ;
var html = "" ;
/* ******* Shopping Cart Items ********* */
for ( var i = 0 ; i < result.length; i ++ )
{
var shoppingCartItem = result[i];
var subtotal = (shoppingCartItem.Price * shoppingCartItem.Quantity); // 商品价格*数量
var restaurantItemHtml = "" ;
var endPrevRestaurantItemHtml = "" ;
if (restaurantId != shoppingCartItem.RestaurantIdentifier && menuSort != 1 )
{
if ( ! firsth)
endPrevRestaurantItemHtml = " </table></td></tr> " ;
firsth = false ;
restaurantId = shoppingCartItem.RestaurantIdentifier;
restaurantItemHtml = " <table width='100%' border='0' align='center' cellpadding='4' cellspacing='4' bgcolor='#5686B4' class='thinblueline'><tr><td align='left' bgcolor='#31465B' class='boldWhite'> "
+ shoppingCartItem.RestaurantName + " </td></tr><tr><td> " ;
}
var shoppingCartHtml = " <table width='100%' border='0' cellspacing='2' cellpadding='2' bgcolor='#5686B4'><tr><td align='left'><a class='noUnderline' href=\ " javascript:DeleteItemFromShoppingCart( ' "
+ shoppingCartItem.MenuItemIdentifier + " ' );\ " ><img src='images/delete.gif' alt='Remove item' width='17' height='16'/></a></td><td align='left' width='60%'> "
+ shoppingCartItem.MenuItemName + " </td><td align='left'><input type='text' id=' "
+ shoppingCartItem.MenuItemIdentifier + " _itemViewQuantityBox' size='2' class='checkOutFormsField' onchange=\ " updateShoppingCartQuantity( ' "
+ shoppingCartItem.MenuItemIdentifier + " ' );\ " value =' "
+ shoppingCartItem.Quantity + " '></input></td><td align='left' nowrap='nowrap' class='bodyTextWhite'> $ "
+ subtotal + " </td></tr> " ;
html += endPrevRestaurantItemHtml + restaurantItemHtml + shoppingCartHtml + " </table><br/> " ;
}
/* ******* Shopping Cart Items ********* */
if (html != "" )
html += " </td></tr></table> " ;
var shoppingCartElement = document.createElement( " span " );
shoppingCartElement.innerHTML = html;
shoppingCartContainer.appendChild(shoppingCartElement);
// ajax调用计算购物车中的Total,ETA信息
DinnerNow.ShoppingCartService.Totals(getTotals_onSuccess, getTotals_onFailed, null );
}
可以看出,整个选购流程还是有些复杂的,但在操作上却很流畅,让人感觉不出什么繁锁.这其实都要得益于ajax的适当应用.当然微软的Ajax Extensions无形中也降低了AJAX代码的开发门槛:)
在下一篇文章中,我们将会继续订餐流程,不过重点将会转移动到DinnerNow - ServicePortfolio2.sln解决方案下,并且因为DinnerNow中使用了WWF(Windows Work Flow Foundation), 所以下一篇文章的内容可能会更偏向于WWF。如果大家感兴趣的话,敬请留意:)
好了,今天的文章就先到这里了,如果大家有什么问题欢迎与我交流.
本文转自 daizhenjun 51CTO博客,原文链接:http://blog.51cto.com/daizhj/339019,如需转载请自行联系原作者