Dans un article précédent, j'ai présenté plusieurs problèmes que vous pouvez rencontrer lors de l'utilisation de LINQ to SQL pour les opérations de mise à jour. En fait, ce n’est pas un problème que j’ai rencontré seul. Lorsque j’ai cherché des réponses sur Internet, j’ai découvert que de nombreuses personnes avaient publié des articles similaires sur ce sujet. Mais ce qui ne me satisfait pas, c'est que bien qu'ils aient soulevé le problème, ils n'ont pas effectué d'analyse détaillée. Ils ont seulement donné des solutions (comme l'ajout de colonnes RowVersion, la suppression d'associations, etc.), mais n'ont pas expliqué pourquoi ils devaient le faire. . C'est également l'intention initiale de l'écriture de l'article précédent. J'espère découvrir la solution au problème étape par étape grâce à l'analyse du code source LINQ to SQL. Cet article abordera ces méthodes une par une.
Option 1 : Réaffectation Dans le framework open source Ezsocio de TerryLee, Anytao, Ding Xue et d'autres, la réaffectation est adoptée à certains endroits. Dans la méthode Update, obtenez les entités de la base de données en fonction de la clé primaire, puis attribuez des valeurs à leurs propriétés une par une avec les entités dans les paramètres.
public void UpdateProfile (Profil p)
{
en utilisant (RepositoryContext db = new RepositoryContext())
{
var profile = db.GetTable<Profile>().First<Profile>(u => u.ID == p.ID);
profil.Anniversaire = p.Anniversaire;
profil.Gender = p.Gender;
profile.Hometown = p.Hometown;
profil.MSN = p.MSN ;
profil.NickName = p.NickName;
profile.PhoneNumber = p.PhoneNumber;
profil.QQ = p.QQ;
profil.State = p.State;
profil.TrueName = p.TrueName;
profile.StateRefreshTime = p.StateRefreshTime ;
profil.Avatar = p.Avatar;
profil.Website = p.Website;
db.SubmitChanges();
}
}
Frère Yang Guo a également fourni une méthode de réflexion pour cette solution afin de réaliser une copie automatique des valeurs d'attribut.
Mais je pense personnellement qu'il s'agit d'un schéma qui évite la réalité et évite la réalité. Il n'utilise pas l'API fournie par LINQ to SQL pour les opérations de mise à jour, mais adopte une stratégie de détour. Il s'agit en fait d'un compromis. Est-ce parce que la méthode Attach n'est "pas facile à utiliser", donc nous ne l'utilisons pas ? héhé.
Option 2 : désactiver le suivi des objets. À cet égard, Lea a proposé que la mise à jour correcte puisse être obtenue en définissant la propriété ObjectTrackingEnabled de DataContext sur false.
Produit public GetProduct (identifiant int)
{
NorthwindDataContext db = new NorthwindDataContext();
db.ObjectTrackingEnabled = false ;
return db.Products.SingleOrDefault(p => p.ProductID == id);
}
Aucun autre changement de code.
Pourquoi peut-il se mettre à jour normalement après avoir désactivé le suivi des objets ? Trouvons la réponse dans le code source.
public bool ObjectTrackingEnabled
{
obtenir
{
this.CheckDispose();
renvoie this.objectTrackingEnabled ;
}
ensemble
{
this.CheckDispose();
si (this.Services.HasCachedObjects)
{
lancer System.Data.Linq.Error.OptionsCannotBeModifiedAfterQuery();
}
this.objectTrackingEnabled = valeur ;
si (!this.objectTrackingEnabled)
{
this.deferredLoadingEnabled = false ;
}
this.services.ResetServices();
}
}
Il s'avère que lorsque ObjectTrackingEnabled est défini sur false, DeferredLoadingEnabled sera défini sur false en même temps. De cette façon, lors de l'exécution de la requête, aucune donnée nécessitant une requête différée ne sera chargée pour l'entité, donc aucune exception ne sera levée lors de l'attachement (voir l'analyse dans l'article précédent).
Dans MSDN, nous obtenons également les informations utiles suivantes : Définir la propriété ObjectTrackingEnable sur false peut améliorer les performances de récupération car cela réduit le nombre d'éléments à suivre. C'est une fonctionnalité très tentante.
Cependant, lors de la désactivation du suivi d'objet, une attention particulière doit être portée à deux points : (1) Il doit être désactivé avant d'exécuter la requête. (2) Après avoir été désactivées, les méthodes Attach et SubmitChanges ne peuvent plus être appelées. Sinon, une exception sera levée.
Option 3 : supprimez l'association. Une méthode boiteuse a été introduite dans l'article précédent, qui consiste à définir manuellement la catégorie associée au produit sur null dans la méthode GetProduct. Nous pouvons extraire cette partie du code et la mettre dans une méthode Detach. Comme ce Detach est une méthode d'entité, des classes partielles peuvent être utilisées :
produit de classe partielle publique
{
public void Détacher()
{
this._Category = default(EntityRef<Category>);
}
}
Catégorie de classe partielle publique
{
public void Détacher()
{
foreach (var produit dans this.Products)
{
produit.Detach();
}
}
}Mais cette méthode de définition de Detach pour chaque entité est trop lourde. À mesure que le nombre d’entités augmente, les relations deviennent de plus en plus complexes et il est facile de faire apparaître des attributs manquants. Zhang Yi a proposé une méthode très élégante pour abstraire cette logique en utilisant la réflexion :
private void Detach (entité TEntity)
{
foreach (FieldInfo fi dans Entity.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance))
{
if (fi.FieldType.ToString().Contains("EntityRef"))
{
var valeur = fi.GetValue(entité);
si (valeur != null)
{
fi.SetValue(entité, null);
}
}
if (fi.FieldType.ToString().Contains("EntitySet"))
{
var valeur = fi.GetValue(entity);
si (valeur != null)
{
MethodInfo mi = value.GetType().GetMethod("Clear");
si (mi != nul)
{
mi.Invoke(valeur, null);
}
fi.SetValue(entité, valeur);
}
}
}
}
Certaines personnes pensent également que les événements PropertyChanging et PropertyChanged doivent être définis sur null lors du détachement, mais l'idée générale est la même.
Option 4 : Utiliser la délégation. C'est la méthode donnée par ZC29 dans les commentaires de mon dernier article. Je pense personnellement qu'elle vaut la peine d'être apprise.
public void UpdateProductWithDelegate (prédicat Expression<Func<Product, bool>>, action Action<Product>)
{
NorthwindDataContext db = new NorthwindDataContext();
var produit = db.Products.SingleOrDefault (prédicat);
action(produit);
db.SubmitChanges();
}
//Code client
Dépôt ProductRepository = new ProductRepository();
référentiel.UpdateProductWithDelegate(p => p.ProductID == 1, p =>
{
p.ProductName = "Modifié" ;
});
Utilisez des expressions Lambda pour intégrer la logique GetProduct dans UpdateProduct et utilisez des délégués pour différer l'exécution de la logique de mise à jour. Cela place intelligemment la recherche et la mise à jour dans un DataContext, contournant ainsi Attach. Cependant, l'API de cette méthode est un peu trop complexe et nécessite des niveaux trop élevés de programmeurs clients. De plus, la logique Get doit être exécutée à nouveau dans Update. Bien que la perte de performances soit minime, elle semble toujours donner aux gens le sentiment qu'elle n'est pas assez SÈCHE.
Option 5 : utiliser l'instruction UPDATE Dans le code source d'Ezsocio, j'ai trouvé la méthode RepositoryBase.UpdateEntity. L'épissage des instructions SQL est effectué à l'intérieur de la méthode et seules les colonnes modifiées seront mises à jour. Étant donné qu'ITable n'est plus utilisé ici et qu'une prise en charge complète du framework est requise, aucun autre commentaire ne sera fait. Veuillez vous référer au code source d'Ezsocio pour plus de détails.
Résumé Cet article répertorie plusieurs solutions que j'ai trouvées sur Internet ces derniers jours. Elles ont toutes des avantages et des inconvénients. La meilleure ou la pire dépend de l'opinion de différentes personnes. Dans le prochain article, je comparerai les performances de ces méthodes pour trouver la solution optimale.