一步一步学习ASP.NET MVC 1.0创建NerdDinner 范例程序,Part 6
本文根据《Professional ASP.NET MVC 1.0》中微软牛人Scott Guthrie 提供免费下载的第一章,一步一步演示如何通过ASP.NET MVC 1.0 正式版创建NerdDinner 范例程序。对了解如何使用最新的ASP.NET MVC 1.0框架创建Web Application 非常有帮助。本文由
http://forum.entlib.com 开源论坛小组提供。关于本文的一些资源,请参考文章最后的链接。
前面系列文章:
本文继续学习之旅,一步一步通过ASP.NET MVC 1.0 实现NerdDinner 范例程序。
使用DinnerRepository类实现检索、更新、插入和删除操作
现在我们已经创建了DinnerRepository类,下面我们看看一些示例代码。
查询代码:
下面的代码使用DinnerID检索一条Dinner记录:
DinnerRepository dinnerRepository = new DinnerRepository();
// Retrieve specific dinner by its DinnerID
Dinner dinner = dinnerRepository.GetDinner(5);
下面的代码用来检索所有将来的dinners,并遍历:
DinnerRepository dinnerRepository = new DinnerRepository();
// Retrieve all upcoming Dinners
var upcomingDinners = dinnerRepository.FindUpcomingDinners();
// Loop over each upcoming Dinner
foreach (Dinner dinner in upcomingDinners) {
}
插入和更新代码:
下面的代码演示新增2个dinners,新增或更新的信息不会提交到数据库,直到调用Save() 方法。LINQ to SQL自动包装所有更新的数据库事务,因此在调用Repository的Save() 方法时,或者所有的变更都发生,或者都不发生。
DinnerRepository dinnerRepository = new DinnerRepository();
// Create First Dinner
Dinner newDinner1 = new Dinner();
newDinner1.Title = "Dinner with Scott";
newDinner1.HostedBy = "ScotGu";
newDinner1.ContactPhone = "425-703-8072";
// Create Second Dinner
Dinner newDinner2 = new Dinner();
newDinner2.Title = "Dinner with Bill";
newDinner2.HostedBy = "BillG";
newDinner2.ContactPhone = "425-555-5151";
// Add Dinners to Repository
dinnerRepository.Add(newDinner1);
dinnerRepository.Add(newDinner2);
// Persist Changes
dinnerRepository.Save();
下面的代码首先检索一个存在的Dinner对象,然后更新2个属性,最后调用Repository对象的Save() 方法,提交更新到数据库。
DinnerRepository dinnerRepository = new DinnerRepository();
// Retrieve specific dinner by its DinnerID
Dinner dinner = dinnerRepository.GetDinner(5);
// Update Dinner properties
dinner.Title = "Update Title";
dinner.HostedBy = "New Owner";
// Persist changes
dinnerRepository.Save();
下面的示例代码首先检索一个dinner对象,然后添加一个RSVP对象。这里,使用Dinner对象的RSVPs集合对象。当调用Resposity对象的Save()方法时,一条新的记录添加RSVP表中。
DinnerRepository dinnerRepository = new DinnerRepository();
// Retrieve specific dinner by its DinnerID
Dinner dinner = dinnerRepository.GetDinner(5);
// Create a new RSVP object
RSVP myRSVP = new RSVP();
myRSVP.AttendeeName = "EntLib.com Team";
// Add RSVP to Dinner's RSVP Collection
dinner.RSVPs.Add(myRSVP);
// Persist changes
dinnerRepository.Save();
删除操作代码:
下面的代码首先检索一个已存在的Dinner对象,然后调用Repository对象的Delete() 方法,标记该条记录删除。最后,在调用Repository对象的Save() 方法时,从数据库表中删除该记录。
DinnerRepository dinnerRepository = new DinnerRepository();
// Retrieve specific dinner by its DinnerID
Dinner dinner = dinnerRepository.GetDinner(5);
// Mark dinner to be deleted
dinnerRepository.Delete(dinner);
// Persist changes
dinnerRepository.Save();
Model类集成验证和业务规则逻辑
集成验证和业务规则逻辑是任何与数据打交道的应用程序的最重要部分。
Schema验证
当使用LINQ to SQL设计器定义Model类时,数据模型类的属性类型和数据表的字段类型相关。例如,如果Dinners表中EventDate列是datetime类型,LINQ to SQL创建的数据模型类相关属性也是DateTime类型(这是内置的.NET数据类型)。这意味着,如果你试图赋值整型或布尔型,将产生编译错误。
当使用字符串时,LINQ to SQL将自动避开SQL值,因此你不必担心SQL注入攻击。
验证和业务规则逻辑
作为第一步,数据类型验证是非常有用的,但是还不足够。在大多数情况下,需要指定更丰富的验证逻辑。有很多不同的模式和框架可对模型类定义和应用验证。
在NerdDinner范例程序中,我们将采用相对简单和直接的模式,对Dinner模型类公开IsValid属性和GetRuleViolations() 方法,IsValid属性根据验证和业务规则返回true或false,GetRuleViolations() 则返回所有错误的业务逻辑列表。
我们通过添加Partial class到项目中,来实现IsValid和GetRuleViolations()方法。Partial类用来增加方法/属性/事件到VS 设计器生成的类中(如LINQ to SQL设计器生成的Dinner类),有助于区分我们编写的代码。
在项目中\Models文件夹,添加新的类,并命名为Dinner.cs。如下图所示:
点击Add按钮,添加Dinner.cs文件到项目中,并默认打开。接着,我们使用如下的代码来实现基本的业务规则/验证机制。
public partial class Dinner
{
public bool IsValid
{
get { return (GetRuleViolations().Count() == 0); }
}
public IEnumerable<RuleViolation> GetRuleViolations()
{
yield break;
}
partial void OnValidate(ChangeAction action)
{
if (!IsValid)
throw new ApplicationException("Rule violations prevent saving");
}
}
public class RuleViolation
{
public string ErrorMessage { get; private set; }
public string PropertyName { get; private set; }
public RuleViolation(string errorMessage)
{
ErrorMessage = errorMessage;
}
public RuleViolation(string errorMessage, string propertyName)
{
ErrorMessage = errorMessage;
PropertyName = propertyName;
}
}
上述代码提供了集成验证和业务规则的简单框架。现在,我们可以增加如下规则到GetRuleViolations() 方法中。
public IEnumerable<RuleViolation> GetRuleViolations()
{
if (String.IsNullOrEmpty(Title))
yield return new RuleViolation("Title required", "Title");
if (String.IsNullOrEmpty(Description))
yield return new RuleViolation("Description required", "Description");
if (String.IsNullOrEmpty(HostedBy))
yield return new RuleViolation("HostedBy required", "HostedBy");
if (String.IsNullOrEmpty(Address))
yield return new RuleViolation("Address required", "Address");
if (String.IsNullOrEmpty(Country))
yield return new RuleViolation("Country required", "Address");
if (String.IsNullOrEmpty(ContactPhone))
yield return new RuleViolation("Phone# required", "ContactPhone");
if (!PhoneValidator.IsValidNumber(ContactPhone, Country))
yield return new RuleViolation("Phone# does not match country",
"ContactPhone");
yield break;
}
这里,我们使用C#的yield return 特性,返回有序的RuleViolation集合。
**
yield return语句返回集合的一个元素,并移动到下一个元素上。yield break可停止迭代。包含yield语句的方法或属性也称为迭代块。迭代块必须声明为返回IEnumerator或IEnumerable接口。这个块可以包含多个yield return语句或yield break语句,但不能包含return语句。
**
前6个检查确保Dinner的字符串属性不能是null或空,最后一个规则比较有趣,调用PhoneValidator.IsValidNumber() 方法,该方法将添加项目,用来验证ContactPhone数字格式符合相应的规则。
我们使用.NET的正则表达式来实现电话号码的验证,下面是我们添加到项目中的一个简单PhoneValidator的实现,实现指定国家的正则模式检查。
public class PhoneValidator
{
static IDictionary<string, Regex> countryRegex =
new Dictionary<string, Regex>() {
{ "USA", new Regex("^[2-9]\\d{2}-\\d{3}-\\d{4}$")},
{ "UK", new Regex("(^1300\\d{6}$)|(^1800|1900|1902\\d{6}$)|(^0[2|3|7|8]{1}[0-9]{8}$)|(^13\\d{4}$)|(^04\\d{2,3}\\d{6}$)")},
{ "Netherlands", new Regex("(^\\+[0-9]{2}|^\\+[0-9]{2}\\(0\\)|^\\(\\+[0-9]{2}\\)\\(0\\)|^00[0-9]{2}|^0)([0-9]{9}$|[0-9\\-\\s]{10}$)")},
};
public static bool IsValidNumber(string phoneNumber, string country)
{
if (country != null && countryRegex.ContainsKey(country))
return countryRegex[country].IsMatch(phoneNumber);
else
return false;
}
public static IEnumerable<string> Countries
{
get
{
return countryRegex.Keys;
}
}
}
现在,当我们创建或更新Dinner对象时,验证逻辑规则将生效。开发人员可以主动判断是否Dinner对象是有效,并在不抛出异常的情况下,检索所有冲突列表。
Dinner dinner = dinnerRepository.GetDinner(5);
dinner.Country = "USA";
dinner.ContactPhone = "425-555-BOGUS";
if (!dinner.IsValid) {
var errors = dinner.GetRuleViolations();
// do something to fix errors
}
如果我们试图保存一个无效状态的Dinner对象,在调用DinnerRepository的Save() 方法时,将产生异常。这是因为Dinner对象存在冲突的业务规则,Dinner.OnValidate() 分部方法(Partial Method)抛出异常。我们可以捕获这一异常,并主动检索冲突列表,进行修复。
Dinner dinner = dinnerRepository.GetDinner(5);
try {
dinner.Country = "USA";
dinner.ContactPhone = "425-555-BOGUS";
dinnerRepository.Save();
}
catch {
var errors = dinner.GetRuleViolations();
// do something to fix errors
}
因为验证和业务规则在业务领域模型层实现,而不是在用户界面UI层,这些可以应用在应用程序的所有场景中。随后,我们可以更改或增加业务规则,并让这些代码应用到Dinner对象上。我们只需要在一个地方灵活更改业务规则,而不需要在整个应用程序和用户界面层到处更改,这是一个良好的应用程序设计。
相关资源下载链接:
1. ASP.NET MVC 1.0 正式版发布了!!!
2. 免费ASP.NET MVC eBook 向导-电子书下载