2012-03-30 55 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

相關問題