J'ai un débat mental avec moi-même chaque fois que je commence à travailler sur un nouveau projet et que je conçois mes POCO. J'ai vu de nombreux tutoriels/exemples de code qui semblent favoriser associations de clés étrangères:
public class Order
{
public int ID { get; set; }
public int CustomerID { get; set; } // <-- Customer ID
...
}
Par opposition à associations indépendantes:
public class Order
{
public int ID { get; set; }
public Customer Customer { get; set; } // <-- Customer object
...
}
J'ai travaillé avec NHibernate dans le passé et utilisé des associations indépendantes, qui non seulement se sentent plus OO, mais ont également (avec un chargement paresseux) l'avantage de me donner accès à l'ensemble de l'objet Client, au lieu de simplement son ID. Cela me permet, par exemple, de récupérer une instance de commande, puis de faire Order.Customer.FirstName
sans avoir à faire explicitement une jointure, ce qui est extrêmement pratique.
Donc, pour récapituler, mes questions sont les suivantes:
Si vous souhaitez profiter pleinement de l'ORM, vous utiliserez certainement la référence d'entité:
public class Order
{
public int ID { get; set; }
public Customer Customer { get; set; } // <-- Customer object
...
}
Une fois que vous générez un modèle d'entité à partir d'une base de données avec des FK, il génère toujours des références d'entité. Si vous ne souhaitez pas les utiliser, vous devez modifier manuellement le fichier EDMX et ajouter des propriétés représentant les FK. C'était du moins le cas dans Entity Framework v1 où seules les associations indépendantes étaient autorisées.
Le framework d'entité v4 propose un nouveau type d'association appelé Association de clé étrangère. La différence la plus évidente entre l'association de clé indépendante et l'association de clé étrangère est dans la classe Order:
public class Order
{
public int ID { get; set; }
public int CustomerId { get; set; } // <-- Customer ID
public Customer Customer { get; set; } // <-- Customer object
...
}
Comme vous pouvez le voir, vous disposez à la fois d'une propriété FK et d'une référence d'entité. Il existe plus de différences entre deux types d'associations:
Association indépendante
ObjectStateManager
. Il a son propre EntityState
!Association de clé étrangère
ObjectStateManager
. Pour cette raison, vous devez suivre certaines règles spéciales.Si vous souhaitez utiliser l'association de clé étrangère, vous devez cocher Inclure les colonnes de clé étrangère dans le modèle dans l'assistant de modèle de données d'entité.
Modifier:
J'ai trouvé que la différence entre ces deux types d'associations n'est pas très bien connue alors j'ai écrit un court article couvrant cela avec plus de détails et ma propre opinion à ce sujet.
Utilise les deux. Et rendez vos références d'entité virtuelles pour permettre un chargement paresseux. Comme ça:
public class Order
{
public int ID { get; set; }
public int CustomerID { get; set; }
public virtual Customer Customer { get; set; } // <-- Customer object
...
}
Cela permet d'économiser sur les recherches de base de données inutiles, permet un chargement paresseux et vous permet de voir/définir facilement l'ID si vous savez ce que vous voulez qu'il soit. Notez que les deux ne modifient en rien la structure de votre table.
L'association indépendante ne fonctionne pas bien avec AddOrUpdate
qui est généralement utilisé dans Seed
méthode. Lorsque la référence est un élément existant, elle sera réinsérée.
// Existing customer.
var customer = new Customer { Id = 1, Name = "edit name" };
db.Set<Customer>().AddOrUpdate(customer);
// New order.
var order = new Order { Id = 1, Customer = customer };
db.Set<Order>().AddOrUpdate(order);
Le résultat est que le client existant sera réinséré et qu'un nouveau client (réinséré) sera associé à une nouvelle commande.
Sauf si nous utilisons l'association de clé étrangère et assignons l'ID.
// Existing customer.
var customer = new Customer { Id = 1, Name = "edit name" };
db.Set<Customer>().AddOrUpdate(customer);
// New order.
var order = new Order { Id = 1, CustomerId = customer.Id };
db.Set<Order>().AddOrUpdate(order);
Nous avons le comportement attendu, le client existant sera associé à une nouvelle commande.
Je privilégie l'approche objet pour éviter les recherches inutiles. Les objets de propriété peuvent être tout aussi facilement remplis lorsque vous appelez votre méthode d'usine pour construire l'entité entière (en utilisant un code de rappel simple pour les entités imbriquées). Il n'y a aucun inconvénient que je peux voir, sauf pour l'utilisation de la mémoire (mais vous mettriez en cache vos objets non?). Donc, tout ce que vous faites est de remplacer la pile par le tas et de gagner en performances en n'effectuant pas de recherches. J'espère que cela a du sens.