2009-03-17 8 views
5

Je le DB suivant: Posts qui ont un Id, Tags aussi avec Id et TagsToPosts tableau qui ont TagsToPosts.PostId => Posts.Id et TagsToPosts.TagId => Tags.Id relations FK. Je dois supprimer plusieurs éléments de TagsToPosts de la manière suivante. Je crée IList<Tag> newTags en analysant une chaîne. Chaque étiquette a son nom. Je veux supprimer tous les articles TagsToPosts pointant vers un seul poste (TagsToPosts.PostId == mypostid) et qui pointe vers Tag avec un nom qui ne figure pas dans mon newTags.LINQ to SQL batch supprimer

Par exemple j'ai un poste avec Id = 1, trois tags: 1 => "tag1", 2 => "tag2", 3 => "tag3" et table relations ManyToMany TagsToPosts: 1 => 1, 1 => 2, 1 => 3 Donc, les trois balises sont liées à mon poste. Après cela, je vais créer un nouveau IList<Tag> newList = new List<Tag>() en analysant une chaîne. newList contient: 0 => "tag1", 0 => "tag2". Maintenant, je veux supprimer la troisième relation de la table TagsToPosts parce que ma nouvelle liste de tags ne contient pas tag avec le nom "tag3". Donc j'ai besoin de trouver une différence. Je sais que je peux trouver des articles similaires en utilisant JOIN mais comment trouver la différence?

Je veux que cela se produise dans une requête DB sans répéter sur chaque élément pour le supprimer.

Répondre

0

Avez-vous regardé l'opérateur Linq Except?

Par exemple:

var toDelete = (from t in TagsToPost 
       select t).Except(from nt in newList 
           select nt, new TagComparer()); 

class TagComparer: IEqualityComparer<TagsToPosts> 
{ 
    public bool Equals(TagsToPosts x, TagsToPosts y) 
    { 
     return x.Tag.Equals(y.Tag, CompareOptions.Ordinal); 
    } 
} 
3

Vous ne pouvez pas le faire avec LINQ to SQL. LINK-to-SQL n'est pas bon pour les opérations par lots: il ne peut pas effectuer d'insertions par lots, il ne peut pas effectuer de mises à jour par lots et il ne peut pas effectuer de suppressions par lots. Chaque objet de votre collection est traité individuellement. Vous pouvez faire toutes les opérations dans une transaction, mais il y aura toujours une requête pour chaque enregistrement.

MSDN

Une meilleure option est d'écrire une procédure stockée qui va faire ce que vous voulez.

0

PLINQO prend en charge les opérations de suppression par lots sans récupérer les entités en premier.

var = Supprimer de t dans TagsToPost select t) .Except (de nt dans newList sélectionner nt, nouveau TagComparer())

context.Tags.Delete (suppression);

http://plinqo.com

0

Ma solution qui vous permet d'effectuer des suppressions déterminées par un champ de classe:

public static void DeleteByPropertyList<T, R>(List<T> listToDelete, Expression<Func<T, R>> getField, DataContext context) where T : class { 
    List<List<string>> partitionedDeletes = listToDelete.Select(d => string.Format("'{0}'", getField.Compile()(d).ToString())).ToList().Partition<string>(2000).ToList(); 
    Func<Expression<Func<T, R>>, string> GetFieldName = propertyLambda => ((MemberExpression)propertyLambda.Body).Member.Name; 
    MetaTable metaTable = context.Mapping.GetTable(typeof(T)); 
    string tableName = string.Format("{0}.{1}", metaTable.Model.DatabaseName, metaTable.TableName); 
    foreach (List<string> partitionDelete in partitionedDeletes) { 
     string statement = "delete from {0} where {1} in ({2})"; 
     statement = string.Format(statement, tableName, GetFieldName(getField), string.Join(",", partitionDelete)); 
     context.ExecuteCommand(statement); 
    } 
} 

public static IEnumerable<List<T>> Partition<T>(this IList<T> source, int size) { 
    for (int i = 0; i < Math.Ceiling(source.Count/(double)size); i++) 
    yield return new List<T>(source.Skip(size * i).Take(size)); 
} 

Utilisation:

List<OrderItem> deletions = new List<OrderItem>(); 
    // populate deletions 
    LinqToSqlHelper.DeleteByPropertyList<OrderItem, long>(deletions, oi => oi.OrderItemId, context); 

Il ne fonctionne qu'avec un seul champ, mais il pourrait être étendu aux champs composites assez facilement.