2009-05-28 9 views
13

J'ai finalement obtenu ce travail après des jours de lutte.ASP.NET MVC fortement typé avec ADO.NET Entity Framework

J'ai une simple base de données des personnes et des départements:

ADO.NET Entity Framework Entity Data Model diagram with Department and Person objects http://img39.imageshack.us/img39/1368/edmxdepartmentperson.gif

Je peux utiliser des vues ASP.NET MVC fortement typées pour les propriétés de référence/navigation! Voir la liste des départements ...

ASP.NET MVC with DropDownList http://img11.imageshack.us/img11/7619/dropdownlistdepartment.gif

Une partie de ma personne/Modifier Vue:

<% 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> 
<% } %> 

Une partie de mon contrôleur Personne:

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

Pour obtenir ce travail, J'ai étendu la Person EntityObject avec une nouvelle propriété 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); 
      } 
     } 
    } 
} 

Et j'étendu Entity Framework ObjectContext avec de nouvelles méthodes et 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; 
      } 
     } 
    } 

} 

Je voulais juste documenter mes progrès ici. S'il vous plaît suggérer des améliorations.


Merci:

+3

Beau travail, mais malheureusement, stackoverflow.com n'est pas le lieu pour documenter vos progrès. J'ai voté pour la clôture: "pas une vraie question". –

+1

N'avez-vous pas besoin d'exclure la propriété ID lors de la liaison de l'objet personne ici: public ActionResult Edit (Guid ID, Person Model)? –

+3

Ah, j'ai raté la partie sur "suggérer des améliorations". Laissez-le vivre, dis-je. –

Répondre

8

J'ai commencé à travailler avec ASP .NET MVC c'est pourquoi je suis tombé sur ce sujet, donc je ne suis pas sûr si vous êtes encore en train de vérifier les améliorations.

Je n'aime pas l'idée d'ajouter la nouvelle propriété à une classe partielle sur le framework d'entité car elle ne permet pas autant de changement. Essayez d'étiqueter le département "Deparment DropDown".Id » comme celui-ci

<p> 
    <label for="Department.Id">Department:</label> 
<%= Html.DropDownList("Department.Id", new SelectList((IEnumerable)ViewData["Departments"], "Id", "Name"))%> 
</p> 

Le ModelBinding du framework MVC ramassera la valeur et l'appliquer à la « propriété de navigation. Id » Propriété du « département » Ce que je trouve que les autres valeurs du département sont Vous avez maintenant un moyen de récupérer l'entité de département correcte et de l'appliquer à la propriété de navigation de département de la nouvelle entité de personne créée dans le paramètre Modèle lié à votre action, par exemple:

newPerson.Department = ctx.Department.First(d => d.DepartmentId == newPerson.Department.Id); 

En procédant de cette manière, vous n'avez pas besoin de mettre à jour votre Entité du tout pour une propriété qu'elle devrait avoir

+1

bon et propre! Zack, vous pouvez également définir EntityKey sur vos contrôleurs Méthode d'édition: newPerson.DepartmentReference.EntityKey = nouvelle EntityKey ("YourEntities.Department", "DepartmentId", int.Parse (Request.Form ["DepartmentId"])); –

0

Améliorez votre Modifier le contrôle afin qu'il gère les exceptions levées et réaffiche l'entrée que l'utilisateur a tapé jusqu'à présent. Je suis sûr que vous étiez sur le point;)

Mettez à jour votre vue d'avoir validateurs:

<label for="Name">Name:</label> 
<%= Html.TextBox("Name", Model.Name) %> 
<%= Html.ValidationMessage("Name", "*") %> 

puis les utiliser dans votre édition:

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

"'.... Person' ne contient pas de définition pour 'Errors' et aucune méthode d'extension 'Errors' acceptant un premier argument de type '.... Person' n'a été trouvée (manque-t-il une directive using ou une référence d'assemblage?) " –

+0

Cela dépend du framework que vous utilisez. Lightspeed et Linq2Sql vous donnent la propriété Errors sur chaque Entité. Si vous construisez vos Entités manuellement au lieu d'utiliser un ORM, vous devrez construire cette propriété dans votre classe partielle pour Person. – womp

+0

Quelque chose comme Listing 3 et 4 dans cet article: http://www.asp.net/Learn/mvc/tutorial-16-cs.aspx – womp

Questions connexes