使用实体框架中的导航属性填充多对多关系表

Posted

技术标签:

【中文标题】使用实体框架中的导航属性填充多对多关系表【英文标题】:populate many-to-many relationship table using navigation properties in Entity Framework 【发布时间】:2016-05-09 11:54:02 【问题描述】:

我正在学习 asp.net mvc 应用程序中的实体框架。我有 3 个模型 -

AppModel、CategoryModel 和 App_CategoryModel(指定 AppModel 和 CategoryModel 之间的多对多关系)。一个sn-p是:

public class CategoryModel
    
        [Key]
        public int id  get; set; 
        public string Name get; set; 
        public virtual ICollection<App_CategoryModel> mapping  get; set; 
    

    public class AppModel
    
        [Key]
        public int id  get; set; 
        public string Name  get; set; 
        public virtual ICollection<App_CategoryModel> mapping  get; set; 
    

    public class App_CategoryModel
    
        [Key]
        public int id get; set;

        public int AppId get; set; 
        public int CategoryId get; set; 

        public virtual AppModel App get; set;
        public virtual CategoryModel Category get; set;
    

我遵循“代码优先”的方法,并且成功创建了表。但是,现在我被困在如何填充和显示这些信息上。 我有以下输入数据: List&lt;AppModel&gt; List&lt;CategoryModel&gt;Dictionary&lt;"appname", List&lt;CategoryModel&gt;&gt;

如何从这里继续,以便更新映射表?

另外,想了解这是否是表示数据的正确方法。由于一个应用程序可以有多个类别 - 我希望输出是一组独特的应用程序以及每个应用程序的类别列表,例如:

Dictionary<AppModel, List<CategoryModel>>

编辑: 这是我根据 smoksnes 的建议尝试的方法-

    List<CategoryModel> cat_list = new List<CategoryModel>();
    CategoryModel c1 = new CategoryModel();
    c1.Name = "string1";
    cat_list.Add(c1);

    CategoryModel c2 = new CategoryModel();
    c2.Name = "string2";
    cat_list.Add(c2);

    List<AppModel> app_list = new List<AppModel>();
    AppModel a1 = new AppModel();
    a1.Name = "app1";
    app_list.Add(a1);

    AppModel a2 = new AppModel();
    a2.Name = "app2";
    app_list.Add(a2);

    a1.mapping.Add(c1);
    a1.mapping.Add(c2);
    a2.mapping.Add(c1);
    a2.mapping.Add(c2);

    db.categories.AddRange(cat_list);
    db.apps.AddRange(app_list);

    db.SaveChanges();

在此之后,EF 担任专家 - 2 个类别、2 个应用程序和 4 个映射表中的条目。

虽然这有效,但不确定是谁阻止 EF 为类别创建 4 个条目?

【问题讨论】:

你需要App_CategoryModel 类吗?如果在CategoryModel中使用ICollection&lt;AppModel&gt;,在AppModel中使用ICollection&lt;CategoryModel&gt;,EF不会生成中间表吗? 【参考方案1】:

正如您在评论中提到的Barry O´Kane 一样,没有理由保留App_CategoryModel 模型。 EF 将为您管理。只有当它包含有关两个表之间关系的任何额外信息时,您才应该保留它。但是根据你的例子,没有理由保留它。

public class CategoryModel

    public CategoryModel()
    
        AppModels = new List<AppModel>();
    

    [Key]
    public int id  get; set; 
    public string Name get; set; 
    public virtual ICollection<AppModel> AppModels  get; set; 


public class AppModel

    public AppModel()
    
        // Not sure if this is actually needed anymore. But EF usually adds it.
        CategoryModels = new List<CategoryModel>();
    

    [Key]
    public int id  get; set; 
    public string Name  get; set; 
    public virtual ICollection<CategoryModel> CategoryModels  get; set; 

关于你关于代表的问题,我认为没有必要。由于AppModel 已经在其模型上连接了CategoryModel,因此没有理由使用Dictionary。您可以将其存储在 List&lt;AppModel&gt; 中。

IList<AppModel> myApps = context.AppModels.ToList();

foreach (var myApp in myApps)

    Console.Writeline("App 0 has the following categories:", myApp.id);
    foreach (var category in myApp.CategoryModels)
    
        Console.Writeline(category.Name);
    

当您想为应用添加类别时:

// I don't know how you create your Context, so below it's just called context.
var newCategoryModel = new CategoryModel

    Name = "SO is awesome!"
;
var appModel = context.AppModels.FirstOrDefault(x => x.id == 1);
appModel.CategoryModels.Add(newCategoryModel); // EF will automatically set foreign keys for you...
context.SaveChanges();

如果您想确保没有添加任何类别两次:

public void AddCategory(int appId, string categoryName)

    using(var context = new DbContext())
    
        var category = context.CategoryModels.FirstOrDefault(x => x.Name == categoryName);
        if(category == null)
        
            // Only create new CategoryModel if it doesn't exist.
            category = new CategoryModel
            
                Name = categoryName
            ;
        
        var appModel = new AppModel
        
            id = appId
        ;
        // Attach to save on db-trip
        context.AppModels.Attach(appModel);

        //TODO: Possibly check if this particular appModel already has this category?
        appModel.CategoryModels.Add(category);
        context.SaveChanges();
    

【讨论】:

谢谢!!将尝试并更新。我的问题不清楚关于要求 - 我有一个类别列表和一个应用程序列表,因此使用这种方法我需要先更新 db 中的类别,然后使用返回的主键更新应用程序内部,然后存储db 中的应用程序。否则,如果多个应用程序有多个类别,我可能会在类别表中出现不必要的重复项。 @pankaj,是的,您必须使用您的数据更新您的表格。但是这个解决方案没有什么不同。将生成相同的数据库表。只是需要维护的代码更少。我还添加了一个示例函数,您可以在其中添加新类别并根据名称检查类别是否已存在。 你是从哪里得到这个 appid 的? 它是方法签名的一部分。这只是一个示例,说明如何为已知应用添加类别。 这就是我所做的:将类别列表添加到 db,然后在 db 中添加具有类别列表的应用程序。不知何故,我看到,EF 自己设法不添加重复的类别。我将编辑我的问题,向您展示我做了什么。

以上是关于使用实体框架中的导航属性填充多对多关系表的主要内容,如果未能解决你的问题,请参考以下文章

EF更新多对多关系表中记录的时候,无法更新关系表的问题。

实体框架:使用现有数据(种子)填充多对多关系

EntityFramework同一张表多对多关系

实体框架中的多对多关系导致无限循环

Remove() 不适用于实体框架中的多对多关系

实体框架6:多对多关系问题[关闭]