Entity Framework - Why Does It Work?

I'm sure most people ask why things don't work. I'm going to mix it up by asking why it works.

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>

      

I've removed unnecessary code to shorten this a bit. I am completely new to C #,. NET and everything related to it. I'm completely at a loss as to why this even works. When I call the first dbContext.SaveChanges () to save the entry to "AlarmTotals", it also saves all "MemberResponse" entries at the same time and even more surprisingly has the correct AlarmTotals.ID field. This one really throws me off, I just don't understand how it magically works.

Any insight and explanation would be greatly appreciated. I really want to understand what's going on here.

+3


source to share


3 answers


To add to the words of others, I think the "magic" happens on line 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 }

      



This is the string that makes your new MemberResponse attach to the current EF ObjectContext (and then makes them save on SaveChanges ()). MemberResponse.Member is an EF navigation property.

But are you sure the saved MemberResponses are correctly set by MemberResponse.AlarmTotalID? The code doesn't look like this. The best way to understand what really happened is to set a breakpoint in the AlarmTotalID property setting .

+3


source


First, your data context is not closed and you will be leaking TON of memory / bandwidth if you do not close the database context connections. Please study this.

Second, the method .SaveChanges()

will determine which correct ID should be assigned based on the database settings. If there is no such definition as auto-increment

, then it is possible that these ids will not be set correctly, and you can store them into one identifier and then throw an exception. Children will have their foreign keys assigned only through an explicit association, which you already do.

Edit:

In response to your comment, it is common to use the using statement to manage contexts, because this is pure code:



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

      

If this approach doesn't work for how long you want to open the connection, you can also manually (though not highly recommended) close the connection yourself.

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

      

+4


source


The short answer is this is what Context and Entities do; they keep track of all of this, so updates are kept for the entire object graph.

By object graphs, I mean the "root" object you are working with, and any related items you might add, whether you always understand what you are doing or not.

It's amazing!

EDIT: I recommend reading Julia Lerman's excellent work if you want to dig more into the Entity Framework. This is a very big topic, but worth it. She has a canon book called Entity Framework, msdn column, blog, etc.

Note that the book questions are off-topic here, but I suggest this because you seem to be new to EF.

+2


source







All Articles