1

我是开发新手,并尝试使用 Entity Framework 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 上的控件,并有一种方法来保存记录,而无需根据状态手动分配值(query.FirstName = TextBoxFirstName.Text 等)?

我已经看过 EntityDataSource,但我认为除了最简单的事情之外,这不是一个好的选择。

谁能告诉我我正在做的事情是否可以或提供更好的示例或指导?

非常感谢您的帮助!

4

4 回答 4

1

Best approach, IMHO, is that when you're retrieving data to DISPLAY only, do it without change-tracking. This will avoid performance issues. So, use the AsNoTracking method to avoid change-tracking proxies.

Than, for the update, you should load WITH change-tracking enabled, that's why there is no call to AsNoTracking on the save part.

Remember to check for null values. You're using the FirstOrDefault but since you're using the primary key, there won't be a second record, so just using SingleOrDefault. But since default (null) can happen, check for null values.

Also, use lambda expressions. They are not so easy to get with at first but you'll get used with minor effort and they will simplify your code a lot.

But from your question, there are some workarounds to avoid this but they are not the best approach. You should avoid long-living entities and prefer ViewModels for long-living objects, with the UnitOfWork pattern in mind for the repository and persistent entities.

If you really want that, you can Detach your entity from the context, use it everywhere and when you're ready, Attach it back and set it's state to Modified. For this, take a look here: http://msdn.microsoft.com/en-us/library/bb896271.aspx

On your case, I'd suggest this:

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();
      }
   }
}
于 2013-09-26T23:47:40.497 回答
0

尝试使用

      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 对象,在这种情况下你必须声明字段,因为它们将具有相同的名称,只是一个尽管)

于 2014-02-27T00:52:12.677 回答
0

这也是我的做法。有必要检索您要更新的对象。

于 2013-09-26T23:03:12.760 回答
0

“让这两种方法分别查询数据库以检索和更新记录对我来说似乎很愚蠢”

你是绝对正确的不要重复自己应该是​​你应该努力遵循的口头禅 原则。

在这里,您选择在页面加载事件中检索数据并在按钮单击事件中再次检索它。这两个事件都发生在同一个网页实例中。您可以将其存储在实例变量中并在单击按钮时重新使用它,或者您可以为“延迟加载”的实体设置一个属性。有各种各样的方法。延迟加载在这里可能肯定是矫枉过正,因为您可能只会使用 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 事件中设置 TextBoxes。这意味着当您到达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你会想要停止重复自己的不同类型。

那是您应该开始使用“泛型”的时候。您将PeopleRepositoryand the替换为MantraRepositorya Repository<People>and aRepository<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 将返回处于已添加状态的实体。也就是说,Find 将返回已添加到上下文但尚未保存到数据库的实体。”

现在,如果您想进行更改,并且因为您没有在任何地方重复自己,您只需更改 GetPerson 方法中的代码。

PS 当您最终实现该通用存储库时,获取记录的代码可能看起来像这样。

T e = Context.Set<T>().Find(id)

一条线就可以全部搞定

于 2013-09-27T10:01:48.093 回答