[entity-framework] Entity Framework에서 여러 행을 삭제하는 방법 (foreach 제외)

Entity Framework를 사용하여 테이블에서 여러 항목을 삭제하고 있습니다. 외래 키 / 부모 개체가 없으므로 OnDeleteCascade 로이를 처리 할 수 ​​없습니다.

지금 나는 이것을하고있다 :

var widgets = context.Widgets
    .Where(w => w.WidgetId == widgetId);

foreach (Widget widget in widgets)
{
    context.Widgets.DeleteObject(widget);
}
context.SaveChanges();

그것은 효과가 있지만 foreach는 나를 버그로 만듭니다. EF4를 사용하고 있지만 SQL을 실행하고 싶지 않습니다. 나는 단지 내가 빠진 것이 없는지 확인하고 싶습니다-이것이 얻는 것만 큼 좋습니다. 확장 방법이나 도우미로 추상화 할 수 있지만 어딘가에서 foreach를 수행 할 것입니다.



답변

루프에서 DeleteObject를 직접 호출하여 SQL을 실행하지 않으려면 오늘 최선을 다하십시오.

그러나 여기에 설명 된 접근 방식을 사용하여 SQL을 실행하고 확장 방법을 통해 완전히 범용으로 만들 수 있습니다 .

그 대답은 3.5였습니다. 4.0의 경우 아마도 StoreConnection으로 드롭 다운하는 대신 새로운 ExecuteStoreCommand API를 사용합니다.


답변

EntityFramework 6을 사용하면이 작업을보다 쉽게 ​​수행 할 수 있습니다 .RemoveRange().

예:

db.People.RemoveRange(db.People.Where(x => x.State == "CA"));
db.SaveChanges();


답변

이것이 얻는 것만 큼 좋습니다. 확장 방법이나 도우미로 추상화 할 수 있지만 어딘가에서 foreach를 수행 할 것입니다.

네, 두 개의 라이너로 만들 수 있다는 점을 제외하고는 :

context.Widgets.Where(w => w.WidgetId == widgetId)
               .ToList().ForEach(context.Widgets.DeleteObject);
context.SaveChanges();


답변

using (var context = new DatabaseEntities())
{
    context.ExecuteStoreCommand("DELETE FROM YOURTABLE WHERE CustomerID = {0}", customerId);
}


답변

나는 그것이 늦었다는 것을 알고 있지만 누군가가 간단한 해결책이 필요한 경우 멋진 점은 where 절을 추가 할 수 있다는 것입니다.

public static void DeleteWhere<T>(this DbContext db, Expression<Func<T, bool>> filter) where T : class
{
    string selectSql = db.Set<T>().Where(filter).ToString();
    string fromWhere = selectSql.Substring(selectSql.IndexOf("FROM"));
    string deleteSql = "DELETE [Extent1] " + fromWhere;
    db.Database.ExecuteSqlCommand(deleteSql);
}

참고 : MSSQL2008로 방금 테스트했습니다.

최신 정보:

EF가 parameters로 sql 문을 생성 할 때 위의 솔루션이 작동하지 않으므로 EF5에 대한 업데이트가 있습니다 .

public static void DeleteWhere<T>(this DbContext db, Expression<Func<T, bool>> filter) where T : class
{
    var query = db.Set<T>().Where(filter);

    string selectSql = query.ToString();
    string deleteSql = "DELETE [Extent1] " + selectSql.Substring(selectSql.IndexOf("FROM"));

    var internalQuery = query.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance).Where(field => field.Name == "_internalQuery").Select(field => field.GetValue(query)).First();
    var objectQuery = internalQuery.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance).Where(field => field.Name == "_objectQuery").Select(field => field.GetValue(internalQuery)).First() as ObjectQuery;
    var parameters = objectQuery.Parameters.Select(p => new SqlParameter(p.Name, p.Value)).ToArray();

    db.Database.ExecuteSqlCommand(deleteSql, parameters);
}

약간의 반사가 필요하지만 잘 작동합니다.


답변

EF5를 사용하는 사람은 다음 확장 라이브러리를 사용할 수 있습니다. https://github.com/loresoft/EntityFramework.Extended

context.Widgets.Delete(w => w.WidgetId == widgetId);


답변

서버를 삭제하기 위해 서버에서 무언가를 가져와야하는 것은 여전히 ​​미치게 보이지만 적어도 ID 만 다시 가져 오는 것이 전체 엔티티를 끌어내는 것보다 훨씬 느립니다.

var ids = from w in context.Widgets where w.WidgetId == widgetId select w.Id;
context.Widgets.RemoveRange(from id in ids.AsEnumerable() select new Widget { Id = id });