LINQ(数据库操作增、删、改及并发管理)
2014-01-11 20:30
357 查看
本文将演示如何通过EntityFramework数据模型创建、修改、删除数据库记录。
可以多次AddObject()后调用一次SaveChanges()全部写入数据库。
1.创建部分加载的实体类
在之前的示例里,我们调用了Customer实体类的默认构造函数,它创建的实例没有加载任何数据。不过,我们还可以通过在构造函数里指定必须字段的值来减少数据库错误的风险。
每个实体类都有一个名为CreateT的工厂方法,例如,Customer的实体类的工厂方法是CreateCustomer。看下面示例:
我们倾向于使用默认构造函数,因为可以在一条语句里指定属性的值,但是如果你经常会忘记给必需的字段赋值,那么工厂方法对你就非常有用。
2.插入关联的实体
可以用实体类的导航属性创建一组关联的对象,然后一次把它们存储到数据库:
如果单独创建Order和Customer对象,就不得不显式的添加Order:
Single():返回序列的唯一元素;如果该序列并非恰好包含一个元素,则会引发异常。
注意:EntityFramework不会删除关联的实体对象,因此调用SaveChanges()前必须小心地删除所有通过外键约束关联的对象。
乐观并发会导致痛苦的数据一致性问题。
可以让EntityFramework在更新前检查数据库是否由第三方执行了更改,虽然这还是乐观并发,因为实体对象不会锁住数据库的任何对象。但至少它可以在发生问题时给你提醒。
打开实体数据模型,选中字段,在属性中设置“并发模式”为“Fixed”,如下图:
因为在ContactName字段上做了并发检查,因此在EntityFramework更新时捕获了这个异常,最终更新并没有成功。
不过,我们除了要检查数据的差异,还是想把数据写回数据库,这时可以调用ObjectContext.Refresh()来解决这一问题,可以在捕获异常时增加这样一条代码:
Refresh():
参数一:RefreshMode枚举
参数二:要刷新的对象
RefreshMode.StoreWins表示用数据库中的值更新实体对象的值
RefreshMode.ClientWins表示用实体对象的值更新数据库中的值
让我们回顾一下这里所发生的一切。我们试图向数据库写入在其他某处已经被更新了的数据。EntityFramework检测到了并发冲突并抛出OptimisticConcurrencyException异常,让我们知道发生了问题。我们用数据库里的数据刷新修改了实体对象,它使得我们重新回到一致的状态。
但我们的更新发生了什么?嗯,什么也没有发生。如果我们一定要应用自己的修改的话,就应该使用RefreshMode.ClientWins枚举值,并再次调用SaveChanges():
这一回,就好像说“我知道有并发冲突发生了,但我也坚持我的更新”那样。为妥善的处理并发冲突时我们要指出一点:刷新实体对象时可能会有人再次修改数据,也就是说第二次调用SaveChanges()可能会引发另一个OptimisticConcurrencyException异常,为了解决这个问题,我们可以循环尝试应用更新:
插入
为了在数据库里创建新纪录,需要创建相应实体类的新实例,填充字段,把实体类加入ObjectContext派生类维护的EntityCollection,然后调用SaveChanges()写入新纪录:Customercust=newCustomer()
{
CustomerID="LAWN",
CompanyName="LawnWranglers",
ContactName="Mr.AbeHenry",
ContactTitle="Owner",
Address="1017MapleLeafWay",
City="Ft.Worth",
Region="TX",
PostalCode="76104",
Country="USA",
Phone="(800)MOW-LAWN",
Fax="(800)MOW-LAWO"
};
NorthwindEntitiesdb=newNorthwindEntities();
db.Customers.AddObject(cust);
db.SaveChanges();
可以多次AddObject()后调用一次SaveChanges()全部写入数据库。
1.创建部分加载的实体类
在之前的示例里,我们调用了Customer实体类的默认构造函数,它创建的实例没有加载任何数据。不过,我们还可以通过在构造函数里指定必须字段的值来减少数据库错误的风险。
每个实体类都有一个名为CreateT的工厂方法,例如,Customer的实体类的工厂方法是CreateCustomer。看下面示例:
Customercust=Customer.CreateCustomer("LAWN","LawnWranglers");
cust.ContactName="Mr.AbeHenry";
cust.ContactTitle="Owner";
cust.Address="1017MapleLeafWay";
cust.City="Ft.Worth";
cust.Region="TX";
cust.PostalCode="76104";
cust.Country="USA";
cust.Phone="(800)MOW-LAWN";
cust.Fax="(800)MOW-LAWO";
NorthwindEntitiesdb=newNorthwindEntities();
db.Customers.AddObject(cust);
db.SaveChanges();
我们倾向于使用默认构造函数,因为可以在一条语句里指定属性的值,但是如果你经常会忘记给必需的字段赋值,那么工厂方法对你就非常有用。
2.插入关联的实体
可以用实体类的导航属性创建一组关联的对象,然后一次把它们存储到数据库:
Customercust=newCustomer
{
CustomerID="LAWN",
CompanyName="LawnWranglers",
ContactName="Mr.AbeHenry",
ContactTitle="Owner",
Address="1017MapleLeafWay",
City="Ft.Worth",
Region="TX",
PostalCode="76104",
Country="USA",
Phone="(800)MOW-LAWN",
Fax="(800)MOW-LAWO",
Orders={
newOrder{
CustomerID="LAWN",
EmployeeID=4,
OrderDate=DateTime.Now,
RequiredDate=DateTime.Now.AddDays(7),
ShipVia=3,
Freight=newDecimal(24.66),
ShipName="LawnWranglers",
ShipAddress="1017MapleLeafWay",
ShipCity="Ft.Worth",
ShipRegion="TX",
ShipPostalCode="76104",
ShipCountry="USA"
}
}
};
NorthwindEntitiesdb=newNorthwindEntities();
db.Customers.AddObject(cust);
db.SaveChanges();
如果单独创建Order和Customer对象,就不得不显式的添加Order:
Customercust=newCustomer
{
CustomerID="LAWN",
CompanyName="LawnWranglers",
ContactName="Mr.AbeHenry",
ContactTitle="Owner",
Address="1017MapleLeafWay",
City="Ft.Worth",
Region="TX",
PostalCode="76104",
Country="USA",
Phone="(800)MOW-LAWN",
Fax="(800)MOW-LAWO",
};
Orderord=newOrder
{
CustomerID="LAWN",
EmployeeID=4,
OrderDate=DateTime.Now,
RequiredDate=DateTime.Now.AddDays(7),
ShipVia=3,
Freight=newDecimal(24.66),
ShipName="LawnWranglers",
ShipAddress="1017MapleLeafWay",
ShipCity="Ft.Worth",
ShipRegion="TX",
ShipPostalCode="76104",
ShipCountry="USA"
};
NorthwindEntitiesdb=newNorthwindEntities();
db.Customers.AddObject(cust);
db.Orders.AddObject(ord);
db.SaveChanges();
更新
更新实体类和修改对象的属性一样简单:NorthwindEntitiesdb=newNorthwindEntities();
Customercust=(fromcindb.Customers
wherec.CustomerID=="LAWN"
selectc).Single();
cust.ContactName="JohnSmith";
cust.Fax="(800)1231234";
db.SaveChanges();
Single():返回序列的唯一元素;如果该序列并非恰好包含一个元素,则会引发异常。
删除
删除也很简单:NorthwindEntitiesdb=newNorthwindEntities();
IEnumerable<Order_Detail>ods=fromoindb.Order_Details
whereo.OrderID==10248
selecto;
//对LINQ查询而返回的结果集进行处理
//要么使用Single()取出单条记录
//要么就迭代集合进行处理
foreach(Order_Detailoinods)
{
db.Order_Details.DeleteObject(o);
}
db.SaveChanges();
注意:EntityFramework不会删除关联的实体对象,因此调用SaveChanges()前必须小心地删除所有通过外键约束关联的对象。
管理并发
EntityFramework默认使用乐观并发模型,也就是说在读取数据后,它不检查是否有人修改了数据库中的数据。调用SaveChanges()时,所有待更新数据全部被写到数据库中,即使他人已经更新了有冲突的记录时也是如此。乐观并发会导致痛苦的数据一致性问题。
可以让EntityFramework在更新前检查数据库是否由第三方执行了更改,虽然这还是乐观并发,因为实体对象不会锁住数据库的任何对象。但至少它可以在发生问题时给你提醒。
打开实体数据模型,选中字段,在属性中设置“并发模式”为“Fixed”,如下图:
处理并发冲突
为实体对象启用并发冲突检查后,试图更新已经被更新过的数据时,会得到一个OptimisticConcurrencyException。为了模拟并发异常,我们使用EntityFramework执行更新,然后通过ExecuteStatementInDb方法直接执行会造成冲突的SQL语句:protectedvoidPage_Load(objectsender,EventArgse)
{
NorthwindEntitiesdb=newNorthwindEntities();
Customercust=db.Customers
.Where(c=>c.CustomerID=="LAZYK")
.Select(c=>c).First();
Response.Write(string.Format("Initialvalue{0}<br/>",cust.ContactName));
//changetherecordoutsideoftheentityframework
stringsql=string.Format(@"updateCustomerssetContactName='SamuelArthurSanders'
whereCustomerID='LAZYK'");
ExecuteStatementInDb(sql);
//modifythecustomer
cust.ContactName="JohnDoe";
//savethechanges
try
{
db.SaveChanges();
}
catch(OptimisticConcurrencyException)
{
Response.Write("Detectedconcurrencyconflict-givingup<br/>");
}
finally
{
sql=string.Format(@"selectContactNamefromCustomers
whereCustomerID='LAZYK'");
stringdbValue=GetStringFromDb(sql);
Response.Write(string.Format("Databasevalue:{0}<br/>",dbValue));
Response.Write(string.Format("Cachedvalue:{0}<br/>",cust.ContactName));
}
}
privatevoidExecuteStatementInDb(stringsql)
{
stringconStr=WebConfigurationManager.ConnectionStrings["Northwind"].ConnectionString;
SqlConnectionconn=newSqlConnection(conStr);
SqlCommandcmd=newSqlCommand(sql,conn);
try
{
conn.Open();
Response.Write("ExecuteingSqlstatementagainstdatabasewithADO.NET...<br/>");
cmd.ExecuteNonQuery();
Response.Write("Databaseupdated.<br/>");
conn.Close();
}
catch(Exceptionerr)
{
thrownewApplicationException(err.Message);
}
finally
{
conn.Close();
}
}
privatestringGetStringFromDb(stringsql)
{
stringconStr=WebConfigurationManager.ConnectionStrings["Northwind"].ConnectionString;
SqlConnectionconn=newSqlConnection(conStr);
SqlCommandcmd=newSqlCommand(sql,conn);
try
{
conn.Open();
objectobj=cmd.ExecuteScalar();
conn.Close();
returnobj.ToString();
}
catch(Exceptionerr)
{
thrownewApplicationException(err.Message);
}
finally
{
conn.Close();
}
}
因为在ContactName字段上做了并发检查,因此在EntityFramework更新时捕获了这个异常,最终更新并没有成功。
不过,我们除了要检查数据的差异,还是想把数据写回数据库,这时可以调用ObjectContext.Refresh()来解决这一问题,可以在捕获异常时增加这样一条代码:
catch(OptimisticConcurrencyException)
{
Response.Write("Detectedconcurrencyconflict-givingup<br/>");
db.Refresh(RefreshMode.StoreWins,cust);
}
Refresh():
参数一:RefreshMode枚举
参数二:要刷新的对象
RefreshMode.StoreWins表示用数据库中的值更新实体对象的值
RefreshMode.ClientWins表示用实体对象的值更新数据库中的值
让我们回顾一下这里所发生的一切。我们试图向数据库写入在其他某处已经被更新了的数据。EntityFramework检测到了并发冲突并抛出OptimisticConcurrencyException异常,让我们知道发生了问题。我们用数据库里的数据刷新修改了实体对象,它使得我们重新回到一致的状态。
但我们的更新发生了什么?嗯,什么也没有发生。如果我们一定要应用自己的修改的话,就应该使用RefreshMode.ClientWins枚举值,并再次调用SaveChanges():
catch(OptimisticConcurrencyException)
{
Response.Write("Detectedconcurrencyconflict-givingup<br/>");
db.Refresh(RefreshMode.ClientWins,cust);
db.SaveChanges();
}
这一回,就好像说“我知道有并发冲突发生了,但我也坚持我的更新”那样。为妥善的处理并发冲突时我们要指出一点:刷新实体对象时可能会有人再次修改数据,也就是说第二次调用SaveChanges()可能会引发另一个OptimisticConcurrencyException异常,为了解决这个问题,我们可以循环尝试应用更新:
//modifythecustomer
cust.ContactName="JohnDoe";
intmaxAttempts=5;
boolrecordsUpdated=false;
for(inti=0;i<maxAttempts&&!recordsUpdated;i++)
{
try
{
db.SaveChanges();
recordsUpdated=true;
}
catch(Exception)
{
db.Refresh(RefreshMode.ClientWins,cust);
}
} 原文:http://www.cnblogs.com/SkySoot/archive/2012/08/23/2652216.html
相关文章推荐
- 十步完全理解SQL
- mysql 查询当天数据
- Oracle11G+win8环境下配置PL/SQL Developer(版本7.1.5.1398)
- mysql 执行顺序 SQL语句执行顺序分析
- MySQL-SQL语句中SELECT语句的执行顺序
- 10个简单步骤,完全理解SQL
- DS5020配置集群存储
- 如何在linux下启动oracle 服务
- Windows2008R2 安装Oracle11.2.0.3 85%EM报错
- Entity Framework6使用SQL Server Compact免安装部署
- 十步完全理解SQL(转)
- 数据库插入数据之select into from与insert into select区别详解
- php防止sql注入漏洞代码 && 几种常见攻击的正则表达式
- oracle如何删除重复行
- MySQL Workbench 6 不能删除数据等问题(“Error Code: 1175”) 和入门教程
- hibernate使用getHibernateTemplate().update()出错
- win8下安装oracle11G 参考
- 这三种东西永远不要放到数据库里
- 可以下载oracle database 12C for AIX HPUX Zlinux安装体验了
- SQLite数据库详解