2009-03-17 5 views
5

Ho il seguente DB: Posts che hanno un Id, Tags anche con Id, e TagsToPosts tavolo che hanno TagsToPosts.PostId => Posts.Id e TagsToPosts.TagId => Tags.Id rapporti FK. Ho bisogno di cancellare più oggetti da TagsToPosts nel modo seguente. Sto creando IList<Tag> newTags analizzando una stringa. Ogni tag ha il suo nome. Voglio eliminare tutti gli articoli TagsToPosts che puntano al singolo post (TagsToPosts.PostId == mypostid) e che punta a Tag con nome che non è nel mio newTags.LINQ to SQL batch cancellare

Per esempio ho un post con Id = 1, tre tag: 1 => "tag1", 2 => "tag2", 3 => "tag3" E tabella relazioni ManyToMany TagsToPosts: 1 => 1, 1 => 2, 1 => 3 Così tutti e tre i tag sono legati al mio post. Successivamente creerò un nuovo IList<Tag> newList = new List<Tag>() analizzando una stringa. newList contiene: 0 => "tag1", 0 => "tag2". Ora voglio rimuovere la terza relazione dalla tabella TagsToPosts perché il mio nuovo elenco di tag non contiene tag con nome "tag3". Quindi ho bisogno di trovare una differenza. So che posso trovare oggetti simili usando JOIN ma come trovare la differenza?

Voglio che questo accada in una query DB senza iterare su ogni elemento per eliminarlo.

risposta

0

Hai guardato l'operatore Linq Except?

Ad esempio:

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

Non si può fare questo con LINQ to SQL.

LINQ-to-SQL non è adatto per le operazioni batch: non può eseguire inserimenti batch, non può eseguire aggiornamenti batch e non può eseguire eliminazioni batch. Ogni oggetto nella tua collezione viene trattato individualmente. Puoi fare tutte le operazioni in una transazione, ma ci sarà sempre una query per ogni record.

MSDN

Una soluzione migliore è quello di scrivere una stored procedure che farà quello che vuoi.

0

PLINQO supporta le operazioni di eliminazione batch senza recuperare prima le entità.

var delete = da t in TagsToPost selezionate t) .Except (da nt in newList selezionare nt, nuova TagComparer())

context.Tags.Delete (eliminare);

http://plinqo.com

0

La mia soluzione che consente di apportare le eliminazioni determinate da un campo di 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)); 
} 

Usage:

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

Funziona solo con un singolo campo, ma potrebbe essere esteso a campi compositi abbastanza facilmente.