使用存根实体

假设我们在多对多关系中有 Products 和 Categorys:

public class Product
{
    public Product()
    {
        Categories = new HashSet<Category>();
    }
    public int ProductId { get; set; }
    public string ProductName { get; set; }
    public virtual ICollection<Category> Categories { get; private set; }
}

public class Category
{
    public Category()
    {
        Products = new HashSet<Product>();
    }
    public int CategoryId { get; set; }
    public string CategoryName { get; set; }
    public virtual ICollection<Product> Products { get; set; }
}

如果我们想要将 Category 添加到 Product,我们必须加载产品并将类别添加到其 Categories,例如:

不好的例子:

var product = db.Products.Find(1);
var category = db.Categories.Find(2);
product.Categories.Add(category);
db.SaveChanges();

(其中 dbDbContext 的子类)。

这会在 ProductCategory 之间的联结表中创建一条记录。但是,此表仅包含两个 Id 值。加载两个完整实体以创建一个小记录是浪费资源。

更有效的方法是使用在内存中创建的存根实体,即实体对象,仅包含最少的数据,通常只包含 Id 值。这就是它的样子:

好例子:

// Create two stub entities
var product = new Product { ProductId = 1 };
var category = new Category { CategoryId = 2 };

// Attach the stub entities to the context
db.Entry(product).State = System.Data.Entity.EntityState.Unchanged;
db.Entry(category).State = System.Data.Entity.EntityState.Unchanged;

product.Categories.Add(category);
db.SaveChanges();

最终结果是相同的,但它避免了两次到数据库的往返。

防止重复

你要检查关联是否已存在,便宜的查询就足够了。例如:

var exists = db.Categories.Any(c => c.Id == 1 && c.Products.Any(p => p.Id == 14));

同样,这不会将完整实体加载到内存中。它有效地查询联结表,只返回一个布尔值。