实体框架 - 为什么这样做?

时间:2012-03-30 00:00:09

标签: c# .net wpf entity-framework

我相信大多数人会问为什么事情不起作用。我想通过询问为什么这会起作用来混淆它。

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 个答案:

答案 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不熟悉。