5

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

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

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

1 回答 1

6

你要知道这条线...

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

...对关系的状态没有影响。product它只是将传递给的实体标记EntryModified,即标量属性Product.Name被标记为已修改,仅此而已。UPDATE发送到数据库的 SQL语句只会更新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在上面的代码中使用,因为我不确定您的代码片段(InsertAndOrUse缺少确切的代码)是否product.ProductTags集合中的标签附加到context实例。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);
于 2013-08-03T15:07:52.430 回答