c# – 在EF中保存对象时,数据库中没有新的多对多连接

我的代码有问题,我尝试在两个对象之间保存多对多的连接,但由于某种原因它不会被保存.

我们使用代码优先方法来创建我们的数据库,在我们的数据库中我们有以下实体来解决这个问题:

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<ProductTag> ProductTags { get; set; }
}
public class ProductTag
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Product> Products { get; set; }
}

ProductTagProducts表是自动创建的,当然这只是两者之间的连接表.

现在创建产品很好.我们可以运行以下命令,它将在ProductTagProducts表中创建连接:

Product.ProductTags.Add(productTag);

为了确保数据库中没有重复的任务,我们自己处理它的保存. productTag始终包含具有现有ID的产品标签.

当我们想要编辑相同或其他产品时会出现问题.该产品有现有标签.我们使用以下过程来保存它:

List<ProductTag> productTags = new List<ProductTag>();
string[] splittedTags = productLanguagePost.TagList.Split(',');
foreach (string tag in splittedTags) {
    ProductTag productTag = new ProductTag();
    productTag.Name = tag;
    productTags.Add(productTagRepository.InsertAndOrUse(productTag));
}

我们用逗号分隔标记,这是从HTML元素接收的方式.然后我们为它定义一个新实体,并使用InsertAndOrUse来确定标签是否已经存在.如果标记已经存在,则返回相同的实体,但填写了ID,如果它不存在,则将标记添加到数据库,然后还返回具有ID的实体.我们创建了一个新列表,以确保产品中没有重复的Id(我已尝试将其直接添加到产品的现有标签列表中,结果相同).

product.ProductTags = productTags;
productRepository.InsertOrUpdate(product);
productRepository.Save();

然后我们将列表设置为ProductTags并让存储库处理插入或更新,当然,将进行更新.以防万一,这是InsertOrUpdate函数:

public void InsertOrUpdate(Product product) {
    if (product.Id == default(int)) {
        context.Products.Add(product);
    } else {
        context.Entry(product).State = EntityState.Modified;
    }
}

save方法只调用上下文的SaveChanges方法.当我编辑产品并添加另一个标签时,它不会保存新标签.但是,当我在保存功能上设置断点时,我可以看到它们都在那里:

当我打开新添加的标签’Oeh-la-la’时,我甚至可以通过它返回产品:

但是当保存发生时,所有其他值都成功,则ProductTagProducts表中没有连接.也许这是非常简单的事情,但此刻我一无所知.我真的希望别人可以给人一种好看的感觉.

提前致谢.

编辑:请求ProductTag的InsertAndOrUse方法.它调用的InsertOrUpdate方法与上面完全相同.

public ProductTag InsertAndOrUse(ProductTag productTag)
{
    ProductTag resultingdProductTag = context.ProductTags.FirstOrDefault(t => t.Name.ToLower() == productTag.Name.ToLower());

    if (resultingdProductTag != null)
    {
        return resultingdProductTag;
    }
    else
    {
        this.InsertOrUpdate(productTag);
        this.Save();
        return productTag;
    }
}
最佳答案
你必须知道这条线……

context.Entry(product).State = EntityState.Modified;

……对关系的状态没有影响.它只是将实体产品标记为已修改的Entry,即标量属性Product.Name被标记为已修改且没有其他内容.发送到数据库的SQL UPDATE语句只更新Name属性.它不会在多对多链接表中写入任何内容.

您可以更改与该行的关系的唯一情况是外键关联,即在模型中将外键公开为属性的关联.

现在,多对多关系永远不是外键关联,因为您不能在模型中公开外键,因为外键位于模型中没有相应实体的链接表中.多对多关系始终是独立的关联.

除了直接操作关系状态条目(相当高级并且需要转到ObjectContext)之外,只能使用Entity Framework的更改跟踪添加或删除独立关联.此外,您必须考虑用户可能已删除标记,这要求必须删除链接表中的关系条目.要跟踪此类更改,您必须首先从数据库加载给定产品的所有现有相关标记.

要将所有这些组合在一起,您将不得不更改InsertOrUpdate方法(或引入新的专用方法):

public void InsertOrUpdate(Product product) {
    if (product.Id == default(int)) {
        context.Products.Add(product);
    } else {
        var productInDb = context.Products.Include(p => p.ProductTags)
            .SingleOrDefault(p => p.Id == product.Id);

        if (productInDb != null) {
            // To take changes of scalar properties like Name into account...
            context.Entry(productInDb).CurrentValues.SetValues(product);

            // Delete relationship
            foreach (var tagInDb in productInDb.ProductTags.ToList())
                if (!product.ProductTags.Any(t => t.Id == tagInDb.Id))
                    productInDb.ProductTags.Remove(tagInDb);

            // Add relationship
            foreach (var tag in product.ProductTags)
                if (!productInDb.ProductTags.Any(t => t.Id == tag.Id)) {
                    var tagInDb = context.ProductTags.Find(tag.Id);
                    if (tagInDb != null)
                        productInDb.ProductTags.Add(tagInDb);
                }
        }
    }

我在上面的代码中使用了Find,因为如果product.ProductTags集合中的标记是否附加到上下文实例,我不确定您的代码片段(缺少InsertAndOrUse的确切代码).通过使用Find它应该工作,无论它们是否被附加,可能以数据库往返为代价来加载标记.

如果product.ProductTags中的所有标签都已附加,您可以替换…

                    var tagInDb = context.ProductTags.Find(tag.Id);
                    if (tagInDb != null)
                        productInDb.ProductTags.Add(tagInDb);

……只是

                    productInDb.ProductTags.Add(tag);

或者如果不能保证它们全部附加并且您想避免往返数据库(因为您确定标记至少存在于数据库中,如果附加或不连接),您可以用以下代码替换代码:

                    var tagInDb = context.ProductTags.Local
                        .SingleOrDefault(t => t.Id == tag.Id);
                    if (tagInDb == null) {
                        tagInDb = tag;
                        context.ProductTags.Attach(tagInDb);
                    }
                    productInDb.ProductTags.Add(tagInDb);

转载注明原文:c# – 在EF中保存对象时,数据库中没有新的多对多连接 - 代码日志