解释,不解释:
紧接上文,我们在Visual Studio2012中看到系统为我们自动创建的视图(View)文件Index.cshtml中,开头有如下这句话:
@model IEnumerable<GuestBook.Models.Book>
这句话是MVC通过强类型获取数据的方式,我们可以看出,在MVC视图中使用了IEnumerable<T>接口来循环读取数据并生成列表,该接口在System.Collections.Generic命名空间下。这就需要我们在控制器中传递的数据也是IEnumerable<T>类型的。
那么,我们是如何通过控制器将数据传递到视图中的哪?在MVC中我们使用数据上下文来存取数据,数据上下文是一个继承自DbContext基类的派生类,该类定义在System.Data.Entity命名空间下,DbContext 通常将与包含模型的根实体的 DbSet<TEntity> 属性的派生类型一起使用。 DbContext简化了实体与数据库的沟通细节,让我们可以专注于逻辑的开发,值得一提的是,在定义它的构造函数时可以使用基类的构造函数指明使用数据库的链接字符串,如下定义:
public MVCGuestBookContext() : base("name=DefaultConnection") { }
在使用DbContext创建派生类的实例时,会自动初始化这些根实体的 DbSet<TEntity> 集合。在DbContext这个派生类中,我们可以定义类似上节中的
public DbSet<Book> Books { get; set; }
这是一个DbSet<Tentity>泛型集合类,它为数据上下文类提供了一个可以访问的属性,该属性指定的类型为我们定义的Model,如:我们定义的Book。因为该属性通过DbContext初始化,所以我们利用该属性的定义,就可以使用集合的操作方式对数据进行查看等等操作。比如我们上节中在控制器中定义了数据上下文对象:
private MVCGuestBookContext db = new MVCGuestBookContext();
我们就可以在Index动作中使用
var data = db.Books.ToList();
来获取这个集合中的所有行,并通过动作中的ViewResult将data传递到视图中,如下:
return View(data);
视图页面通过@model 定义接收到这个data数据后,就可以使用@foreach (var item in Model) 遍历集合,得到想要的数据列表。
DbContext类
我们常使用自定义的数据上下文类继承自DbContext类,因为DbContext帮我们隐藏了与数据库沟通的细节,仅仅通过构造函数时指明数据库链接字符串,就可以实现自动初始化类型为DbSet<Tentity>泛型集合类的属性。方便了我们对数据的存取。该类定义如下:
命名空间: System.Data.Entity
程序集: EntityFramework(在 EntityFramework.dll 中)
public class DbContext : IDisposable, IObjectContextAdapter
该类提供了一系列的属性和方法来进行操作,你可以仔细阅读MSDN中的介绍。
特别注释:
在构造函数中,其中有一个构造函数重载是指明链接字符串。
DbContext(String) 可以将给定字符串用作将连接到的数据库的名称或连接字符串来构造一个新的上下文实例。 请参见有关这如何用于创建连接的类备注。
在方法中,有一个SaveChanges()方法,该方法可以将在此上下文中所做的所有更改保存到基础数据库。
SaveChanges 将在此上下文中所做的所有更改保存到基础数据库。
DbSet<TEntity>类在数据上下文中主要作用在定义该类型的属性,利用这个属性可以方便对数据进行创建、读取、更新、删除等操作。该类定义如下:
命名空间: System.Data.Entity
程序集: EntityFramework(在 EntityFramework.dll 中)
[SuppressMessageAttribute("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification = "Name is intentional")] public class DbSet<TEntity> : DbQuery<TEntity>, IDbSet<TEntity>, IQueryable<TEntity>, IEnumerable<TEntity>, IQueryable, IEnumerable where TEntity : class
从这个类定义中,我们可以看出它实现了DbQuery<TEntity>, IDbSet<TEntity>, IQueryable<TEntity>, IEnumerable<TEntity>, IQueryable, IEnumerable 接口,这意味着,他可以使用这些接口提供的属性及方法来操作数据。
特别注释:
该类具有一些实用的方法可供我们使用,例如对数据进行操作的Add(),Find(),Remove(),需要重点介绍的是SqlQuery还可以使用原生的SQL语句执行操作,十分方便,你可以阅读http://www.cnblogs.com/mane/p/3387960.html文章详细了解,如下:
Add 将给定实体以“已添加”状态添加到集的基础上下文中,这样一来,当调用 SaveChanges 时,会将该实体插入到数据库中。
Find 查找带给定主键值的实体。 如果上下文中存在带给定主键值的实体,则立即返回该实体,而不会向存储区发送请求。 否则,会向存储区发送查找带给定主键值的实体的请求,如果找到该实体,则将其附加到上下文并返回。 如果未在上下文或存储区中找到实体,则返回 null。
Remove 将给定实体标记为“已删除”,这样一来,当调用 SaveChanges 时,将从数据库中删除该实体。 请注意,在调用此方法之前,该实体必须以另一种状态存在于该上下文中。
SqlQuery 创建一个原始 SQL 查询,该查询将返回此集中的实体。 默认情况下,上下文会跟踪返回的实体;可通过对返回的 DbSqlQuery<TEntity> 调用 AsNoTracking 来更改此设置。 请注意返回实体的类型始终是此集的类型,而不会是派生的类型。 如果查询的一个或多个表可能包含其他实体类型的数据,则必须编写适当的 SQL 查询以确保只返回适当类型的实体。
另外,该类还提供了丰富的扩展方法,你可以访问MSDN了解更多详情http://msdn.microsoft.com/query/dev11.query?appId=Dev11IDEF1&l=ZH-CN&k=k(System.Data.Entity.DbSet`1);k(DbSet%3CUser%3E);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.0);k(DevLang-csharp)&rd=true
有了上面的知识,现在,我们可以修改上节中的文档实现简单的数据插入。
MVCGuestBookContext.cs
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Data.Entity;// namespace GuestBook.Models { public class MVCGuestBookContext : DbContext { public MVCGuestBookContext() : base("name=DefaultConnection") { } public DbSet<User> Users { get; set; } public DbSet<Book> Books { get; set; } } }
HomeController.cs
using System; using System.Collections.Generic; using System.Data; using System.Data.Entity; using System.Linq; using System.Web; using System.Web.Mvc; using GuestBook.Models; namespace GuestBook.Controllers { public class HomeController : Controller { private MVCGuestBookContext db = new MVCGuestBookContext(); public ActionResult Index() { var user = db.Users.Find(3); db.Books.Add(new Book() { Id = 33, Body = "这是一个正文",PublishOn=DateTime.Now, User=user}); db.SaveChanges(); return View(db.Books.ToList()); } } }
题外话:本人才疏学浅,所以不对的地方,还请各位高人指点迷津,下一节将讲述另外的几个类,以全面了解使用函数方式操作数据,本文所有内容都为本人理解而写,不代表官方定义!