2014-02-21 36 views
0

我是MVC 5和EF 6的新手,而且我很難理解EF如何工作。我首先使用數據庫,並使用Visual Studio創建edmx文件。請多多包涵,如果是長,我真的想學習EF 6 MVC 5使用MVC 5和實體框架掙扎6

所以在我的數據庫中,我第一次有這種

Book 
----- 
Id 
Name 

AttributeType (ex : Book Size, Book Content, Book Cover) 
------------- 
Id 
Name 

Attribute (ex : Pocket, Mature, Young Reader, Hard Cover, Soft Cover) (FK to AttributeType) 
-------- 
Id 
AttributeTypeId 
Name 

BookAttribute (FK to Book and Attribute, PK (AttributeId and BookId) 
------------- 
AttributeId 
BookId 

因此,使用數據庫,VS 2013會自動創建我的實體:

public partial class Book { 
    public int Id {get;set;} 
    public virtual ICollection<Attribute> Attributes { get; set; } 
} 

,並在我的DbContext

public virtual DbSet<Book> Books { get; set; } 

,我加入了一些類

public enum BookAttributeEnum { BookSize = 1, CoverType = 2, BookAudience = 3 } 

public partial class Book { 
    [NotMapped] 
    [Display(Name = "BookSize", ResourceType = typeof(Resources.Resources))] 
    public Attribute BookSize 
    { 
     get { return Attributes.FirstOrDefault(c => c.AttributeTypeId == (int) BookAttributeEnum.BookSize); } 
    } 

    [NotMapped] 
    [Display(Name = "CoverType", ResourceType = typeof(Resources.Resources))] 
    public Attribute CoverType 
    { 
     get { return Attributes.FirstOrDefault(c => c.AttributeTypeId == (int)BookAttributeEnum.CoverType); } 
    } 

    [NotMapped] 
    [Display(Name = "BookAudience", ResourceType = typeof(Resources.Resources))] 
    public Attribute BookAudience 
    { 
     get { return Attributes.FirstOrDefault(c => c.AttributeTypeId == (int)AttributeTypeEnum.BookAudience); } 
    } 
} 

,在我EditorTemplate的書:

<div class="form-group"> 
    @Html.LabelFor(model => model.BookSize, new { @class = "control-label col-md-2" }) 
    <div class="col-lg-8"> 
     @Html.DropDownListFor(model => model.BookSize.Id, (IEnumerable<SelectListItem>)ViewData["BookSizes"], new { @class = "form-control m-bot15" }) 
    </div> 
</div> 
<div class="form-group"> 
    @Html.LabelFor(model => model.CoverType, new { @class = "control-label col-md-2" }) 
    <div class="col-lg-8"> 
     @Html.DropDownListFor(model => model.CoverType.Id, (IEnumerable<SelectListItem>)ViewData["CoverTypes"], new { @class = "form-control m-bot15" }) 
    </div> 
</div> 
<div class="form-group"> 
    @Html.LabelFor(model => model.BookAudience, new { @class = "control-label col-md-2" }) 
    <div class="col-lg-8"> 
     @Html.DropDownListFor(model => model.BookAudience.Id, (IEnumerable<SelectListItem>)ViewData["BookAudiences"], new { @class = "form-control m-bot15" }) 
    </div> 
</div> 

而且我BookContoller

public ActionResult Edit(int id) 
    { 
     var Db = new CArtEntities(); 

     var attributes = Db.Attributes.ToList(); 


     ViewData["BookSizes"] = attributes.Where(c => c.AttributeTypeId == (int)AttributeTypeEnum.BookSize).ToList() 
      .ToListItems(c => c.Id.ToString(), d => d.Name, true); 

     ViewData["CoverTypes"] = attributes.Where(c => c.AttributeTypeId == (int)AttributeTypeEnum.CoverType).ToList() 
      .ToListItems(c => c.Id.ToString(), d => d.Name, true); 

     ViewData["BookAudiences"] = attributes.Where(c => c.AttributeTypeId == (int)AttributeTypeEnum.BookAudience).ToList() 
      .ToListItems(c => c.Id.ToString(), d => d.Name, true); 

     var art = Db.Books 
      .Include("Attributes") 
      .Include("ApplicationUser") 
      .First(u => u.Id == id); 

     return View(art); 
    } 

這似乎是在那裏我能不能找到一種方法使用實體框架

更新零件
[HttpPost] 
    [ValidateAntiForgeryToken] 
    public async Task<ActionResult> Edit(Book model) 
    { 
     if (ModelState.IsValid) 
     { 
      var Db = new BookEntities(); 

      // this is not a good idea, but i don't know how to do it correctly 
      var book = Db.Books 
      .Include("Attributes") 
      .Include("ApplicationUser") 
      .First(u => u.Id == id); 

      book.Name = model.Name; 
      Db.Entry(book).State = System.Data.Entity.EntityState.Modified; 


      List<int> listAttributes = new List<int>(); 
      listAttributes.Add(Int32.Parse(Request["BookSize.Id"])); 
      listAttributes.Add(Int32.Parse(Request["CoverType.Id"])); 
      listAttributes.Add(Int32.Parse(Request["BookAudience.Id"])); 


      for (int i = book.Attributes.Count - 1; i >= 0; i--) 
      { 
       Attribute at = book.Attributes.ToList()[i]; 

       if (!listAttributes.Contains(at.Id)) 
        Db.Entry(at).State = EntityState.Deleted; 
      } 

      foreach (int i in listAttributes) 
      { 
       if (book.Attributes.FirstOrDefault(c => c.Id == i) == null) 
       { 
        Attribute at = new Attribute {Id = i}; 
        book.Attributes.Add(at); 
       } 
      } 

      await Db.SaveChangesAsync(); 

      return RedirectToAction("Index"); 
     } 
     return View(model); 
    } 

因此,我的問題ns:

  1. 保存時,是否需要重新獲取當前保存的對象 ?因爲我想我需要知道哪些屬性已被刪除,並添加了 。
  2. 我該如何爲書添加屬性? book.Attributes.Add(AT);當我做SaveChangesAsync()時,這行會引發一個錯誤,因爲它似乎要添加一個新的屬性 (這是不正確的,因爲它已經在數據庫中,並且 它有一個id,並且它引發ValidationError(Name is需要)
  3. 什麼是動態生成的書最好的辦法在我的編輯器模板的屬性
    ?還是我通過爲每個屬性類型創建
    財產做是正確的?

我希望有人能幫助我我已經找了幾個小時怎麼做,但是到現在爲止都沒有成功。

謝謝

回答

1

關於1)是的。

關於2)我回答你的編輯POST操作(從上到下)的其他一些可能的改進在一起:

  • 良好做法:實例化背景下的using塊,以確保它得到處理當你處理完成:

    using (var Db = new BookEntities()) 
    { 
        //... 
    } 
    
  • 更喜歡Include拉姆達版在基於字符串的版本,因爲你將有編譯時檢查,如果您的導航性能是正確的:

    .Include(b => b.Attributes) 
    .Include(b => b.ApplicationUser) 
    
  • 寧要FirstSingle(或SingleOrDefault)如果你知道,只能有一個實體與給定id

  • 刪除線Db.Entry(book).State = System.Data.Entity.EntityState.Modified;完全。你裝book是跟蹤實體和EF會認識到,通過設置book.Name = model.Name;你改變了實體,它有當你調用SaveChanges進行更新。

  • 我相信for循環是錯誤的,因爲與設置的AttributeDeleted EF將嘗試真正刪除從Attribute的桌子,其屬性的狀態 - 我認爲 - 是不是你想要的。您只想刪除書和屬性之間的關係。我想重寫for循環,像這樣:

    foreach (var at in book.Attributes.ToList()) 
    { 
        if (!listAttributes.Contains(at.Id)) 
         book.Attributes.Remove(at); 
    } 
    

    更改跟蹤將檢測您刪除其書中的屬性,那意思就是在鏈接表BookAttributes DELETE語句(而不是在Attributes表!)。

  • 我寧願if (!book.Attributes.Any(c => c.Id == i))超過if (book.Attributes.FirstOrDefault(c => c.Id == i) == null)。它更好地表達了代碼的意圖。

  • 爲了避免增加新的屬性來Attribute表(你真正的問題2)將其連接到上下文:

    Attribute at = new Attribute {Id = i}; 
    Db.Attributes.Attach(at); 
    book.Attributes.Add(at); 
    

關於3)似乎挺合我意。有些人(包括我自己)不喜歡ViewData字典,寧願將視圖需要的每個數據都放到一個ViewModel類中,並將其作爲模型傳遞給視圖。但這可能是一個品味問題,並且會詳細討論,讓你的問題爆發一點。

+0

謝謝您的回覆,但Db.Attributes.Attach(在)我扔一個錯誤'與已經在ObjectStateManager存在相同的密鑰的對象。 ObjectStateManager不能使用相同的密鑰跟蹤多個對象。' – puntapret

+0

@puntapret:你可以嘗試替換'屬性at = new Attribute {Id = i}; Db.Attributes.Attach(at);'通過'Attribute at = Db.Attributes.Find(i);'?我不知道你的代碼如何發生這種異常。 – Slauma

+0

我改變了,現在它拋出我的錯誤:'無法更新EntitySet的「BookAttribute」,因爲它有一個DefiningQuery並沒有元素在元素的存在是爲了支持當前operation.'。這很奇怪,因爲BookAttribute表具有主鍵。 – puntapret