2009-03-17 20 views
5

Tengo el siguiente DB: Posts que tienen un Id, Tags también con Id y TagsToPosts mesa que tiene TagsToPosts.PostId => Posts.Id y TagsToPosts.TagId => Tags.Id las relaciones FK. Necesito eliminar varios elementos de TagsToPosts de la siguiente manera. Estoy creando IList<Tag> newTags analizando una cadena. Cada etiqueta tiene su nombre. Quiero eliminar todos los artículos TagsToPosts que apuntan a una sola publicación (TagsToPosts.PostId == mypostid) y que apunta a Tag con el nombre que no está en mi newTags.LINQ a SQL por lotes borrar

Por ejemplo tengo un puesto con Id = 1, tres etiquetas: 1 => "tag1", 2 => "tag2", 3 => "tag3" Y ManyToMany tabla de relaciones TagsToPosts: 1 => 1, 1 => 2, 1 => 3 Así las tres etiquetas están vinculados a mi mensaje. Después de eso, crearé un nuevo IList<Tag> newList = new List<Tag>() analizando una cadena. newList contiene: 0 => "tag1", 0 => "tag2". Ahora quiero eliminar la tercera relación de la tabla TagsToPosts porque mi nueva lista de etiquetas no contiene la etiqueta con el nombre "tag3". Entonces necesito encontrar una diferencia. Sé que puedo encontrar artículos similares usando JOIN pero ¿cómo encontrar la diferencia?

Quiero que esto suceda en una consulta DB sin iterar sobre cada elemento para eliminarlo.

Respuesta

0

¿Has mirado al operador Linq Except?

Por ejemplo:

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

No se puede hacer esto con LINQ a SQL.

LINQ-to-SQL no es bueno para las operaciones por lotes: no puede hacer inserciones por lotes, no puede realizar actualizaciones por lotes y no puede realizar eliminaciones por lotes. Todos los objetos de tu colección se tratan individualmente. Puede hacer todas las operaciones en una transacción, pero siempre habrá una consulta para cada registro.

MSDN

Una mejor opción es escribir un procedimiento almacenado que va a hacer lo que quiera.

0

PLINQO admite operaciones de eliminación por lotes sin recuperar primero las entidades.

var delete = de t en TagsToPost seleccione t) .Except (de nt en newList seleccionar nt, nuevo TagComparer())

context.Tags.Delete (eliminar);

http://plinqo.com

0

Mi solución que permite realizar supresiones determinados por un campo de clase:

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)); 
} 

Uso:

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

Sólo funciona con un solo campo, pero podría ser extendido a campos compuestos con bastante facilidad.