灵魂腐蚀 发表于 2015-1-18 11:15:23

ASP.NET网站制作之EntityFramework蜻蜓点水之CRUD(下)仓酷云

它有很多缺点的,有兴趣可以到网上去搜索一下。于是微软有发明了“下一代”C++:C++/CLI语言,这个可以解决在.NETFramework中,托管C++产生的问题。在《程序员》杂志上,lippman和李建中合作连载介绍了C++/CLI语言。我在EntityFramework系列文章的CRUD上篇中先容了EF的数据查询,中篇谈到了EF的数据更新,下篇则聊聊EF完成CRUD的外部道理。
跟踪实体对象形态

在CRUD上篇和中篇谈到,为了完成提取和更新数据的功效,EF必需利用某种机制来跟踪实体对象,以便根据对象以后形态天生响应的SQL命令。
这里的关头是辨别分明内存中的数据实体对象和数据库中的纪录。
当程序运转时,位于内存中的EF数据实体能够处于以下五种形态之一:
1.Added:实体对象是新创立的,数据库中没有响应的纪录。
2.Unchanged:从数据库加载到内存后,实体对象属性值没有任何改动。
3.Modified:最少有一个实体对象属性值被改动。
4.Deleted:假如用户从实体对象汇合中删除某实体对象,则它将处于此形态。注重数据库中此对象响应的纪录还存在。
5.Detached:此实体对象未被EF所跟踪,属“黑户”和“盲流”。居于这类形态的对象多呈现在具有散布式多层架构的体系中,在后一篇谈到EF数据存取层计划的文章中我将对此再做剖析。
如今,一个风趣的成绩呈现了:
当EF从数据库中提取一笔记录天生一个实体对象以后,使用程序能够针对它的操纵太多了,EF是怎样晓得哪一个对象处于哪一个形态的?
EF的办理计划是:
为以后一切必要跟踪的实体对象,创立一个响应的DbEntityEntry对象,此对象包涵实在体对象每一个属性的三个值:
1.CurrentValue:以后值
2.OriginalValue:原始值,就是从数据库中刚提掏出来的值
3.DatabaseValue:数据库中对应纪录的对应字段的值
刚从数据库中提掏出来时,CurrentValue=OriginalValue=DatabaseValue,今后跟着程序的运转,在挪用SaveChange()办法之前,OriginalValue保持稳定,但CurrentValue极可能会变更,而DatabaseValue一样平常情形下也稳定,不外假如其他用户修正了数据库中的响应纪录,则EF供应了GetDataBaseValues办法猎取DatabaseValue的新值。
以是如今很分明了:
EF为每个必要跟踪形态的实体对象创立一个对应的DbEntityEntry对象,保留实体对象各属性的CurrentValue、OriginalValue和DatabaseValue三个值,只需对照这三个值,很简单地就晓得哪一个属性值被修正了,从而天生响应的Update命令。
关于新到场的实体,没有originalvalues和databasevalues
标志为删除的实体,没有currentvalues.
Detached的实体对象(一般是经由过程收集从客户端发送过去的),没有响应DbEntityEntry对象,因而EF没法跟踪其形态,只能先Attach它,创立好响应DbEntityEntry对象以后,才干保留或更新到数据库中。
浩瀚DbEntityEntry对象的办理由DbContext.ChangeTracker所援用的对象卖力。
从实体对象取得它所对应的DbEntityEntry对象很复杂,利用以下代码便可:
DbEntityEntryentry=DbContext对象.Entry(实体对象援用);
以下这个示例办法提取并输入指定实体对象属性的一切值:
privatestaticvoidPrintChangeTrackingInfo(DbContextcontext,DbEntityEntryentry)
{
//entry.Entity援用相干联的实体对象
Console.WriteLine(entry.Entity);
//entry.State值唆使实体对象以后所处的形态,即后面所述几种形态之一
Console.WriteLine("State:{0}",entry.State);
Console.WriteLine("nCurrentValues:");
PrintPropertyValues(entry.CurrentValues);
Console.WriteLine("nOriginalValues:");
PrintPropertyValues(entry.OriginalValues);
Console.WriteLine("nDatabaseValues:");
PrintPropertyValues(entry.GetDatabaseValues());
}
DbEntityEntry对象的CurrentValues/OriginalValues属性是一个DbPropertyValues范例的汇合对象,以下办法输入其每一个成员的值:

