2014-02-21 20 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