我相信大多数人会问为什么事情不起作用。我想通过询问为什么这会起作用来混淆它。
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字段。这个真的让我失望了,我只是无法理解它是如何通过魔法来运作的。
非常感谢任何见解和解释。我真的很想了解这里发生了什么。
答案 0 :(得分:4)
首先,您的数据上下文未关闭,如果您没有关闭数据库上下文连接,则会泄漏 TON 的内存/带宽。请仔细研究。
其次,方法.SaveChanges()
将根据数据库设置确定要分配的正确ID。如果没有诸如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
答案 1 :(得分: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属性的setter中放置一个断点。
答案 2 :(得分:2)
简短回答:这就是上下文和整体性所做的事情;他们跟踪所有内容,以便保存对整个对象图的更新。
通过对象图,我指的是您正在使用的“根”对象,以及您可能附加的任何相关项目,无论您是否始终意识到您正在做的事情。
太棒了!
编辑:如果你想深入了解实体框架,我建议你阅读Julia Lerman的优秀作品。这是一个非常大的话题,但值得。她有一本名为Entity Framework的规范书,以及一个msdn专栏,博客等。
请注意,关于书籍的问题并不是真正的主题,但我建议这样做,因为你似乎对EF不熟悉。