privatestaticvoidPrintPropertyValues(DbPropertyValuesvalues)
{
foreach(varpropertyNameinvalues.PropertyNames)
{
Console.WriteLine("-{0}:{1}",propertyName,values);
}
}
处置并发抵触

在Web这类高并发的运转情况中,一个用户修正另外一个用户正在处置的数据是很简单呈现的场景,当这类抵触呈现时,EF没法更新数据库中的数据。
举例申明,假定我们有以下实体类:
publicclassPerson
{
publicintPersonId{get;set;}
publicstringName{get;set;}
publicintage{get;set;}
publicstringDescription{get;set;}
}
上面来看第一种情形:要修正的纪录己被他人修正。
以下代码摹拟了这个场景:
Taskt1=newTask(()=>
{
using(varcontext=newMyDbContext())
{
Personp=context.People.First();
p.Description="DescriptionModifiedat"+DateTime.Now.ToShortTimeString();
context.SaveChanges();
}
});
Taskt2=newTask(()=>
{
using(varcontext=newMyDbContext())
{
Personp=context.People.First();
p.age*=2;
context.SaveChanges();
}
});
t1.Start();
t2.Start();
Task.WaitAll(t1,t2);
实验的了局是:当统一笔记录被甲乙两人同时修正时,假如两人修正分歧的字段,则每一个字段都能够失掉新值。
以下是利用SQLServerProfiler截获的EF发往SQLServer数据库的SQL命令:

execsp_executesqlNUPDATE.
SET=@0
WHERE(=@1)
,N@0nvarchar(max),@1int,@0=NDescriptionModifiedat18:15,@1=11
execsp_executesqlNUPDATE.
SET=@0
WHERE(=@1)
,N@0int,@1int,@0=320,@1=11

能够很分明地看到:EF能根据修正的实体属性名天生响应的Update命令,从而在内部看来,这相称于“兼并”了甲乙两人的修正。
我们能够下手修正上述实验代码,很简单失掉下述的另外一个了局:
假如甲乙两人修正的是不异的字段,则究竟谁成功,取决于谁收回的SQL命令是最初实行的,即“厥后者居上”。
第二种情形:要修正的纪录己被其别人删除
这类情形是不是呈现,取决于数据库先实行哪一个EF收回的Update和Delete命令:UpdateFirstorDeleteFirst。


[*]假如是先Delete后Update,则EF会抛出:DbUpdateConcurrencyException。其给出的信息为:
Storeupdate,insert,ordeletestatementaffectedanunexpectednumberofrows(0).Entitiesmayhavebeenmodifiedordeletedsinceentitieswereloaded.RefreshObjectStateManagerentries.


[*]假如是先Update后Delete,则不会有任何非常呈现,实在数据己被删除!但因为EF没有抛出任何非常,以是提交数据更新哀求的人对此一窍不通,他还觉得更新乐成了!这其实是不太妙的事!
以是,程序抛出非常是件功德,别惧怕非常,要感激它,它能让我们晓得出错了,知错能改,就是好同道!
话又说返来,应当怎样凑合上述的情形呢?
有两个办法。
先来看第一个办法:指定实体对象的某属性用于并发检测。
publicclassPerson2
{
publicintPerson2Id{get;set;}
publicstringName{get;set;}
publicintage{get;set;}

publicstringDescription{get;set;}
}
上述代码是接纳CodeFirst的EF代码,假如接纳DatabaseFirst体例,则可在实体计划器中设置响应属性的ConcurrencyMode属性值为Fixed。
如许一来,上面实验更新age属性的代码,

Person2p=context.People2.First();
p.age*=2;
context.SaveChanges();

将天生纷歧样的SQL命令:

