EF:如何在不加载超出需要的情况下删除多对多关系

时间:2014-02-20 10:09:49

标签: c# entity-framework many-to-many dbcontext

鉴于具有多对多关系的实体:TopicsSubscriptions以及相应的表:TopicsSubscriptionsTopicsSubscriptions,我想要使用EF仅从TopicsSubscriptions删除一行,但不加载任何额外数据(可能只有1个主题和1个订阅)。

最后我希望EF为此生成类似的SQL:

exec sp_executesql N'delete [dbo].[TopicSubscriptions]
where (([TopicId] = @0) and ([SubscriptionId] = @1))',N'@0 int,@1 int',@0=1,@1=2

我将LazyLoading设置为false

我想我可以从这里得到答案:How to delete many-to-many relationship in Entity Framework without loading all of the data

var db = new TopicDBEntities(); var topic = db.Topics.FirstOrDefault(x => x.TopicId == 1);

// Get the subscription you want to delete var subscription =
db.Subscriptions.FirstOrDefault(x => x.SubscriptionId == 2);

// !!! This line does not work for me (Attach to ICollection<> ?!?) !!!
topic.Subscriptions.Attach(subscription); // Attach it (theObjectContext now 'thinks' it belongs to the topic)

topic.Subscriptions.Remove(subscription); // Remove it
db.SaveChanges(); // Flush changes

但后来我意识到Attach没有属于ICollection<>的方法...除非我遗漏了什么。

只为一个主题附加一个订阅的想法听起来不错,但我无法理解如何实现它。

我使用的是DbContext而不是ObjectContext和实体框架6,但我想这应该不重要。

编辑:如果可能的话,找到没有存储过程或直接sql的解决方案会很好,因为我必须在我的应用程序中支持许多数据库后端。

如果我不够清楚,我不需要删除实体,我只需要删除它们之间的关联。

3 个答案:

答案 0 :(得分:1)

正如Slauma在评论中所说,解决方案是附加实体,就像“dannie.f”在answer中所做的那样:

var db = new TopicDBEntities();

var topic = new Topic { TopicId = 1 };
var subscription = new Subscription { SubscriptionId = 2};
topic.Subscriptions.Add(subscription);

// Attach the topic and subscription as unchanged 
// so that they will not be added to the db   
// but start tracking changes to the entities
db.Topics.Attach(topic);

// Remove the subscription
// EF will know that the subscription should be removed from the topic
topic.subscriptions.Remove(subscription);

// commit the changes
db.SaveChanges();

答案 1 :(得分:0)

假设您已映射它以便您无法直接引用TopicSubscriptions,并且只能引用主题和订阅,则以下情况适用。

// Add a subscription to a topic
var subscription = dbContect.Subscriptions.Find(2);
var topic = dbContext.Topics.Find(1);

// Recommend checking here, but omitted for example

// Make the association
topic.Subscriptions.Add(subscription);

// Update
dbContext.SaveChanges();

从主题中删除订阅

// Remove
topic.Subscriptions.Remove(subscription);

// Update
dbContext.SaveChanges();

如果您知道ID并且想直接删除它,我建议在数据库上使用简单的StoredProcedure接受@topicId和@subscriptionId并删除TSQL中的条目。

@topicId INT,
@subscriptionId INT

DELETE FROM [dbo].[TopicSubscriptions] WHERE TopicId = @topicId AND SubscriptionId = @subscriptionId;

然后,您可以将StoredProcedure映射到EF6并从您的上下文中调用它。

using (DbContext dbContext = new DbContext())
{
dbContext.DeleteTopicSubscriptionStoredProcedure(topicId, subscriptionId);
}

EDIT 只有SP选项可用(对于缺少Lazy-Loading错误而道歉),您也可以直接从dbContext执行SQL。

using (var dbContext = new DbContext())
{
string sql = @"DELETE FROM [dbo].[TopicSubscriptions] WHERE TopicId = @p0 AND SubscriptionId = @p1";
context.Database.ExecuteSqlCommand(sql, topicId, subscriptionId);
}

答案 2 :(得分:0)

我建议让实体框架通过像这样在流畅的api中声明cascade on delete来处理删除;

modelBuilder.Entity<Product>()
            .HasOptional(p => p.Category)
            .WithMany()
            .WillCascadeOnDelete(true);

概述了完整的解决方案here