locked
Adding Objects to a Model (empty List) inside View using a "Many-to-Many" Relationship RRS feed

  • Question

  • User1058269376 posted

    Hey guys, so I'm fairly new with .Net and I've been trying to figure how to do this correctly but I've been struggling quite a bit. So in case you can take a minute to help me, here's the deal. If you see something very stupid in my code, I'd like to remind you that I'm not totally confortable with this, so my apologies. Also, if this is not the right place to leave a question like this, or if you have any helpful link that can lead me to a possible solution, please leave it here. (Thank you very much!)

    Given the following Models:

    public class Project
    
    {
    
    [Key, Required]
    
    public int ProjectID { get; set; }
    
    [Required]
    
    public string Title { get; set; }
    
    public string Description { get; set; }
    
    public System.DateTime Date_Proposal { get; set; }
    
    public int Up_Votes { get; set; }
    
    public int Down_Votes { get; set; }
    
    
    
    public virtual List<ProjectSector> ProjectSectors { get; set; }
    
    }

    public class Sector
    
    {
    
    [Key, Required]
    
    public int SectorID { get; set; }
    
    [Required]
    
    [Display(Name = "Sector Title")]
    
    public string Title { get; set; }
    
    [DefaultValue("No Description")]
    
    [Display(Name = "Sector Description")]
    
    public string Description { get; set; }
    
    //public System.Drawing.Image Image { get; set; }
    
    
    
    public virtual List<ProjectSector> ProjectSectors { get; set; }
    
    }


    and

    public class ProjectSector
    
    {
    
    [Key, Required]
    
    [Column(Order = 1)]
    
    public int ProjectID { get; set; }
    
    [Key, Required]
    
    [Column(Order = 2)]
    
    public int SectorID { get; set; }
    
    
    
    public virtual Project Project { get; set; }
    
    public virtual Sector Sector { get; set; }
    
    }


    Where a Project can be assigned to multiple sectors, and a Sector can contain multiple projects. I created the Model ProjectSector to represent this kind of relationship.

    Now, during the Create action from the Controller Projects other than the default Editors for the Title and Description I would like to display some kind of Table with Buttons where you can select multiple (and leave selected status ON for the ones you clicked on), and once you send the Post request to create a new Instance of Project, I would like to have the controller automatically adding ProjectSectors for each Sector that the user chose the Project to be assigned to.

    To do this, I'm passing the Sectors that are currently stored in my database through ViewData:

    public ActionResult Create()
    
    {
    
    IEnumerable<Sector> Sectors = db.Sectors.ToList();
    
    ViewData["Sectors"] = Sectors;
    
    return View();
    
    }


    I was able to get this kind of layout using a table with Input buttons of type "Checkbox". But they're not interacting with the server in any way, and I would prefer actual buttons where I could just leave their Status as "Selected" and not update them every time I click on another one.

    TL;DR: How can I create a view for a Project where I can show all Sectors from Database (Project has none during creation) and assign them to the Project Model by creating a new entry "ProjectSector" for each assignment between the Project and each Sector selected? Is it attainable using an Editor View?

    Tuesday, March 19, 2019 1:53 PM

Answers

  • User1520731567 posted

    Hi Vunoo,

    Actually,you just add Project and Sector model,EF automatically creates a joining table (ProjectSector) with the name of the both entities and the suffix 's'.

    EF 6 includes default conventions for many-to-many relationships .You need to include a collection navigation property at both ends without any configuration. 

    For example:

    public class Project
    {
    
    public Project()
    {
    this.Sectors= new HashSet<Sector>();
    }
    [Key, Required] public int ProjectID { get; set; } [Required] public string Title { get; set; } public string Description { get; set; } public System.DateTime Date_Proposal { get; set; } public int Up_Votes { get; set; } public int Down_Votes { get; set; }
    public virtual List<Sector> Sectors{ get; set; }//Navigation properties
    } public class Sector {
    public Sector()
    {
    this.Projects= new HashSet<Project>();
    } [Key, Required] public int SectorID { get; set; } [Required] [Display(Name = "Sector Title")] public string Title { get; set; } [DefaultValue("No Description")] [Display(Name = "Sector Description")] public string Description { get; set; } //public System.Drawing.Image Image { get; set; } public virtual List<Project> Projects{ get; set; } //Navigation properties
    }
    public class ProjectSector { [Key, Required] [Column(Order = 1)] public int ProjectID { get; set; } [Key, Required] [Column(Order = 2)] public int SectorID { get; set; } public virtual Project Project { get; set; } public virtual Sector Sector { get; set; } }

    And then add-migration and update-database by code-first.

    The default conventions for many-to-many relationships creates a joining table with the default naming conventions.

    You could use Fluent API to customize a joining table name and column names.

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
    
        modelBuilder.Entity<Deal>()
                    .HasMany<Product>(s => s.Products)
                    .WithMany(c => c.Deals)
                    .Map(cs =>
                            {
                                cs.MapLeftKey("ProjectID");
                                cs.MapRightKey("SectorID");
                                cs.ToTable("ProjectSector");
                            });
    
    }

    More details,,you could refer to this article,here is the Tutorial about Configure Many-to-Many Relationships in Code-First:

    http://www.entityframeworktutorial.net/code-first/configure-many-to-many-relationship-in-code-first.aspx

    Vunoo

    TL;DR: How can I create a view for a Project where I can show all Sectors from Database (Project has none during creation) and assign them to the Project Model by creating a new entry "ProjectSector" for each assignment between the Project and each Sector selected? Is it attainable using an Editor View?

    According to this requirement,You could refer to these links:

    https://forums.asp.net/post/6230028.aspx

    https://forums.asp.net/p/2149331/6238609.aspx?Re+how+to+get+selected+values+from+a+checkboxlist+in+mvc+

    Best Regards.

    Yuki Tao

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, March 20, 2019 8:50 AM

