C# EF:如何删除多对多关系而不加载超出需要的内容

C# EF:如何删除多对多关系而不加载超出需要的内容,c#,entity-framework,many-to-many,dbcontext,C#,Entity Framework,Many To Many,Dbcontext,给定具有多对多关系的实体:Topics和Subscriptions以及相应的表:Topics、Subscriptions,TopicsSubscriptions,我只想使用EF从TopicsSubscriptions中删除一行,但不加载任何额外数据(可能只有一个主题和一个订阅除外) 最后,我希望EF生成与此类似的SQL: exec sp_executesql N'delete [dbo].[TopicSubscriptions] where (([TopicId] = @0) and ([Sub

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

最后,我希望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

我想我可以从这里得到答案:

var db=new-TopicDBEntities();var-topic=db.Topics.FirstOrDefault(x=>x.TopicId==1);
//获取要删除var订阅的订阅=
db.Subscriptions.FirstOrDefault(x=>x.SubscriptionId==2);
//!!!这条线对我不起作用(附加到ICollection?!?)!!!
topic.Subscriptions.Attach(订阅);//附加它(对象上下文现在“认为”它属于该主题)
topic.Subscriptions.Remove(订阅);//删除它
db.SaveChanges();//刷新更改
但是后来我意识到没有属于
ICollection
Attach
方法……除非我遗漏了什么

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

我使用的是
DbContext
而不是
ObjectContext
和entityframework 6,但我想这不重要

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


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

假设您已经映射了实体,这样您就不能直接引用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并希望直接删除它,我建议在数据库上使用一个简单的StoredProcess,接受@topicId和@subscriptionId并删除TSQL中的条目

@topicId INT,
@subscriptionId INT

DELETE FROM [dbo].[TopicSubscriptions] WHERE TopicId = @topicId AND SubscriptionId = @subscriptionId;
然后,您可以将StoredProcess映射到EF6,并从上下文中调用它

using (DbContext dbContext = new DbContext())
{
dbContext.DeleteTopicSubscriptionStoredProcedure(topicId, subscriptionId);
}
编辑 如果只有SP选项起作用(对于缺少延迟加载false表示歉意),您也可以直接从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);
}

我建议让实体框架通过在fluent api中声明
cascadeondelete
来处理删除操作,如下所示

modelBuilder.Entity<Product>()
            .HasOptional(p => p.Category)
            .WithMany()
            .WillCascadeOnDelete(true);
modelBuilder.Entity()
.has可选(p=>p.Category)
.有很多
.WillCascadeOnDelete(真);

完整的解决方案已概述。

正如Slauma在其评论中所说,解决方案是像“dannie.f”在其:


嗯,您删除关系的代码将不起作用,因为未加载内部集合(LazyLoading设置为false)。存储过程确实可以工作,但也必须有一种使用EF的方法。@Cristi-好的。我错过了那个部分(是的,尽管它被突出显示)!已编辑(两分钟内)要添加第三个选项,但我仍然认为存储过程是一种更好的方法。如果您不想要下面我的任何解决方案,则需要向表中添加有效负载列,以便它在EF中映射,并可以作为
dbContext.TopicSubscriptions
引用。但是,这意味着您使用不必要的spa在表中保留了不必要的数据ce。除此之外,我不相信你还有其他选择。我应该注意到,这将影响你现有的代码。在何种程度上,只有你会知道。也许我的问题不够清楚,我只能从db加载一个特定主题和一个特定订阅,尽管我猜这些可以作为w附加到DbSet嗯。在你链接到dannie.f的问题中,我的答案是一个有效的解决方案:正是我刚才尝试过的,而且效果很好…谢谢,我不需要删除实体,我只需要删除它们之间的关联。那么,哈哈,你肯定会得到大量冗余数据?!
modelBuilder.Entity<Product>()
            .HasOptional(p => p.Category)
            .WithMany()
            .WillCascadeOnDelete(true);
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();