I have the following DB:
Posts
which have an Id
, Tags
also with Id
, and TagsToPosts
table which have T
You can't do this with LINQ-to-SQL.
LINQ-to-SQL is not good for batch operations - it can't do batch inserts, it can't do batch updates, and it can't do batch deletes. Every object in your collection is treated individually. You can do all the operations in one transaction, but there will always be a query for each record.
MSDN
A better option is to write a stored procedure that will do what you want.
PLINQO supports batch delete operations without retrieving the entities first.
var delete = from t in TagsToPost select t).Except(from nt in newList select nt, new TagComparer())
context.Tags.Delete(delete);
http://plinqo.com
Have you looked at the Linq Except operator?
For example:
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);
}
}
My solution which lets you make deletions determined by a class field:
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);
It only works with a single field, but it could be extended to composite fields easily enough.