[英]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 : 所以我的问题是:
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.