简体   繁体   English

使用MVC 5和Entity Framework 6进行挣扎

[英]Struggling using MVC 5 and Entity Framework 6

I'm new to MVC 5 and EF 6, and i'm having lots of trouble of understanding how EF works. 我是MVC 5和EF 6的新手,在了解EF的工作原理时遇到了很多麻烦。 I'm using Database first, and used Visual Studio to create the edmx file. 我首先使用数据库,并使用Visual Studio创建edmx文件。 Please bear with me if it's long, and i'm really want to learn EF 6 with MVC 5 如果时间长的话,请多多包涵,我真的很想通过MVC 5学习EF 6

So in my database i have this 所以在我的数据库中

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

So using Database first, VS 2013 creates automatically my entities : 因此,首先使用数据库,VS 2013会自动创建我的实体:

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

and in my DbContext 在我的DbContext中

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

and i added some classes 我增加了一些课程

  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); }
    }
}

and in my EditorTemplate for book : 并在我的book的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>

And my BookContoller 还有我的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);
    }

This is the part where i can't seem find a way to update using Entity Framework 这是我似乎找不到使用Entity Framework更新的方法的部分

    [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);
    }

So my questions : 所以我的问题是:

  1. When saving, do i need to re-get the current object that i'm saving ? 保存时,是否需要重新获取要保存的当前对象? Since i think i need to know which attribute has been removed and added. 由于我认为我需要知道已删除和添加了哪个属性。
  2. How can i add attribute to the book ? 如何向书中添加属性? book.Attributes.Add(at); book.Attributes.Add(AT); this line throw an error when i do SaveChangesAsync(), since it seems want to add a new Attribute (which is not correct at all, since it's already in database, and it has an id, and it throw ValidationError (Name is required) 这行在我执行SaveChangesAsync()时引发错误,因为似乎要添加新的属性(这根本不正确,因为它已经存在于数据库中,并且具有ID,并且引发ValidationError(名称为必填项)
  3. What is the best way to generating dynamically the book Attributes 动态生成书籍属性的最佳方法是什么
    in my Editor Template ? 在我的编辑器模板中? Or did i do it correctly by creating 还是我通过创建正确地做到了
    property for each AttributeType ? 每个AttributeType的属性?

I hope someone can help me out, i've already searching for hours about how to do it, but failed until now. 我希望有人可以帮助我,我已经花了数小时来寻找如何做的方法,但是直到现在都失败了。

Thanks 谢谢

About 1) Yes. 大约1)是。

About 2) I answer this together with some other possible improvements of your Edit POST action (from top to bottom): 大约2)我将回答此问题,同时对您的Edit POST操作进行一些其他可能的改进(从上到下):

  • Good practice: Instantiate your context in a using block to ensure it gets disposed when your processing is finished: 优良作法:在using块中实例化上下文,以确保在处理完成后将其释放:

     using (var Db = new BookEntities()) { //... } 
  • Prefer the lambda version of Include over the string-based version because you will have compile time checks if your navigation properties are correct: 与基于字符串的版本相比,首选Include的lambda版本,因为如果导航属性正确,则会进行编译时检查:

     .Include(b => b.Attributes) .Include(b => b.ApplicationUser) 
  • Prefer Single (or SingleOrDefault ) over First if you know there can only be one entity with the given id . 如果您知道只有一个具有给定id实体,则First使用Single (或SingleOrDefault )。

  • Remove the line Db.Entry(book).State = System.Data.Entity.EntityState.Modified; 删除行Db.Entry(book).State = System.Data.Entity.EntityState.Modified; completely. 完全。 Your loaded book is a tracked entity and EF will recognize that by setting book.Name = model.Name; 您加载的book是一个跟踪的实体,EF会通过设置book.Name = model.Name;来识别它book.Name = model.Name; you changed the entity and that it has to be updated when you call SaveChanges . 您更改了实体,并且在调用SaveChanges时必须对其进行更新。

  • I believe the for loop is wrong because with setting the state of an Attribute to Deleted EF will try to really delete the attribute from the Attribute s table which - I think - is not what you want. 我认为for循环是错误的,因为将Attribute的状态设置为Deleted EF会尝试从Attribute表中真正删除该属性,我认为这不是您想要的。 You only want to delete the relationship between book and attribute. 您只想删除书籍和属性之间的关系。 I would rewrite the for loop like so: 我会这样重写for循环:

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

    Change tracking will detect that you removed the attribute from the book and translate that to a DELETE statement in the link table BookAttributes (and not in the Attributes table!). 变更跟踪将检测到您从书中删除了该属性,并将其转换为链接表BookAttributes的DELETE语句(而不是Attributes表中的!)。

  • I would prefer if (!book.Attributes.Any(c => c.Id == i)) over if (book.Attributes.FirstOrDefault(c => c.Id == i) == null) . 我宁愿if (!book.Attributes.Any(c => c.Id == i))胜过if (book.Attributes.FirstOrDefault(c => c.Id == i) == null) It expresses the intent of the code better. 它更好地表达了代码的意图。

  • To avoid adding a new attribute to the Attribute table (your real question 2) attach it to the context: 为避免向Attribute表中添加新属性(您的实际问题2),将其附加到上下文:

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

About 3) Seems OK to me. 大约3)对我来说似乎还可以。 Some people (including myself) don't like the ViewData dictionary and prefer to put every data the view needs into a single ViewModel class and pass that as the model to the view. 有些人(包括我自己)不喜欢ViewData字典,而是喜欢将视图所需的每个数据放入单个ViewModel类中,并将其作为模型传递给视图。 But it's perhaps a matter of taste and discussing that in detail would let your question explode a bit. 但这可能是一个喜好问题,详细讨论将使您的问题有些爆发。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM