在 Web 应用中使用实体框架检索和更新记录
本文关键字:检索 框架 更新 新记录 实体 Web 应用 | 更新日期: 2023-09-27 18:32:54
我是开发新手,并尝试使用实体框架 5.0(数据库优先方法(检索要在 ASP.NET Web 应用程序页面上的 FormView 中显示的记录,但我不确定解决此问题的最佳方法。
为了检索记录,我使用以下代码:
protected void Page_Load(object sender, EventArgs e)
{
LoadData(int.Parse(Session["PersonID"].ToString()));
}
private void LoadData(int iPersonID)
{
using (PeopleEntities ctx = new PeopleEntities())
{
var query = (from a in ctx.People
where a.PersonID == iPersonID
select a).FirstOrDefault();
TextBoxFirstName.Text = query.FirstName;
TextBoxLastName.Text = query.LastName;
}
}
为了保存它,我使用:
protected void ButtonSave_Click(object sender, EventArgs e)
{
SaveEmployee(int.Parse(Session["PersonID"].ToString()));
}
private void SaveEmployee(int iPersonID = 0)
{
using (PeopleEntities ctx = new PeopleEntities())
{
var query = (from a in ctx.People
where a.PersonID == iPersonID
select a).FirstOrDefault();
query.FirstName = TextBoxFirstName.Text;
query.LastName = TextBoxLastName.Text;
ctx.SaveChanges();
}
}
让这两种方法分别查询数据库以检索和更新记录对我来说似乎很愚蠢,但是,同样,我是一个新手,也许我只是错过了一些东西。 有没有办法使用实体填充 FormView 上的控件,并具有保存记录的方法,而无需手动分配值(查询。FirstName = TextBoxFirstName.Text等(基于状态?
我已经看过实体数据源,但我认为除了最简单的事情之外,这不是一个好的选择。
谁能告诉我我正在做的事情是否正常或提供更好的示例或指导?
非常感谢您的帮助!
恕我直言,最好的方法是,当您将数据检索到DISPLAY时,请不要更改跟踪。这将避免性能问题。因此,请使用AsNoTracking
方法来避免更改跟踪代理。
对于更新,您应该加载启用更改跟踪的U,这就是为什么在保存部分没有调用AsNoTracking
的原因。
请记住检查空值。您使用的是 FirstOrDefault,但由于您使用的是主键,因此不会有第二条记录,因此只需使用 SingleOrDefault。但由于可能会发生默认值 (null(,请检查空值。
此外,请使用 lambda 表达式。它们一开始并不容易使用,但您只需付出一点努力就会习惯,它们会大大简化您的代码。
但是从您的问题来看,有一些解决方法可以避免这种情况,但它们不是最好的方法。您应该避免使用长期存在的实体,而对长期存在的对象首选 ViewModels,同时考虑到存储库和持久实体的 UnitOfWork 模式。
如果你真的想要它,你可以从上下文中Detach
你的实体,在任何地方使用它,当你准备好时,把它Attach
回来,并将其状态设置为 Modified
。为此,请看这里:http://msdn.microsoft.com/en-us/library/bb896271.aspx
就您而言,我建议这样做:
private void LoadData(int iPersonID)
{
using (PeopleEntities ctx = new PeopleEntities())
{
// AsNoTracking will avoid performance hit of change-tracking here...
// Since we're building-up a view, not an update case yet, you don't have to create
// proxies that will check for entity changing...
var query = ctx.People.AsNoTracking().SingleOrDefault(_people => _people.PersonID == iPersonID)
// Rendering comes into action
if (query != null)
{
TextBoxFirstName.Text = query.FirstName;
TextBoxLastName.Text = query.LastName;
}
}
}
private void SaveEmployee(int iPersonID = 0)
{
using (PeopleEntities ctx = new PeopleEntities())
{
var query = ctx.Prople.SingleOrDefault(_person => _person.PersonID == iPersonID);
if (query != null)
{
query.FirstName = TextBoxFirstName.Text;
query.LastName = TextBoxLastName.Text;
ctx.SaveChanges();
}
}
}
这也是我的做法。 有必要检索要更新的对象。
"在我看来,让这两种方法分别查询数据库似乎很愚蠢 以检索和更新记录">
你是绝对正确的 不要重复自己 应该是你应该努力遵循的口头禅原则。
在这里,您已选择在页面加载事件中检索数据,并在按钮单击事件中再次检索它。这两个事件都发生在网页的同一实例中。您可以将其存储在实例变量中,并在按钮单击中重复使用它,也可以为"延迟加载"的实体设置属性。有各种各样的方法可以做到这一点。延迟加载在这里可能肯定是矫枉过正,因为您可能只会使用 PageLoad 中的属性,应该了解何时需要访问数据库,何时不需要。
必须转到数据库以获取首次加载页面时要显示的数据。此后,当页面回发时,数据通常存在于表单值中。
更新记录时还需要转到数据库 - 在此示例中,当用户单击保存按钮时,就会发生这种情况。
下面是一个我可能不应该提到的延迟加载示例:
private People _Person;
//lazy loaded property
private People Person
{
get
{
if (_Person == null)
using (PeopleEntities ctx = new PeopleEntities())
_Person = GetPerson(ctx);
//returning a Person that isn't updateable because we've disposed of the context
return _Person;
}
}
//Retrieve an updateable person
private static object GetPerson(PeopleEntities ctx)
{
return (from a in ctx.People
where a.PersonID == int.Parse(Session["PersonID"]
select a).FirstOrDefault();
}
代码的另一个问题是,您始终根据数据库中的值设置 PageLoad 事件中的文本框。这意味着当您到达ButtonSave_Click
事件时,回发的值已被数据库中的内容覆盖,并且不会保存更改!
所以你应该这样做:
protected void Page_Load(object sender, EventArgs e)
{
if(!IsPostBack)//Only do this first time it's loaded
{
TextBoxFirstName.Text = Person.FirstName;
TextBoxLastName.Text = Person.LastName;
}
}
您的按钮单击可能如下所示:
protected void ButtonSave_Click(object sender, EventArgs e)
{
SavePerson(TextBoxFirstName.Text, TextBoxLastName.Text);
}
private SavePerson(string firstName, string lastName)
{
using (PeopleEntities ctx = new PeopleEntities())
{
var person = GetPerson(ctx);
person.FirstName = firstName;
person.LastName = lastName;
ctx.SaveChanges();
}
}
随着编码的进行,您会发现要重复SavePerson
并在其他页面上GetPerson
代码。- 那是你开始引入存储库或层的时候。不要忘记你应该努力遵循的口头禅原则,并将代码移动到另一个类,以便你可以重用它。
该类应位于PeopleRepository
或其他层中。最终你会发现PeopleRepository
中的代码看起来非常像MantraRepository
中的代码,你会想要停止对不同类型的代码重复自己。
那时你应该开始使用"泛型"。您将PeopleRepository
和MantraRepository
替换为Repository<People>
和Repository<Mantra>
,代码在一个类中定义,类似于 public class BaseRepository<T>
。
不过,在你继续这个旅程之前,还有另一件事是关于实体框架位 - 而不是
var query = (from a in ctx.People
where a.PersonID == iPersonID
select a).FirstOrDefault();
您应该/可以使用
var query = ctx.People.Find(iPersonID)
从此源:查询/查找实体
"DbSet 上的 Find 方法使用主键值来尝试查找 由上下文跟踪的实体。如果在 上下文,然后将查询发送到数据库以查找实体 那里。如果在上下文中找不到实体,则返回 Null 或 在数据库中。
查找与使用查询在两个重要方面有所不同:
仅当具有 在上下文中找不到给定键。查找将返回以下实体: 处于"已添加"状态。也就是说,Find 将返回具有 已添加到上下文中,但尚未保存到数据库中。
现在,如果您想进行该更改,并且由于您没有在任何地方重复自己,您只需更改 GetPerson 方法中的代码即可。
附言:当您最终实现该通用存储库时,用于获取记录的代码可能如下所示。
T e = Context.Set<T>().Find(id)
一行即可全部获得
尝试使用
xxx.xxx.SelectSingleOrDefault(c => c.AccountSenderID == userId_int)
替换匿名 lambda 表达式的使用(例如使用 var(
xxx.xxx.Select(c => new { c.FriendInvitationID,c.AccountSenderID,
c.Account1.AccountID, c.Account1.FirstName, c.Account1.LastName, c.Account1.Email,
c.FriendInvitationStatus, c.CreationDate })
.Where(c => c.AccountSenderID == userId_int).ToList();
即使匿名在这个意义上更加动态,您也不必描述您的对象(图像您希望检索具有同一表的两个不同引用的 JSON 对象,在这种情况下,您必须声明字段,因为它们将具有相同的名称,只是一个虽然(