execsp_executesqlNUPDATE.
SET=@0
WHERE((=@1)AND(=@2))
,N@0int,@1int,@2nvarchar(max),@0=80,@1=1,@2=NDescriptionModifiedat18:49

能够看到,加了的属性名和值将呈现在Where子句中。
这就是关头地点了:
只需给实体类指定一个或多个并发抵触属性(使用),EF就会把它们作为Where子句的前提到场到天生的SQL命令中,假如Update命令前往了局为0,那一定是堕落了,由于原始纪录给他人改了。
这类体例必要在实体类中指定特定的属性作为并发抵触检测根据,假如项目中实体类良多,并且程序必要运转于高并发的情况中,为每一个实体类都独自地设定其实太贫苦了。这时候,数据库跑来协助了。
很多数据库体系撑持界说一种独一标识整笔记录的特别字段,当本纪录的任一其他字段值有变化时,这一特别字段即刻就会有一个分歧的值。这一字段的值是由数据库天生并保护的,使用程序不要显式设置它。
在EF中,我们能够如许为指定实体类指定一个特别属性:
publicclassPerson3
{
publicintPerson3Id{get;set;}
publicstringName{get;set;}
publicintage{get;set;}
publicstringDescription{get;set;}

publicbyte[]RowVersion{get;set;}
}
在SQLServer中响应的字段范例为timestamp。
增加如许的一个字段以后,在Update数据时,假如纪录被别人所修正,则EF将老是抛出DbUpdateConcurrencyException,让用户晓得无数据抵触产生,从而用户能接纳响应举动以包管数据平安牢靠。
假如有多个实体类都但愿撑持并发抵触检测,能够设定一个实体基类,以下所示:
publicclassEntityBase
{

publicbyte[]RowVersion{get;set;}
}
让一切相干实体类都派生自它便可。这是一个偷懒的办法,却很好用。
事件处置

