接上篇:
下面我们就来创建DataContext
我们之前说过:DataContext和我们之前谈的SqlHelper类来类比,它们都是封装了数据操作的细节。我们在V2.PaidTimeOffDAL项目上右击,选择"添加新项",如下:
![paidtimeoffdal.JPG](https://i-blog.csdnimg.cn/blog_migrate/af90f4ce03b88f60adb6e76624be5912.jpeg)
VS2008自动的添加System.Data.Linq的引用,同时也添加了三个文件:HRPaidTimeOff.dbml, RPaidTimeOff.dbml.layout和HRPaidTimeOff.designer.cs.其中HRPaidTimeOff.dbml, RPaidTimeOff.dbml.layout将会被ORM图形设计器所使用,.cs文件包含了所有自动创建的类。
大家可以双击"HRPaidTimeOff.designer.cs"文件,我们就会看见VS2008创建的分部类HRPaidTimeOffDataContext,这个类继承自System.Data.Linq.DataContext。我们可以用这个类来和数据打交道,就想我们之前使用SqlHelper类一样。这个类有一个名为MappingSource的变量,和一些构造函数。其中构造函数有的采用一个连接字符串为参数,有的用一个实现了IDBConnection接口的类为参数,然后所有的构造函数都调用了OnCreated方法。
打开折叠区域--"Extensibility Method Definitions",就可以看到下面的方法
这是VS2008的一个新的语法:分部方法,大家可以和之前的partial class类比。这个方法允许你在类中先定义方法,然后在该类的其他partial类中实现方法体。如果我们的partial 方法没有实现,那么及时你调用了这个方法,也不会出错,因为编译器会忽略这个方法的调用。
下面,我们双击"HRPaidTimeOff.dbml",就可以看到图形化的ORM设计器。然后,我们展开"服务器资源管理器"窗口,然后把PaidTimeOff数据库总的ENTUserAccount拖到设计器的左边。如下:
此时,VS2008就直接创建一个实体类来映射ENTUserAccount表,再次打开"HRPaidTimeOff.designer.cs"文件,我们可以看到这个文件和我们之前看到的就不同了,我们首先可以看到下面的Attribute被添加了:
而且在"Extensibility Method Definitions:"很多新的方法也添加了:
partial void UpdateENTUserAccount(ENTUserAccount instance);
partial void DeleteENTUserAccount(ENTUserAccount instance);
而且,一个新的构造函数也添加了,这个函数采用一个连接字符串为参数和一个映射源为参数:
{
OnCreated();
}
除此之外,还有一些类添加了,在解决方案窗口,我们可以看到Settings.settings文件和Settings.Designer.cs文件,打开Settings.Designer.cs文件,我们可以看到这个类是继承自global::System.Configuration.ApplicationSettingsBase,有一个属性很重要,就是HRPaidTimeOffConnectionString,这属性返回数据库连接字符串 ,所以在默认情况下,我们之前建立的HRPaidTimeOffDataContext的数据库连接字符串在Settings文件中指定。
我们再回到HRPaidTimeOffDataContext类,我们可以找到一个ENTUserAccount的类,它就是数据库表的一个映射类。
我们只要了解这么多就行了,下面就通过例子的使用来了解其他的知识。
添加数据记录
编译V2.PaidTimeOffDAL,我们在我们解决方案的那个网站项目这引用V2.PaidTimeOffDAL.dll,我们这里只是临时的使用以下V2.PaidTimeOffDAL.dll而以,大家知道UI层不能直接和DAL打交道的,我们这里暂时的使用,使得大家对Linq更加的熟悉一点。
大家跟着做:
1.在网站项目中添加System.Data.Linq引用。
2.在Default.aspx页面添加一个按钮,ID为btnInsert,Text为Insert
3.在Default.aspx.cs添加V2.PaidTimeOffDAL引用。
4.在Insert按钮点击事件下,添加下面代码:
protected void btnInsert_Click(object sender, EventArgs e)
{
//创建DataContext实例
HRPaidTimeOffDataContext db = new HRPaidTimeOffDataContext();
//创建新的ENTUserAccount 对象,并且设计属性
ENTUserAccount userAccount = new ENTUserAccount
{
WindowsAccountName = @"VARALLO1\VaralloMadison",
FirstName = "Madison",
LastName = "Varallo",
Email = "madison.varallo@v2.com",
IsActive = true,
InsertDate = DateTime.Now,
InsertENTUserAccountId = 1,
UpdateDate = DateTime.Now,
UpdateENTUserAccountId = 1
};
//传入数据
db.ENTUserAccounts.InsertOnSubmit(userAccount);
//保存到数据库
db.SubmitChanges();
}
上面的代码其实和我们之前使用ADO.NET的形式很接近。首先我们创建一个连接实例HRPaidTimeOffDataContext和数据库连接,然后就是创建一条数据,然后调用InsertOnSubmit方法插入数据,最后就保存数据,db.SubmitChanges()把数据保存到数据库中。
更新数据
在Default.aspx页面中再添加一个按钮,ID为btnUpdate,Text为Update,按钮事件代码如下:
![ContractedBlock.gif](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
![ExpandedBlockStart.gif](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
protected void btnUpdate_Click(object sender, EventArgs e)
{
HRPaidTimeOffDataContext db = new HRPaidTimeOffDataContext();
ENTUserAccount userAccount = db.ENTUserAccounts.Single(u =>
u.WindowsAccountName == @"VARALLO1\VaralloMadison");
userAccount.IsActive = false;
db.SubmitChanges();
}
这个段代码和之前的差不多,首先还是实例化一个数据库连接的对象HRPaidTimeOffDataContext ,然后用Lamdal表达式选出一条数据,然后更改IsActive =false;最后保存回数据库中。
大家可以看到,使用Linq以后,我们再也没有用ADO.NET的语句了,而且我们写代码的方式也完全改变。
我们从更新数据的例子中看到,我们首先从数据库中获取一条数据,然后改变数据,最后再次保存回数据库。对于Web程序来说用,我们通常从数据库中获取一条数据,显示在界面上,然后用户更改数据,然后点击按钮回传到服务器,那么数据就传到了我们的DAL,DAL取执行更新操作。例如,我们把一个用户的信息全部取出了,包含FirstName,LastName,Email,IsActive等等,那么我们的界面上面就显示出来这些数据,我们假设是用TextBox来显示的,那么当我们更改了其中一个数据,如IsActive,那么我们就点击按钮,我们此时其实就要把界面上面的所有信息,包括FirstName,LastName,WindowAccountName,Email等全部返回到服务器(大家想想我们的一般更新的存储过程是怎样写的,就明白了),因为如果我们只是返回一个IsActive,服务器端根本不知道更新那条数据,此时我们在服务器端就应该写下面的代码:
![ContractedBlock.gif](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
![ExpandedBlockStart.gif](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
protected void btnUpdate_Click(object sender, EventArgs e)
{
//Create an ENTUserAccount object and set the properties
ENTUserAccount userAccount = new ENTUserAccount
{
ENTUserAccountId = 2,
WindowsAccountName = @"VARALLO1\VaralloMadison",
FirstName = "Madison",
LastName = "Varallo",
Email = "madison.varallo@v2.com",
IsActive = false,
UpdateDate = DateTime.Now,
UpdateENTUserAccountId = 1
};
HRPaidTimeOffDataContext db = new HRPaidTimeOffDataContext();
db.ENTUserAccounts.Attach(userAccount, true);
db.SubmitChanges();
}
这段代码和之前一样,也是把IsActive设置为false,但是我们调用的是Attach方法,而且把第二个参数设置为true,说明我们要采用更新操作。
如果运行代码,会报错的:
An entity can only be attached as modified without original state if it declares a version member or does not have an update check policy.
意思是说,我们回传的那条记录在数据库中不存在。
这里还有一个问题要说明:我们确实是从数据库中获取一条数据,然后显示在界面上,我们更新数据后,数据要取数据库中更新,那么数据库怎么知道现在我们传来的数据就是之前从数据库中取出的那条呢?
所以我们就需要一个标识或者说是时间戳,来表明我们现在回传的数据确实属于数据库,那么我们就修改我们的数据表ENTUserAccount,添加一个新的字段Version,类型为timespan。所以当我们取数据的时候,我们就把数据的版本Version字段保存在ViewState中,然后更新数据之后,我们把这个字段一起返回,数据库就检查我们传回的Version字段是否和我们要更新的数据记录的Version是否相同,如果相同就更新数据,如下代码:
Page_Load事件中把Version保存:
![ContractedBlock.gif](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
![ExpandedBlockStart.gif](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
protected void Page_Load(object sender, EventArgs e)
{
HRPaidTimeOffDataContext db = new HRPaidTimeOffDataContext();
ENTUserAccount userAccount = db.ENTUserAccounts.Single(ua =>
ua.WindowsAccountName == @"VARALLO1\VaralloMadison");
ViewState["ENTUserAccountId"] = userAccount.ENTUserAccountId;
ViewState["InsertENTUserAccountId"] =userAccount.InsertENTUserAccountId;
ViewState["InsertDate"] = userAccount.InsertDate;
ViewState["Version"] = userAccount.Version;
}
在btnUpdate按钮事件中,如下:
![ContractedBlock.gif](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
![ExpandedBlockStart.gif](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
protected void btnUpdate_Click(object sender, EventArgs e)
{
//Create an ENTUserAccount object and set the properties
ENTUserAccount userAccount = new ENTUserAccount
{
WindowsAccountName = @"VARALLO1\VaralloMadison",
FirstName = "Madison",
LastName = "Varallo",
Email = "madison.varallo@v2.com",
IsActive = false,
UpdateDate = DateTime.Now,
UpdateENTUserAccountId = 1
};
userAccount.ENTUserAccountId =Convert.ToInt32(ViewState["ENTUserAccountId"]);
userAccount.InsertENTUserAccountId =
Convert.ToInt32(ViewState["InsertENTUserAccountId"]);
userAccount.InsertDate =Convert.ToDateTime(ViewState["InsertDate"]);
userAccount.Version = (Binary)ViewState["Version"];
HRPaidTimeOffDataContext db = new HRPaidTimeOffDataContext();
db.ENTUserAccounts.Attach(userAccount, true);
db.SubmitChanges();
}
这样就OK了。如果大家有什么问题,就留言!
删除记录:
![ContractedBlock.gif](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
![ExpandedBlockStart.gif](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
protected void btnDelete_Click(object sender, EventArgs e)
{
HRPaidTimeOffDataContext db = new HRPaidTimeOffDataContext();
//Create an ENTUserAccount object
ENTUserAccount userAccount = new ENTUserAccount();
userAccount.ENTUserAccountId =
Convert.ToInt32(ViewState["ENTUserAccountId"]);
userAccount.Version = (Binary)ViewState["Version"];
db.ENTUserAccounts.Attach(userAccount);
db.ENTUserAccounts.DeleteOnSubmit(userAccount);
db.SubmitChanges();
}
方法DeleteOnSubmit是自动生成的。我们也可以定制。我们后面谈。
查询数据:
我们可以根据条件查询,然后绑定:
![ContractedBlock.gif](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
![ExpandedBlockStart.gif](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
protected void btnSelect_Click(object sender, EventArgs e)
{
HRPaidTimeOffDataContext db = new HRPaidTimeOffDataContext();
var userAccounts =
from u in db.ENTUserAccounts
select u;
GridView1.DataSource = userAccounts;
GridView1.DataBind();
}