Avis:
1. Les "entités" mentionnées dans cet article sont toutes générées par LINQ TO SQL (c'est-à-dire .dbml)
2. Vous devez comprendre comment LINQ TO SQL implémente les associations de tables, EntitySet et EntityRef
Peut-être qu'après avoir vu le titre, vous penserez que la question est relativement abstraite, alors laissez-moi vous donner un exemple pour expliquer le problème en détail.
Dans l'architecture N-tier basée sur LINQ TO SQL, si nous devons mettre à jour une entité, le processus doit être le suivant :
processus
BLL.GetModel(p=>p.id==1) --> Modifier la valeur de l'attribut (champ) correspondant --> BLL.Update(Entité entité) --> DAL.Update(Entité entité) --> Mise à jour réussie
À ce stade, nous devons transmettre cette entité de la couche métier (BLL) à la couche d'accès aux données (DAL). Lorsque la méthode GetModel renvoie l'entité, le DataContext sera immédiatement libéré, puis ré-instancié lorsque le DAL. La méthode Update (entité Entity) est exécutée. Créez un DataContext pour effectuer l'opération de mise à jour ; on peut voir que l'entité transmise est transmise du premier DataContext à un autre DataContext. Lors de l'opération sur différents DataContexts dans LINQ TO SQL, des opérations supplémentaires doivent être effectuées. être effectué en premier, c'est-à-dire context.Entity. Attach(entity,true) Enfin context.SubmitChanges();
Jetons un coup d'œil au code :
code
classe BLL
{
DAL privé en lecture seule dal = new DAL();
public LinqToSqlProvider.User GetModel(Expression<Func<LinqToSqlProvider.User, bool>> expression)
{
dal.GetModel(expression);
}
Mise à jour publique void (entité LinqToSqlProvider.User)
{
dal.Update(entité);
}
}
classe DAL
{
public LinqToSqlProvider.User GetModel(Expression<Func<LinqToSqlProvider.User, bool>> expression)
{
Entrée LinqToSqlProvider.User = new CriTextBroadcast.LinqToSqlProvider.User();
en utilisant (contexte CriTextBroadcastDBDataContext = DataContext)
{
entrée = contexte.User.SingleOrDefault(expression);
}
entrée de retour ;
}
Mise à jour publique void (entité LinqToSqlProvider.User)
{
en utilisant (contexte CriTextBroadcastDBDataContext = DataContext)
{
contexte.User.Attach(entry, true);
contexte.SubmitChanges();
}
}
}
En fait, lorsque nous utilisons le code ci-dessus pour opérer, cette exception se produira :
Tentative d'attachement ou d'ajout d'entité, cette entité n'est pas une nouvelle entité, peut être chargée à partir d'un autre DataContext...
Après avoir vérifié de nombreuses informations en vain, j'ai finalement vu une question similaire dans un message étranger. Il s'avère que la table correspondant à cette entité possède plusieurs tables liées, telles que : Utilisateur -> Message, Utilisateur -> Images, etc.
Les attributs tels que EntitySet<Message> Message seront automatiquement générés lors de la génération des classes d'entité. Puisque nous ne lisons pas ces contenus associés ensemble lors de l'obtention de l'entité, une ObjectDisposedException se produira dans ces attributs lors de l'attachement (Attach). utilisé pour interroger cette entité a été publié.
Solution:
code
/// <résumé>
/// L'entité auxiliaire LinqToSql est séparée du DataContext d'origine
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="entity"></param>
public static void Detatch<TEntity>(entité TEntity)
{
Tapez t = entité.GetType();
Propriétés System.Reflection.PropertyInfo[] = t.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
foreach (propriété var dans les propriétés)
{
nom de chaîne = propriété.Nom ;
si (property.PropertyType.IsGenericType &&
property.PropertyType.GetGenericTypeDefinition() == typeof(EntitySet<>))
{
property.SetValue (entité, null, null);
}
}
Champs System.Reflection.FieldInfo[] = t.GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
foreach (var champ dans les champs)
{
nom de chaîne = champ.Nom ;
si (field.FieldType.IsGenericType &&
field.FieldType.GetGenericTypeDefinition() == typeof(EntityRef<>))
{
field.SetValue(entité, null);
}
}
System.Reflection.EventInfo eventPropertyChanged = t.GetEvent("PropertyChanged");
System.Reflection.EventInfo eventPropertyChanging = t.GetEvent("PropertyChanging");
si (eventPropertyChanged != null)
{
eventPropertyChanged.RemoveEventHandler(entité, null);
}
si (eventPropertyChanging != null)
{
eventPropertyChanging.RemoveEventHandler(entité, null);
}
}
Après avoir obtenu l'entité, vous devez utiliser la méthode Detach(entity) pour séparer l'entité du DataContext d'origine, puis l'attacher au nouveau DataContext.