我正在考虑如何在.net项目的经典3层架构中使用Linq。显然,Linq to SQL应该出现在Data层中。我选择Linq的原因是因为它会比使用存储过程节省我很多时间。我在线搜索了Linq的插入/更新/删除方法,但没有找到使用实体进行记录更新的合适方法。通常,人们会使用这种方式进行更新:
public void UpdateUser(String username, String password, int userId)
{
using (var db = new UserDataContext()){
var user = db.user.Single(p => p.Id = userId);
user.Username = username;
user.Password = password;
db.SubmitChanges();
}
}
为什么我们不使用实体传递这样的记录:
public void Update(Application info)
{
VettingDataContext dc = new VettingDataContext(_connString);
var query = (from a in dc.Applications
where a.Id==info.Id
select a).First();
query = info;
try{
dc.SubmitChanges();
}
catch(Exception e){
//...
}
}
但不幸的是,由于“query = info”,上面的代码是错误的,但如果我将“info”中的每个值分配给“query”,它就可以正常工作。喜欢
query.firstName=info.firstName;
query.lastName=info.lastName;
因此,如果此表有40个字段,我必须编写40行代码。有没有更简单的方法来进行更新?希望我能清楚地描述这个问题。
答案 0 :(得分:1)
考虑数据存储库实际需要执行更新的内容。它不需要包含这些更改的对象,而是需要对哪些更改进行描述。这可以很容易地封装到回调委托中......
public void UpdateUser(int userId, Action<User> callback)
{
using (var db = new DataContext())
{
User entity = db.Users.Where(u => u.Id == userId).Single();
callback(entity);
db.SubmitChanges();
}
}
myrepository.UpdateUser(userId, user =>
{
user.Username = username;
user.Password = password;
// etc...
});
答案 1 :(得分:1)
添加另一个答案作为评论不足以扩展我以前的答案。
让我们退一步,从逻辑的角度看看你想做什么。您想要告诉您的数据访问层如何更新数据库,以及需要写入的所有新/更改值。
这样做的一种非常常见的方法是传递具有这些更改的实体(这是您在示例中所做的)。正如您所看到的,这可能会变得棘手,因为如果您只是用更改的实体覆盖实体变量,Linq2Sql将丢失更改跟踪...只是因为新实体被分配给同一个变量,并不意味着Linq2Sql自动从新对象中获取更改...实际上Linq2Sql根本不了解新对象...
示例:
// In domain layer:
MyEntity entity = new MyEntity();
entity.PrimaryKey = 10;
entity.Name = "Toby Larone";
entity.Age = 27;
myDataRepository.Update(entity);
// In data layer:
void Update(MyEntity changedEntity)
{
using (var db = new DataContext())
{
var entity = (from e in db.MyEntities
where e.PrimaryKey == changedEntity.PrimaryKey
select e).First();
// Linq2Sql now has change tracking of "entity"... any changes made will be persisted when SubmitChanges is called...
entity = changedEntity;
// Linq2Sql does **not** have change tracking of changedEntity - the fact that it has been assigned to the same variable that once stored a tracked entity does not mean that Linq2Sql will magically pick up the changes...
db.SubmitChanges(); // Nothing happens - as far as Linq2Sql is concerned, the entity that was selected in the first query has not been changed (only the variable in this scope has been changed to reference a different entity).
}
}
现在您已经看到将每个字段分配给实体而不是替换它按预期工作 - 这是因为正在对原始实体进行更改,原始实体仍在Linq2Sql更改跟踪系统中。
这个问题的一个可能的解决方案是编写一个方法,将另一个实体的更改“应用”到现有实体,即:
partial class MyEntity
{
void ApplyChanges(MyEntity changedEntity)
{
this.PrimaryKey = changeEntity.PrimaryKey;
this.Name = changedEntity.Name;
this.Age = changedEntity.Age;
}
}
然后您的数据访问将如下所示:
// In data layer:
void Update(MyEntity changedEntity)
{
using (var db = new DataContext())
{
var entity = (from e in db.MyEntities
where e.PrimaryKey == changedEntity.PrimaryKey
select e).First();
// Linq2Sql now has change tracking of "entity"... any changes made will be persisted when SubmitChanges is called...
entity.ApplyChanges(changedEntity);
db.SubmitChanges(); // Works OK...
}
}
但是我确定你不喜欢这个解决方案 - 因为你所做的只是将重复的字段赋值从存储库中移出并进入Entity类本身......
回到逻辑视角 - 您真正需要做的就是告诉数据访问存储库2件事情 - 1)您想要更新的记录和2)更改的内容。发送一个包含这两个要求的全新实体并不是实现这一目标的必要条件,事实上我认为效率非常低。
在以下示例中,您将发送数据存储库仅更改,而不是整个实体。因为没有实体,没有变化跟踪问题可以解决
示例:
// In domain layer:
myDataRepository.Update(10, entity =>
{
entity.Name = "Toby Larone";
entity.Age = 27;
});
// In data layer:
void Update(int primaryKey, Action<MyEntity> callback)
{
using (var db = new DataContext())
{
var entity = (from e in db.MyEntities
where e.PrimaryKey == primaryKey
select e).First();
// Linq2Sql now has change tracking of "entity"... any changes made will be persisted when SubmitChanges is called...
// The changes that were sent are being applied directly to the Linq2Sql entity, which is already under change tracking...
callback(entity);
db.SubmitChanges();
}
}
在前面的示例中,字段分配发生了两次 - 一次是在描述了您想要进行的更改时,另一次是在需要将这些更改应用于Linq2Sql更改跟踪实体时在数据存储库中。
使用回调,字段分配只发生一次 - 更改本身的描述是更新被跟踪实体的内容。
我希望我能够很好地解释这一点:)
答案 2 :(得分:0)
query
与info
的类型不同。它们可能具有相同的属性,但代码不知道。
现在,如果你想避免编写一堆不必要的代码,你可以使用像AutoMapper这样的第三方库来为你做这件事。