2012-03-30 69 views
3

我相信大多数人会问为什么不行。我想通过询问为什么这会起作用来混淆它。实体框架 - 为什么这会起作用?

private SmokeFireDBEntities dbContext = null; 
private IList<MemberResponse> gridData = new List<MemberResponse>(); 

private void UserControl_Initialized(object sender, EventArgs e) 
{ 
    this.dbContext = new SmokeFireDBEntities(); 
    var members = from m in dbContext.Members 
       where new[] { "A", "P", "S", "J" }.Contains(m.Class.ShortName) 
       orderby m.Line 
       select m; 

    foreach (Member m in members) 
    { 
     MemberResponse mr = new MemberResponse(); 
     mr.MemberID = m.ID; 
     mr.Member = m; 
     this.gridData.Add(mr); 
    } 
    PercentageGrid.ItemsSource = this.gridData; 
} 

private void SaveButton_Click(object sender, RoutedEventArgs e) 
{ 
    AlarmTotal at = new AlarmTotal(); 

    at.Month = Convert.ToByte(this.MonthField.Text); 
    at.Year = Convert.ToInt16(this.YearField.Text); 
    at.NumAlarms = Convert.ToInt16(this.TotalAlarmsField.Text); 

    this.dbContext.AlarmTotals.AddObject(at); 
    this.dbContext.SaveChanges(); 

    // WHY IS THE FOLLOWING CODE NOT NECESSARY??? 
    //foreach (MemberResponse mr in this.PercentageGrid.Items) 
    //{ 
    // mr.AlarmTotalID = at.ID; 
    // this.dbContext.MemberResponses.AddObject(mr); 
    //} 

    //this.dbContext.SaveChanges(); 
} 

<UserControl.Resources> 
     <DataTemplate x:Key="NameColumnTemplate"> 
      <StackPanel Orientation="Horizontal"> 
       <TextBlock Text="{Binding Path=Member.LastName}" /> 
       <TextBlock Text=", " /> 
       <TextBlock Text="{Binding Path=Member.FirstName}" /> 
      </StackPanel> 
     </DataTemplate> 
     <DataTemplate x:Key="InputColumnTemplate"> 
      <StackPanel Orientation="Horizontal"> 
       <TextBox Text="{Binding Path=NumAttended}" Name="MonthResponse" Width="60" /> 
      </StackPanel> 
     </DataTemplate> 
    </UserControl.Resources> 

    <Grid Background="WhiteSmoke" Height="353" Width="509"> 
     <TextBox Height="23" HorizontalAlignment="Left" Margin="12,33,0,0" Name="MonthField" VerticalAlignment="Top" Width="75" /> 
     <TextBox Height="23" HorizontalAlignment="Left" Margin="93,33,0,0" Name="YearField" VerticalAlignment="Top" Width="59" /> 
     <TextBox Height="23" HorizontalAlignment="Left" Margin="158,33,0,0" Name="TotalAlarmsField" VerticalAlignment="Top" Width="115" /> 

     <ListView Margin="1,67,0,0" Name="PercentageGrid" ItemsSource="Binding" HorizontalAlignment="Stretch" Width="507" Height="286" VerticalAlignment="Stretch"> 
      <ListView.View> 
       <GridView> 
        <GridView.Columns> 
         <GridViewColumn Header="Name" CellTemplate="{StaticResource NameColumnTemplate}" /> 
         <GridViewColumn Header="Line#" DisplayMemberBinding="{Binding Path=Member.Line}" /> 
         <GridViewColumn Header="Class" DisplayMemberBinding="{Binding Path=Member.Class.ShortName}" /> 
         <GridViewColumn Header="Response" CellTemplate="{StaticResource InputColumnTemplate}" /> 
        </GridView.Columns> 
       </GridView> 
      </ListView.View> 
     </ListView> 

我已经删除了不必要的代码来缩短这一点。我对C#,.NET以及与之相关的一切都是全新的。我完全难以理解为什么这一切都可行。当我调用第一个dbContext.SaveChanges()将记录保存到“AlarmTotals”时,它也同时保存了所有“MemberResponse”记录,更令人惊讶的是填充了正确的AlarmTotals.ID字段。这个真的让我失望,我不明白这是如何运作的,看起来像是魔法。

任何洞察力和解释将不胜感激。我真的很想了解这里发生了什么。

回答

3

为了增加别人说,我想这“神奇”发生在第5行:

1 foreach (Member m in members) 
2 { 
3  MemberResponse mr = new MemberResponse(); 
4  mr.MemberID = m.ID; 
5  mr.Member = m; 
6  this.gridData.Add(mr); 
7 } 

这是导致新MemberResponse附加到当前EF ObjectContext的行(并随后导致它们保存在SaveChanges()上)。 MemberResponse.Member是EF导航属性。

但是你确定保存的MemberResponses有MemberResponse.AlarmTotalID设置是否正确?代码看起来不像。理解真正发生的最好方法是在AlarmTotalID属性设置器中放置了一个断点。

+0

是的,我确定正确的AlarmTotalID已添加到MemberResponse记录中。这令我惊讶的是比添加的记录更多,因为我没有办法让它为此获得正确的上下文。也许它只是使用最后添加的记录?我会跟进你关于断点的建议,看看我能否更好地理解正在发生的事情。谢谢 – 2012-03-30 01:38:34

+0

也许在数据库中有一个默认或触发器应用这些更改? – surfen 2012-03-30 01:40:49

+0

@NickSperling ands surfen:我同意这个答案。这是不可能的,'mr.AlarmTotalID'将被设置为你正在显示的代码中新创建的ID。您只需创建一个新的'AlarmTotal',您设置了三个标量属性(我猜这些不是FK属性),并且没有导航属性到另一个实体。没有其他实体提到新的'at'。设置AlarmTotalID必须发生在别的地方。也许断点测试会显示。 – Slauma 2012-03-30 11:37:42

2

简答:上下文和整体都是这样做的;他们跟踪所有这些东西,以便保存整个对象图的更新。

通过对象图,我指的是您正在使用的“根”对象以及您可能附加的任何相关项目,无论您是否始终意识到这是您正在做的或未做的事情。

太棒了!

编辑:我建议阅读朱莉娅莱曼的出色工作,如果你想挖掘更多的实体框架。这是一个非常大的话题,但它是值得的。她有一个名为实体框架的规范书籍,以及一个msdn专栏,博客等。

请注意,有关书籍的问题在这里并不是真正的主题,但我建议您这样做,因为您对EF似乎很陌生。

+0

太棒了。我喜欢这样的事实,即事情发生时没有太多的努力,但同时它让他们更难以理解,因为你没有看到所有的背景工作。由于我还没有将.AddObject()编辑到上下文中,所以我仍然不了解MemberResponse记录如何将其记录到数据库中。我将更多地阅读它并再次通过我的代码。感谢您的回复和本书建议 – 2012-03-30 01:35:04

4

首先,您的数据上下文未关闭,如果您未关闭数据库上下文连接,则您将泄漏内存/带宽的TON。请考虑一下。

其次,方法.SaveChanges()将根据数据库设置确定要分配的正确标识。如果没有定义,例如auto-increment那么可能这些ID不会被正确设置,并且您可能会保存到相同的ID然后抛出异常。孩子们的外键只能通过你已经在做的明确的联系来分配。

编辑:

在回答您的意见,通常using语句用于管理上下文,因为它是干净的代码:

var members = new Members(); 
using(var context = new SmokeFireDBEntities()) 
{ 
//use context how you would, i.e. 
members = from m in context.Members 
      where new[] { "A", "P", "S", "J" }.Contains(m.Class.ShortName) 
      orderby m.Line 
      select m; 
}//once this is hit the context is closed and you can feel safe about your connection 

如果这个方法不工作多长时间你想要打开连接,您也可以手动(尽管不是高度建议)自行关闭连接。

this.dbContext = new SmokeFireDBEntities(); 
var members = from m in dbContext.Members 
      where new[] { "A", "P", "S", "J" }.Contains(m.Class.ShortName) 
      orderby m.Line 
      select m; 
this.dbContext.Dispose();//this will close the connection for you, and if you need it re-opened then either call new Entities() again or use the using statement 
+0

关于需要关闭的上下文的好注意。 – 2012-03-30 00:10:09

+0

他没有发布任何处理上下文的代码并不意味着他不这样做。他在关闭表格时可能会付出代价。 – surfen 2012-03-30 00:25:01

+0

谢谢。您关闭上下文是正确的。我没有那样做。我意识到这一点的必要性,但还没有研究这方面的问题,至于如何以及在哪里做最好的地方。 – 2012-03-30 01:25:38

相关问题