Сильно типизированный ASP.NET MVC с ADO.NET Entity Framework
Я, наконец, получил эту работу после нескольких дней борьбы.
У меня есть простая база данных людей и отделов:
Диаграмма модели данных сущности Entity Framework с объектами Department и Person http://img39.imageshack.us/img39/1368/edmxdepartmentperson.gif
Я могу использовать строго типизированные представления ASP.NET MVC для свойств ссылки/навигации! См. Список отделов...
ASP.NET MVC с DropDownList http://img11.imageshack.us/img11/7619/dropdownlistdepartment.gif
Часть моего лица/Редактировать вид:
<% using (Html.BeginForm()) {%>
<%= Html.Hidden("Id", Model.Id) %>
<fieldset>
<legend>Fields</legend>
<p>
<label for="Name">Name:</label>
<%= Html.TextBox("Name", Model.Name) %>
</p>
<p>
<label for="DepartmentId">Department:</label>
<%= Html.DropDownList("DepartmentId", new SelectList((IEnumerable)ViewData["Departments"], "Id", "Name"))%>
</p>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
Часть моего контролера Person:
//
// GET: /Person/Edit/5
public ActionResult Edit(Guid id)
{
ViewData["Departments"] = ctx.Department;
Person model = (from Person p in ctx.Person
where p.Id == id
select p).FirstOrDefault();
return View(model);
}
//
// POST: /Person/Edit
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Person model)
{
ctx.AttachUpdated(model); //extension
ctx.SaveChanges();
return RedirectToAction("Index");
}
Чтобы получить эту работу, я расширил объект Person EntityObject с помощью нового свойства DepartmentId.
using System;
using System.Data;
using System.Data.Objects.DataClasses;
namespace ProjectName.Models
{
public partial class Person : EntityObject
{
public Guid DepartmentId
{
get
{
try
{
return (Guid)this.DepartmentReference.EntityKey.EntityKeyValues[0].Value;
}
catch
{
return Guid.Empty;
}
}
set
{
this.DepartmentReference.EntityKey = new EntityKey("JunkEntities.Department", "Id", value);
}
}
}
}
И я расширил ObjectContext объекта Entity Framework с помощью новых методов AttachUpdated и ApplyReferencePropertyChanges:
using System;
using System.Data;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
public static class EntityFrameworkExtensionMethods
{
public static void AttachUpdated(this ObjectContext ctx, EntityObject objectDetached)
{
if (objectDetached.EntityKey == null)
{
String entitySetName = ctx.DefaultContainerName + "." + objectDetached.GetType().Name;
Guid objectId = (Guid)objectDetached.GetType().GetProperty("Id").GetValue(objectDetached, null);
objectDetached.EntityKey = new System.Data.EntityKey(entitySetName, "Id", objectId);
}
if (objectDetached.EntityState == EntityState.Detached)
{
object currentEntityInDb = null;
if (ctx.TryGetObjectByKey(objectDetached.EntityKey, out currentEntityInDb))
{
ctx.ApplyPropertyChanges(objectDetached.EntityKey.EntitySetName, objectDetached);
ctx.ApplyReferencePropertyChanges((IEntityWithRelationships)objectDetached,
(IEntityWithRelationships)currentEntityInDb); //extension
}
else
{
throw new ObjectNotFoundException();
}
}
}
public static void ApplyReferencePropertyChanges(this ObjectContext ctx, IEntityWithRelationships newEntity, IEntityWithRelationships oldEntity)
{
foreach (var relatedEnd in oldEntity.RelationshipManager.GetAllRelatedEnds())
{
var oldRef = relatedEnd as EntityReference;
if (oldRef != null)
{
var newRef = newEntity.RelationshipManager.GetRelatedEnd(oldRef.RelationshipName, oldRef.TargetRoleName) as EntityReference;
oldRef.EntityKey = newRef.EntityKey;
}
}
}
}
Я просто хотел документировать свой прогресс здесь. Пожалуйста, предложите улучшения.
Спасибо:
Ответы
Ответ 1
Я начал работать с ASP.NET MVC, поэтому я столкнулся с этим потоком, поэтому я не уверен, что вы все еще проверяете улучшения.
Мне не нравится идея добавления нового свойства в неполный класс в инфраструктуре сущности, поскольку он не позволяет столько изменений.
Попробуйте пометить свой Deppment DropDown "Department.Id", как этот
<p>
<label for="Department.Id">Department:</label>
<%= Html.DropDownList("Department.Id", new SelectList((IEnumerable)ViewData["Departments"], "Id", "Name"))%>
</p>
ModelBinding для MVC Framework подберет значение и применит его к свойству "Id" свойства навигации "Департамент". Я обнаружил, что другие значения Департамента являются нулевыми, но это не существенно. Теперь у вас есть способ получить правильное ведомство и применить его к навигационному свойству отдела нового объекта Person, созданного в параметре "Связывание модели" с вашим параметром Action, например:
newPerson.Department = ctx.Department.First(d => d.DepartmentId == newPerson.Department.Id);
Таким образом, вам не нужно обновлять свой Entity вообще для свойства, которое оно должно иметь.
Ответ 2
Улучшите управление редактированием так, чтобы оно обрабатывало исключения, которые были сброшены, и повторно отображает введенные пользователем данные. Я уверен, что ты собирался;)
Обновите свой просмотр, чтобы иметь валидаторы:
<label for="Name">Name:</label>
<%= Html.TextBox("Name", Model.Name) %>
<%= Html.ValidationMessage("Name", "*") %>
а затем используйте их в своем редактировании:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Person Model)
{
try
{
ctx.AttachUpdated(Model); //extension
ctx.SaveChanges();
return RedirectToAction("Index");
}
catch
{
foreach (var err in Model.Errors)
ModelState.AddModelError(err.PropertyName, err.ErrorMessage)
return View(Model);
}
}