默许情形下,当EF挪用SaveChanges()时,会把天生的一切SQL命令“包”到一个“事件(transaction)”中,只需有一个数据更新操纵失利,全部事件将回滚。
在多半情形下,假如你总在数据更新操纵代码中利用一个而不是多个DbContext对象,而且只是在最初挪用一次SaveChanges(),那末EF的默许事件处置机制己经够用了,无需做分外的事变。
但是,假如呈现以下的情况,你就必需显式地处置事件了。
第一种情形:你必要分阶段地保留数据,因此必要屡次挪用SaveChanges()大概实行修正数据库的SQL命令。
请看以下示例代码:
using(varcontext=newMyDbContext())
{
try
{
Person3p=context.People3.First();
p.Name="newName"+(newRandom().Next(1,100));
context.SaveChanges();
context.Database.ExecuteSqlCommand("updatePerson3setDescription={0}wherePerson3Id={1}",
"DescriptionModifiedat"+DateTime.Now.ToShortTimeString(),
p.Person3Id);
p.age*=2;
context.SaveChanges();
}
catch(Exceptione)
{
Console.WriteLine(e.Message);
}

上述代码中,挪用两次SaveChanges(),另有一次实行Update命令。
假如在最初一次SaveChanges()中呈现非常,固然最初一次没乐成,但你会发明前两次数据己经保留!这就带来了数据纷歧致的成绩。
关于这类场景,你必要显式地编写事件代码了(注:以下代码合用于EF6):
using(varcontext=newMyDbContext())
{
using(vartransaction=context.Database.BeginTransaction())
{
try
{
……
context.SaveChanges();
context.Database.ExecuteSqlCommand("……);
……
context.SaveChanges();
transaction.Commit();
}
catch(Exceptione)
{
Console.WriteLine(e.Message);
transaction.Rollback();
}
}
}
出格要注重必定要挪用commit(),我测试发明,只需不Commit,即便没有非常产生,事件仍将回滚,数据库中的数据不会更新。
第2种情形,你必要利用多个DbContext保留数据。
以下是处置这类场景的典范代码:
staticvoidTestTransactionScope2()
{
using(TransactionScopescope=newTransactionScope())
{
StringconnStr=……;
using(varconn=newSqlConnection(connStr))
{
try
{
conn.Open();
using(varcontext1=newMyDbContext(conn,contextOwnsConnection:false))
{
……
context1.SaveChanges();
}
using(varcontext2=newMyDbContext(conn,contextOwnsConnection:false))
{
context2.Database.ExecuteSqlCommand(……);
context2.SaveChanges();
}
using(varcontext3=newMyDbContext2(conn,contextOwnsConnection:false))
{
……
context3.SaveChanges();
}
scope.Complete();
}
catch(Exceptione)
{
Console.WriteLine(e.ToString());
}
finally
{
conn.Close();
}
}
}
}
上述代码中有几个关头点:
(1)在机关DbContext对象时,必要把一个己翻开的数据库毗连对象传给它,而且必要指定EF在DbContext对象烧毁时不封闭数据库毗连。
为完成此目标,你的DbContext对象应当相似因而如许的,供应两个重载的机关函数:
publicclassMyDbContext2:DbContext
{
publicMyDbContext2(DbConnectionconn,boolcontextOwnsConnection):base(conn,contextOwnsConnection)
{
}
publicMyDbContext2():base()
{
}
publicDbSet<OtherEntity>OtherEntities{get;set;}
……
}
注重在代码停止时封闭毗连。
(2)假如不Commit,则一切数据将不会保留。
也许C#刚上市的时候有些抄袭net网页编程吧,但自从C#2.0上市之后,整个局面就扭转乾坤了,不但net网页编程在模仿C#,而且他从来都没能跟得上C#的脚步。

飘灵儿 发表于 2015-1-20 18:39:08

在调试JSP代码时,如果程序出错,JSP服务器会返回出错信息,并在浏览器中显示。这时,由于JSP是先被转换成Servlet后再运行的,所以,浏览器中所显示的代码出错的行数并不是JSP源代码的行数。

不帅 发表于 2015-1-21 11:44:48

可以通过在现有ASP应用程序中逐渐添加ASP.NET功能,随时增强ASP应用程序的功能。ASP.NET是一个已编译的、基于.NET的环境,可以用任何与.NET兼容的语言(包括VisualBasic.NET、C#和JScript.NET.)创作应用程序。另外,任何ASP.NET应用程序都可以使用整个.NETFramework。开发人员可以方便地获得这些技术的优点,其中包括托管的公共语言运行库环境、类型安全、继承等等。

爱飞 发表于 2015-1-25 16:43:04

我觉得什么语言,精通就好,你要做的就是比其他80%的人都厉害,你就能得到只有20%的人才能得到的高薪。

乐观 发表于 2015-2-2 21:59:50

ASP(ActiveServerPages)是Microsfot公司1996年11月推出的WEB应用程序开发技术,它既不是一种程序语言,也不是一种开发工具,而是一种技术框架,不须使用微软的产品就能编写它的代码。

分手快乐 发表于 2015-2-8 08:36:31

通过这次激烈的讨论,我从大家身上学到了太多,开阔了眼界,不管是支持我的还是骂我的,都感谢你们。

兰色精灵 发表于 2015-2-25 04:45:12

由于CGI程序每响应一个客户就会打开一个新的进程,所以,当有多个用户同时进行CGI请求的时候,服务器就会打开多个进程,这样就加重了服务器的负担,使服务器的执行效率变得越来越低下。

活着的死人 发表于 2015-3-7 16:33:18

市场决定一切,我个人从经历上觉得两者至少在很长时间内还是要共存下去,包括C和C++,至少从找工作就看得出来,总不可能大家都像所谓的时尚一样,追捧一门语言并应用它。

第二个灵魂 发表于 2015-3-15 09:28:46

Asp.net:首先来说,Asp.net和Asp没什么关系,看着像是升级版本什么的,其实没什么联系。Asp是脚本编程,用的是ASP语言,而ASP.net用的是C#语言,完全不同的东西。
页: [1]
查看完整版本: ASP.NET网站制作之EntityFramework蜻蜓点水之CRUD(下)仓酷云