All replies

  • User-474980206 posted

    you should design the UI first via mockups. Then a view model that supports the UI. then create the mapping layer from the database to view model.

    Tuesday, March 19, 2019 3:14 PM
  • User1120430333 posted

    You should keep this in mind.

    https://www.c-sharpcorner.com/UploadFile/56fb14/understanding-separation-of-concern-and-Asp-Net-mvc/

    https://www.c-sharpcorner.com/UploadFile/56fb14/understanding-separation-of-concern-and-Asp-Net-mvc/

    http://sampathloku.blogspot.com/2012/10/how-to-use-viewmodel-with-aspnet-mvc.html

    https://docs.microsoft.com/en-us/aspnet/mvc/overview/older-versions-1/overview/understanding-models-views-and-controllers-cs

    <copied>

    An MVC model contains all of your application logic that is not contained in a view or a controller. The model should contain all of your application business logic, validation logic, and database access logic. For example, if you are using the Microsoft Entity Framework to access your database, then you would create your Entity Framework classes (your .edmx file) in the Models folder.

    A view should contain only logic related to generating the user interface. A controller should only contain the bare minimum of logic required to return the right view or redirect the user to another action (flow control). Everything else should be contained in the model.

    In general, you should strive for fat models and skinny controllers. Your controller methods should contain only a few lines of code. If a controller action gets too fat, then you should consider moving the logic out to a new class in the Models folder.

    <end>

    Tuesday, March 19, 2019 6:42 PM
  • User1520731567 posted

    Hi Vunoo,

    Actually,you just add Project and Sector model,EF automatically creates a joining table (ProjectSector) with the name of the both entities and the suffix 's'.

    EF 6 includes default conventions for many-to-many relationships .You need to include a collection navigation property at both ends without any configuration. 

    For example:

    public class Project
    {
    
    public Project()
    {
    this.Sectors= new HashSet<Sector>();
    }
    [Key, Required] public int ProjectID { get; set; } [Required] public string Title { get; set; } public string Description { get; set; } public System.DateTime Date_Proposal { get; set; } public int Up_Votes { get; set; } public int Down_Votes { get; set; }
    public virtual List<Sector> Sectors{ get; set; }//Navigation properties
    } public class Sector {
    public Sector()
    {
    this.Projects= new HashSet<Project>();
    } [Key, Required] public int SectorID { get; set; } [Required] [Display(Name = "Sector Title")] public string Title { get; set; } [DefaultValue("No Description")] [Display(Name = "Sector Description")] public string Description { get; set; } //public System.Drawing.Image Image { get; set; } public virtual List<Project> Projects{ get; set; } //Navigation properties
    }
    public class ProjectSector { [Key, Required] [Column(Order = 1)] public int ProjectID { get; set; } [Key, Required] [Column(Order = 2)] public int SectorID { get; set; } public virtual Project Project { get; set; } public virtual Sector Sector { get; set; } }

    And then add-migration and update-database by code-first.

    The default conventions for many-to-many relationships creates a joining table with the default naming conventions.

    You could use Fluent API to customize a joining table name and column names.

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
    
        modelBuilder.Entity<Deal>()
                    .HasMany<Product>(s => s.Products)
                    .WithMany(c => c.Deals)
                    .Map(cs =>
                            {
                                cs.MapLeftKey("ProjectID");
                                cs.MapRightKey("SectorID");
                                cs.ToTable("ProjectSector");
                            });
    
    }

    More details,,you could refer to this article,here is the Tutorial about Configure Many-to-Many Relationships in Code-First:

    http://www.entityframeworktutorial.net/code-first/configure-many-to-many-relationship-in-code-first.aspx

    Vunoo

    TL;DR: How can I create a view for a Project where I can show all Sectors from Database (Project has none during creation) and assign them to the Project Model by creating a new entry "ProjectSector" for each assignment between the Project and each Sector selected? Is it attainable using an Editor View?

    According to this requirement,You could refer to these links:

    https://forums.asp.net/post/6230028.aspx

    https://forums.asp.net/p/2149331/6238609.aspx?Re+how+to+get+selected+values+from+a+checkboxlist+in+mvc+

    Best Regards.

    Yuki Tao

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, March 20, 2019 8:50 AM
  • User1058269376 posted

    Thanks for the reply, bruce. I've created a new ViewModel that holds all the data I needed and like this it looks like things work as planned. Thank you!

    Tuesday, March 26, 2019 9:02 AM
  • User1058269376 posted

    Thank you so much for the detailed answer you've given me. I truly appreciated it!

    Tuesday, March 26, 2019 9:07 AM