一步一步学习ASP.NET MVC 1.0创建NerdDinner 范例程序,Part 6


一步一步学习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对象上。我们只需要在一个地方灵活更改业务规则,而不需要在整个应用程序和用户界面层到处更改,这是一个良好的应用程序设计。
 
今天就到这里了,明天继续翻译ASP.NET MVC eBook – 开发NerdDinner 范例程序向导。欢迎访问http://blog.EntLib.com 博客平台获取更多的MVC学习资料。
 
相关资源下载链接:
1. ASP.NET MVC 1.0 正式版发布了!!!
 
2. 免费ASP.NET MVC eBook 向导-电子书下载
 

 

发表 @ 2009年3月29日 2:39

打 印

评论

# re: 一步一步学习ASP.NET MVC 1.0创建NerdDinner 范例程序,Part 6

Left by ZilchWei at 2009/4/10 22:59
Gravatar
错误 1 未找到用于实现分部方法“NerdDinner.Models.Dinner.OnValidate(System.Data.Linq.ChangeAction)”的声明的定义声明\Projects\NerdDinner\NerdDinner\Models\Dinner.cs 39 22 NerdDinner

# re: 一步一步学习ASP.NET MVC 1.0创建NerdDinner 范例程序,Part 6

Left by entlibforum at 2009/4/10 23:26
Gravatar
ZilchWei,

可能是你在某一步操作错误了。
你可以下载NerdDinner的范例程序源代码对比一下:
http://forum.entlib.com/Default.aspx?g=posts&t=487

上述下载的NerdDinner范例程序,包括数据库文件(SQL Server 2008)。

# re: 一步一步学习ASP.NET MVC 1.0创建NerdDinner 范例程序,Part 6

Left by itbird at 2009/4/11 22:13
Gravatar
注意:需要用到下面的引用

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Linq;
using System.Text.RegularExpressions;

# re: 一步一步学习ASP.NET MVC 1.0创建NerdDinner 范例程序,Part 6

Left by gameover at 2009/4/29 11:15
Gravatar
我也出现了一样的问题不晓得为什么。加了引用也不行
还有一些错误
错误 3 当前上下文中不存在名称“ContactPhone” D:\我的文档\Visual Studio 2008\Projects\NerdDinner\NerdDinner\Models\Dinner.cs 29 38 NerdDinner
在验证逻辑里面的参数都说 不存在。
不晓得为什么了
前面以图片的形式出现的代码不用加到项目中吧??
很汗颜。

# re: 一步一步学习ASP.NET MVC 1.0创建NerdDinner 范例程序,Part 6

Left by entlibforum at 2009/4/29 15:00
Gravatar
Gameover, 前面以图片形式的代码不用加到项目中。

你可以到这里下载NerdDinner 范例程序源代码看看,有助于分析错误原因:
forum.entlib.com/Default.aspx?g=posts&t=496

# re: 一步一步学习ASP.NET MVC 1.0创建NerdDinner 范例程序,Part 6

Left by ggz at 2009/4/30 15:59
Gravatar
partial void OnValidate(ChangeAction action);
在分部类Dinner中加入分部方法的声名;
错误 1 未找到用于实现分部方法“NerdDinner.Models.Dinner.OnValidate(System.Data.Linq.ChangeAction)”的声明的定义声明\Projects\NerdDinner\NerdDinner\Models\Dinner.cs 39 22 NerdDinner
就不会出现了

# re: 一步一步学习ASP.NET MVC 1.0创建NerdDinner 范例程序,Part 6

Left by 小米 at 2009/5/11 22:59
Gravatar
错误 1 类型“NerdDinner.Models.Dinner”的声明上缺少 partial 修饰符;存在此类型的其他分部声明 D:\My Documents\Visual Studio 2008\projects\NerdDinner\NerdDinner\Models\Dinner.cs 15 18 NerdDinner

这个怎么改?

# re: 一步一步学习ASP.NET MVC 1.0创建NerdDinner 范例程序,Part 6

Left by moto at 2009/5/31 16:54
Gravatar
public bool IsValid
{
get
{
return (GetRuleViolations().Count() == 0);
}
}

这个里面GetRuleViolations(). 出现:在以下方法或属性之间的调用不明确

# re: 一步一步学习ASP.NET MVC 1.0创建NerdDinner 范例程序,Part 6

Left by RuleViolation at 2009/6/10 21:57
Gravatar
RuleViolation 是什么类?

# re: 一步一步学习ASP.NET MVC 1.0创建NerdDinner 范例程序,Part 6

Left by RuleViolation at 2009/6/11 21:17
Gravatar
我知道了,抱歉没有往下看

# re: 一步一步学习ASP.NET MVC 1.0创建NerdDinner 范例程序,Part 6

Left by yupinghuashi at 2009/7/16 3:31
Gravatar
RuleViolation 放入迭代器里面跑

# re: 一步一步学习ASP.NET MVC 1.0创建NerdDinner 范例程序,Part 6

Left by ask at 2009/7/24 18:04
Gravatar
我在看了下一章节 part7 按照课文创建了 public class DinnersController : Controller
public void Index()
public void Details(int id)

启动调试报这个错误


public IEnumerable<RuleViolation> GetRuleViolations()
{
yield break;
}

错误 1 非泛型 类型“System.Collections.IEnumerable”不能与类型实参一起使用 D:\asp\NerdDinner\NerdDinner\Models\Dinner.cs 66 16 NerdDinner

# re: 一步一步学习ASP.NET MVC 1.0创建NerdDinner 范例程序,Part 6

Left by ask at 2009/7/27 15:09
Gravatar
我的VS2008是中文版。遇到不少问题。
总结解决办法
1.很多页面例子里面没有给出相关的引用。需要用到相关的引用
2.发现问题出在NerdDinner.designer.cs 对比源代码就能发现问题主要是Dinners Dinner
RSVP RSVPs

# re: 一步一步学习ASP.NET MVC 1.0创建NerdDinner 范例程序,Part 6

Left by 过关 at 2009/12/19 9:12
Gravatar
同意楼上的。Partial类用来增加方法/属性/事件到VS 设计器生成的类中(如LINQ to SQL设计器生成的Dinner类)自己写的这个
public partial class Dinner
{…… }
中的Dinner应该和自动生成的
NerdDinner.designer.cs
里面的Dinner一样。如果NerdDinner.designer.cs是Dinners 这里的public partial class Dinner中的Dinner就应该是Dinners

# re: 一步一步学习ASP.NET MVC 1.0创建NerdDinner 范例程序,Part 6

Left by heywap at 2010/2/1 13:35
Gravatar
没有看出是在讲mvc,倒是觉得都是在将linq to sql

# re: 一步一步学习ASP.NET MVC 1.0创建NerdDinner 范例程序,Part 6

Left by chester at 2010/3/3 10:12
Gravatar
错误 1 非泛型 类型“System.Collections.IEnumerable”不能与类型实参一起使用 D:\asp\NerdDinner\NerdDinner\Models\Dinner.cs 66 16 NerdDinner

这个已经找出原因了。
要引用using System.Collections.Generic,才对。

您的评论:



 (不显示)


 
 
 
Please add 7 and 6 and type the answer here:
    
 

评论预览窗口:

 
«九月»
2930311234
567891011
12131415161718
19202122232425
262728293012
3456789