学习笔记4_实体框架Entity Framework
实体框架Entity Framework
实体框架是ADO.NET中的一组支持开发面向数据的软件应用程序技术.
实体框架让开发人员采用特定于域的对象和属性(如客户和客户地址)的形式使用数据,而不必自己考虑存储这些数据的基础数据库表和列.
1.实体框架基本概念
数据建模通常将数据模型分为3个部分:概念模型,逻辑模型和物理模型.
- 概念模型定义要建模的系统中的实体和关系.
- 关系数据库的逻辑模型通过外键约束将实体和关系规划到表中.
- 物理模型通过指定分区和索引等存储详细信息实现特定数据引擎的功能.
实体框架可以开发人员查询概念模型中的实体和关系,同时依赖于实体框架将这些操作转换为特定于数据源的命令.这使应用程序不再对特定数据源具有硬编码的依赖性.
概念模型,存储模型以及两个模型之间的映射以外部规范(称为实体数据模型EDM)表示.可以根据需要对存储模型和映射进行更改,而不需要对概念模型,数据类或应用程序
代码进行更改.存储模型是特定于提供程序的,因此可以在各种数据源之间使用一致的概念模型.
EDM由3种模型和具有相应文件扩展名的映射文件进行定义.
概念架构定义语言文件(.csdl)定义模型概念.
存储架构定义语言文件(.ssdl)定义存储模型(又称逻辑模型).
映射规范语言文件(.msl)定义存储模型与概念模型之间的映射.
2.创建数据模型
实体框架基于实体模型进行工作.既可以从数据库生成实体模型,又可以从实体模型自动生成数据库.
如果要从数据库生成实体模型.则如下操作.
ADO.NET实体数据模型,从数据库生成.
3.查询数据
添加GridView控件和一个分页控件AspNetPager,注意AutoGenerateColumns,ReadOnly,我并没有使用DataKeyNames属性在GridView里面
<form id="form1" runat="server"> <div> <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"> <Columns> <asp:BoundField DataField="ProductID" HeaderText="商品编号" ReadOnly="True" /> <asp:BoundField DataField="ProductName" HeaderText="商品名称" /> <asp:BoundField DataField="Supplier" HeaderText="供货商编号" /> <asp:BoundField DataField="Category" HeaderText="目录编号" /> <asp:BoundField DataField="QuantityPerUnit" HeaderText="包装数量" /> <asp:BoundField DataField="UnitPrice" HeaderText="包装单价" /> <asp:BoundField DataField="UnitsInStock" HeaderText="库存数量" /> <asp:BoundField DataField="UnitsOnOrder" HeaderText="订货数量" /> </Columns> </asp:GridView> </div> <webdiyer:AspNetPager ID="AspNetPager1" runat="server" OnPageChanged="AspNetPager1_PageChanged"> </webdiyer:AspNetPager> </form>
后台代码,页面加载判断回传,AspNetPager1翻页方法绑定数据
protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { BindProducts(); } } protected void AspNetPager1_PageChanged(object sender, EventArgs e) { BindProducts(); } private void BindProducts() { NorthwindEntities db = new NorthwindEntities(); //此处使用了外键关系(导航属性),注意的是"p.外键表.属性"而不是"p.外键ID.属性" //匿名类的属性对应DataField属性取值. var query = from p in db.Products orderby p.ProductID select new { ProductID = p.ProductID, ProductName = p.ProductName, Supplier = p.Suppliers.CompanyName, Category = p.Category.CategoryName, QuantityPerUnit = p.QuantityPerUnit, UnitPrice = p.UnitPrice, UnitsInStock = p.UnitsInStock, UnitsOnOrder = p.UnitsOnOrder }; int count = query.Count(); AspNetPager1.RecordCount = count; var products = query.Skip((AspNetPager1.CurrentPageIndex - 1) * AspNetPager1.PageSize) .Take(AspNetPager1.PageSize) .ToList(); GridView1.DataSource = products; GridView1.DataBind(); }
运行结果,注意"供应商编号"和"目录编号"列
4.外键关系和导航属性
在实体框架中,数据库的外键关系被映射为导航属性.在Product模型底部的导航属性中有一个Category导航属性,通过这个属性可以找到产品所属类别Category,在
Category模型的导航属性中有一个Products导航属性,可以找到该类别的所有商品.而不是使用CategoryID外键.
private void BindProductsByCategory() { NorthwindEntities db = new NorthwindEntities(); var products = (from p in db.Products orderby p.ProductID select p).ToList(); ; //通过导航属性Products找到该类别的所有产品,而不是使用CategoryID外键 var query = products[0].Category.Products; int count = query.Count(); AspNetPager1.RecordCount = count; var result = query.Skip((AspNetPager1.CurrentPageIndex - 1) * AspNetPager1.PageSize) .Take(AspNetPager1.PageSize) .ToList(); GridView1.DataSource = result; GridView1.DataBind(); }
使用下拉列表框DropDownList显示所有类别Category,选择类别,显示类别下面的所有商品Product,商品分页显示.
效果如下显示:
在Default.aspx页面的Page_Load事件中,绑定所有类别到DropDownList控件
protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { NorthwindEntities db = new NorthwindEntities(); //不知道此处加ToList()和不加有什么影响? DropDownList1.DataSource = db.Categories; DropDownList1.DataBind(); } }
DropDownList的SelectedIndexChanged事件,显示当前选中类别下所有的商品
protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e) { NorthwindEntities db = new NorthwindEntities(); int categoryID = int.Parse(DropDownList1.SelectedValue); Category category = (from c in db.Categories where c.CategoryID == categoryID select c) .Single(); var products = category.Products; int count = products.Count; AspNetPager1.RecordCount = count; //Category变化时返回当前类别第一页,不然选择A类浏览第5页,然后选择B类,也会在第5页,如果有. AspNetPager1.CurrentPageIndex = 1; var list = products.Skip((AspNetPager1.CurrentPageIndex - 1) * AspNetPager1.PageSize) .Take(AspNetPager1.PageSize); GridView1.DataSource = list; GridView1.DataBind(); }
AspNetPager的PageChanged事件,绑定分页,when to use "count",the select will be done.
private void Bind() { NorthwindEntities db = new NorthwindEntities(); int categoryID = int.Parse(DropDownList1.SelectedValue); Category category = (from c in db.Categories where c.CategoryID == categoryID select c) .Single(); var products = category.Products; int count = products.Count; AspNetPager1.RecordCount = count; var list = products.Skip((AspNetPager1.CurrentPageIndex - 1) * AspNetPager1.PageSize) .Take(AspNetPager1.PageSize); GridView1.DataSource = list; GridView1.DataBind(); }
protected void AspNetPager1_PageChanged(object sender, EventArgs e) { Bind(); }
疑问:每次分页都会查询Category下面的所有产品,然后显示当前页的产品,不知道性能上有什么牺牲没.
5.修改数据
添加,删除和修改,用了MVC才知道它的好啊.没有对比就没有感觉啊.
页面
Page_Load事件
protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { LoadData(); NorthwindEntities db = new NorthwindEntities(); DropDownList1.DataSource = db.Categories.ToList(); DropDownList1.DataBind(); DropDownList2.DataSource = db.Suppliers.ToList(); DropDownList2.DataBind(); } }
private void LoadData() { string s = Request.QueryString["Id"]; int result = 0; if (string.IsNullOrEmpty(s)) { return; } else if (int.TryParse(s, out result)) { NorthwindEntities db = new NorthwindEntities(); Product product = (from p in db.Products where p.ProductID == result select p).SingleOrDefault(); if (product == null) { txtProductName.Text = "[未找到该产品]"; return; } txtProductID.Text = product.ProductID.ToString(); txtProductName.Text = product.ProductName.ToString(); DropDownList2.SelectedValue = product.SupplierID.ToString(); DropDownList1.SelectedValue = product.CategoryID.ToString(); txtUnitQuantity.Text = product.QuantityPerUnit.ToString(); txtUnitPrice.Text = product.UnitPrice.ToString(); txtStockQuantity.Text = product.UnitsInStock.ToString(); txtOrderUnit.Text = product.UnitsOnOrder.ToString(); txtReorderUnit.Text = product.ReorderLevel.ToString(); CheckBox1.Checked = product.Discontinued; db.Dispose(); } }
更新,JavaScript的提示
protected void btnUpdate_Click(object sender, EventArgs e) { NorthwindEntities db = new NorthwindEntities(); int id = Convert.ToInt32(txtProductID.Text); var product = (from p in db.Products where p.ProductID == id select p).SingleOrDefault(); if (product == null) { Page.ClientScript.RegisterStartupScript(this.GetType(), "showmessage", "<script>alert('数据库中不存在此id所对应的商品,不能更新')</script>"); return; } product.ProductName = txtProductName.Text; product.SupplierID = Int32.Parse(DropDownList2.SelectedValue); product.CategoryID = Int32.Parse(DropDownList1.SelectedValue); product.QuantityPerUnit = txtUnitQuantity.Text; product.UnitPrice = decimal.Parse(txtUnitPrice.Text); product.UnitsInStock = short.Parse(txtStockQuantity.Text); product.UnitsOnOrder = short.Parse(txtOrderUnit.Text); product.ReorderLevel = short.Parse(txtReorderUnit.Text); product.Discontinued = CheckBox1.Checked; db.SaveChanges(); db.Dispose(); Page.ClientScript.RegisterStartupScript(this.GetType(), "showmessage", "<script>alert('商品信息已经保存到数据库')</script>"); }
删除
protected void btnDelete_Click(object sender, EventArgs e) { NorthwindEntities db = new NorthwindEntities(); int id = Convert.ToInt32(txtProductID.Text); var product = (from p in db.Products where p.ProductID == id select p).SingleOrDefault(); if (product == null) { Page.ClientScript.RegisterStartupScript(this.GetType(), "showmessage", "<script>alert('数据库中不存在此id所对应的商品,不能删除')</script>"); return; } db.DeleteObject(product); db.SaveChanges(); db.Dispose(); Page.ClientScript.RegisterStartupScript(this.GetType(), "showmessage", "<script>alert('商品已经从数据库中删除')</script>"); }
添加
protected void btnAdd_Click(object sender, EventArgs e) { Product product = new Product(); product.ProductID = int.Parse(txtProductID.Text); product.ProductName = txtProductName.Text; product.SupplierID = Int32.Parse(DropDownList2.SelectedValue); product.CategoryID = int.Parse(DropDownList1.SelectedValue); product.QuantityPerUnit = txtUnitQuantity.Text; product.UnitPrice = short.Parse(txtUnitPrice.Text); product.UnitsInStock = short.Parse(txtStockQuantity.Text); product.UnitsOnOrder = short.Parse(txtOrderUnit.Text); product.ReorderLevel = short.Parse(txtReorderUnit.Text); product.Discontinued = CheckBox1.Checked; NorthwindEntities db = new NorthwindEntities(); db.Products.AddObject(product); db.SaveChanges(); db.Dispose(); Page.ClientScript.RegisterStartupScript(this.GetType(), "showmessage", "<script>alert('新商品信息已经添加到数据库')</script>"); }
深入理解实体框架
实体框架封装了对象映射,状态查询,连接管理,SQL语句生成等众多功能.
1.对象上下文ObjectContext
对象上下文ObjectContext类是以对象的形式与数据进行交互的主要类.ObjectContext类的实例封装了以下内容:
- 到数据库的连接,以EntityConnection对象的形式封装
- 描述模型的元数据,以MetadataWorkspace对象的形式封装
- 在创建,更新和删除操作过程中跟踪对象的ObjectStateManager对象
在前面例子中NorthwindEntities类就是一个ObjectContext对象上下文.一个实体对象只有在对象上下文中时,才能被更新,删除和修改.在修改例子中,先对商品实体类Product类的实例进行修改或者删除,
最后在调用对象下行文类NorthwindEntities的SaveChanges方法将修改保存到数据库中.
实体对象既可以在一个对象的上下文,也可以不在,这两种状态可以相互转换.将一个孤立的实体对象添加到对象上下文称为附加,反之,使一个对象上下文中的实体对象脱离对象上下文称为分离.
AddObject | 数据源不存在的新对象 |
Attach | 对象已存在于数据源,但是尚未附加到上下文时 |
AttachTo | 将对象附加到对象上下文中特定的实体集 |
2.对象状态和对象修改
学习中...