至于Model层,我看网上大多数人是用LINQ TO SQL实现的,毕竟使用起来很简单,设计好表,用LINQ 设计器往vs.net里一拖就能用了。而且本身就是强类型的,再在自动生成的代码上加一些分部方法,就可以实现数据的有效性验证等。还有就是对LINQ做的Model进行数据持久化和查询的时候更方便,直接用DbContext一个类,增删改查全能搞定。
有得就有舍,ASP.NET MVC虽然提供了先进的思想和一些便利,但ASP.NET以前的一些东西不能用了,比如以前自己写的一些服务器控件儿不能用了,WebPart,皮肤,各种数据绑定控件等都不能用了,但Master页还能用,Asp.net Ajax control toolkit(服务端)也不能用了,但asp.net ajax library(客户端js库)还能继续使用,基于页面和目录的授权不能用了(因为现在没页面,只有view了),但MemberShip和Forms身份验证还是支持的。标准WebForm的生命周期变了,好些事件没了,现在你可以写一些拦截器(Action拦截器、Result拦截器和Exception拦截器)来影响请求的处理过程,还有一些区别,总之失去的东西,都有变通的方法能找吧回来。
2、linq to sql如何获取插入语句产生的标识列的值?
其实很简单,把对象插入数据库后,直接取值就行了,如下BBSPost是一个实体类,其中PostID在数据库里是自增列。 复制代码 代码如下: var db = new BBSDbContext(connstr); BBSPost post = new BBSPost() post.PostUser = User.Identity.Name; post.PostTime = DateTime.Now; db.BBSPosts.InsertOnSubmit(post); db.SubmitChanges(); int postid = post.PostID; //这里就能取到标识列的值。
数据有效性的验证基本上哪个程序都躲不了,LINQ 和ASP.NET MVC的配合,让数据验证的实现也很方便。 LINQ TO SQL设计器自动生成的类是一个分部类,就是半块儿的类,你可以写一个分步类,在自动生成的类上加一些扩展的方法,如下我们在LINQ实体类BBSPost上加了一个GetRuleViolations方法,一个IsValid属性,其中GetRuleViolations方法验证给实体类赋的值的有效性,用yield关键字返回一个枚举器,这里可以写你自己的数据有效性验证逻辑。 IsValid属性内部调用GetRuleViolations方法,如果返回的枚举器的Count不是0的话,表示数据有效性验证不通过。 另外为了方式LINQ TO SQL往数据库里写入无效数据,我们给OnValidate分布方法加了两行代码,在数据有效性验证不通过的情况下写数据库之前抛出异常。 复制代码 代码如下: public partial class BBSPost { public bool IsValid { get { return (GetRuleViolations().Count() == 0); } }
public IEnumerable<RuleViolation> GetRuleViolations() { if (String.IsNullOrEmpty(Title)) yield return new RuleViolation("标题必须输入", "Title"); if (String.IsNullOrEmpty(Content)) yield return new RuleViolation("内容必须输入", "Content"); yield break; }
partial void OnValidate(ChangeAction action) { if (!IsValid) throw new ApplicationException("Rule violations prevent saving"); } }
RuleViolation是一个辅助类,很简单。 复制代码 代码如下: public class RuleViolation {
public string ErrorMessage { get; private set; } public string PropertyName { get; private set; }
public RuleViolation(string errorMessage) { ErrorMessage = errorMessage; }
SQLSERVER 2005有很强悍的分页函数,LINQ TO SQL对其有很好的支持,IQueryable<T>的Skip和Take方法最终就生成分页的SQL,先写如下的一个帮助类(取自NerdDinner),这个类的属性很简单,见名知意,就不介绍了。 复制代码 代码如下: public class PaginatedList<T> : List<T> {
public int PageIndex { get; private set; } public int PageSize { get; private set; } public int TotalCount { get; private set; } public int TotalPages { get; private set; }
public PaginatedList(IQueryable<T> source, int pageIndex, int pageSize) { PageIndex = pageIndex; PageSize = pageSize; TotalCount = source.Count(); TotalPages = (int) Math.Ceiling(TotalCount / (double)PageSize);
public bool HasPreviousPage { get { return (PageIndex > 0); } }
public bool HasNextPage { get { return (PageIndex+1 < TotalPages); } } }
使用起来很简单,用LINQ TO SQL得到一个IQueryable后,再用其New一个PaginatedList就表示一个已分页的数据集了 复制代码 代码如下: var posts = from post in db.BBSPosts where post.CategoryID == id && post.ParentID == 0 orderby post.PostID descending select post; const int pageSize = 10; var pagePosts = new PaginatedList<BBSPost>(posts, page ?? 0, pageSize); return View(pagePosts);
有人怀疑LINQ TO SQL的性能问题,认为它生成的语句不靠谱,其实它生成的语句都是参数化查询,一般的基于主键或者索引列的查询及大多数更新操作性能应该不会比手写SQL差,如果还是不放心的话,可以把LINQ TO SQL生成的SQL打印出来,以避免性能查的语句产生。 如下代码 复制代码 代码如下: var db = new BBSDbContext(conn); var posts = from post in db.BBSPosts where post.CategoryID == 1 && post.ParentID == 0 orderby post.PostID descending select new {post.PostID, post.Title, post.Content}; db.Log = Response.Output; //跟踪自动生成的SQL语句 rpt1.DataSource = posts; rpt1.DataBind(); //只有真正执行使用数据的语句时,SQL查询才会执行,在这之前语句只是语句,自动延迟执行的。
会在页面上看到LINQ TO SQL生成的SQL语句 SELECT [t0].[PostID], [t0].[Title], [t0].[Content] FROM [dbo].[bbs_Post] AS [t0] WHERE ([t0].[CategoryID] = @p0) AND ([t0].[ParentID] = @p1) ORDER BY [t0].[PostID] DESC -- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [1] -- @p1: Input Int (Size = 0; Prec = 0; Scale = 0) [0] -- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.30729.1 如果改成如下分页方式 复制代码 代码如下: var db = new BBSDbContext(conn); var posts = from post in db.BBSPosts where post.CategoryID == 1 && post.ParentID == 0 orderby post.PostID descending select post; db.Log = Response.Output; rpt1.DataSource = posts.Skip(1 * 5).Take(5); rpt1.DataBind();
会输出如下SQL SELECT [t1].[CategoryID], [t1].[PostID], [t1].[ParentID], [t1].[Title], [t1].[Content], [t1].[PostUser], [t1].[PostTime] FROM ( SELECT ROW_NUMBER() OVER (ORDER BY [t0].[PostID] DESC) AS [ROW_NUMBER], [t0].[CategoryID], [t0].[PostID], [t0].[ParentID], [t0].[Title], [t0].[Content], [t0].[PostUser], [t0].[PostTime] FROM [dbo].[bbs_Post] AS [t0] WHERE ([t0].[CategoryID] = @p0) AND ([t0].[ParentID] = @p1) ) AS [t1] WHERE [t1].[ROW_NUMBER] BETWEEN @p2 + 1 AND @p2 + @p3 ORDER BY [t1].[ROW_NUMBER] -- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [1] -- @p1: Input Int (Size = 0; Prec = 0; Scale = 0) [0] -- @p2: Input Int (Size = 0; Prec = 0; Scale = 0) [5] -- @p3: Input Int (Size = 0; Prec = 0; Scale = 0) [5] -- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.30729.1 可以看到这些查询用的都是参数化查询,不是拼SQL,而且还用了ROW_NUMBER函数,LINQ TO SQL还是比较了解SQLSERVER的。