简体   繁体   中英

ef core many to many relation controller

I am trying to do a controller for a many-to-many relationship between user and project using Entity Framework Core Database First. I have used scaffolding to create the models from database./ create/Add is working but the rest of CRUD isn't ( Edit, details ). How can i achieve that? what am i doing wrong?

when I click on the edit or get details this error appear the Error: This page can't be found

userproject tabel creation

CREATE TABLE [user_poject] (
    [user_id] INT , 
    [project_id] INT ,
    [user_roles] nvarchar(225)
    CONSTRAINT [user_project_PK] PRIMARY KEY ([user_id], [project_id]),

    CONSTRAINT fk_userProject_user
     FOREIGN KEY([user_id]) REFERENCES [user]([user_id]) ON DELETE CASCADE,

    CONSTRAINT fk_userProject_project 
     FOREIGN KEY([project_id]) REFERENCES [project]([project_id]) ON DELETE CASCADE
);

The Controller:

public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var userPoject = await _context.UserPojects
                                   .Include(u => u.Project)
                                   .Include(u => u.User)
                                   .FirstOrDefaultAsync(m => m.UserId == id);
    if (userPoject == null)
    {
        return NotFound();
    }

    return View(userPoject);
}

// GET: UserPojects1/Edit/5
public async Task<IActionResult> Edit(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var userPoject = await _context.UserPojects.FindAsync(id);
    if (userPoject == null)
    {
        return NotFound();
    }
    ViewData["ProjectId"] = new SelectList(_context.Projects, "ProjectId", "ProjectName", userPoject.ProjectId);
    ViewData["UserId"] = new SelectList(_context.Users, "UserId", "UserName", userPoject.UserId);
    return View(userPoject);
}

// POST: UserPojects1/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("UserId,ProjectId,UserRoles")] UserPoject userPoject)
{
    if (id != userPoject.UserId)
    {
        return NotFound();
    }

    if (ModelState.IsValid)
    {
        try
        {
            _context.Update(userPoject);
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!UserPojectExists(userPoject.UserId))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }
        return RedirectToAction(nameof(Index));
    }
    ViewData["ProjectId"] = new SelectList(_context.Projects, "ProjectId", "ProjectName", userPoject.ProjectId);
    ViewData["UserId"] = new SelectList(_context.Users, "UserId", "UserName", userPoject.UserId);
    return View(userPoject);
}

UserProject Model:

public partial class UserPoject
{
    [Key]
    [Column("user_id" , Order =1)]
    public int UserId { get; set; }
    [Key]
    [Column("project_id" , Order =2)]
    public int ProjectId { get; set; }
    [Column("user_roles")]
    [StringLength(225)]
    public string UserRoles { get; set; }

    [ForeignKey(nameof(ProjectId))]
    [InverseProperty("UserPojects")]
    public virtual Project Project { get; set; }
    [ForeignKey(nameof(UserId))]
    [InverseProperty("UserPojects")]
    public virtual User User { get; set; }
}

User Model:

  public partial class User
    {
        public User()
        {
            Tasks = new HashSet<Task>();
            UserPojects = new HashSet<UserPoject>();
            UserSprints = new HashSet<UserSprint>();
        }

        [Key]
        [Column("user_id")]
        public int UserId { get; set; }
        [Required]
        [Column("user_name")]
        [StringLength(50)]
        public string UserName { get; set; }
        [Column("user_email")]
        [StringLength(225)]
        public string UserEmail { get; set; }

        [InverseProperty(nameof(Task.User))]
        public virtual ICollection<Task> Tasks { get; set; }
        [InverseProperty(nameof(UserPoject.User))]
        public virtual ICollection<UserPoject> UserPojects { get; set; }
        [InverseProperty(nameof(UserSprint.User))]
        public virtual ICollection<UserSprint> UserSprints { get; set; }
    }

Project Model

 public partial class Project
    {
        public Project()
        {
            Sprints = new HashSet<Sprint>();
            UserPojects = new HashSet<UserPoject>();
        }

        [Key]
        [Column("project_id")]
        public int ProjectId { get; set; }
        [Column("project_name")]
        [StringLength(50)]
        public string ProjectName { get; set; }
        [Column("Project_description")]
        [StringLength(225)]
        public string ProjectDescription { get; set; }

        [InverseProperty(nameof(Sprint.Project))]
        public virtual ICollection<Sprint> Sprints { get; set; }
        [InverseProperty(nameof(UserPoject.Project))]
        public virtual ICollection<UserPoject> UserPojects { get; set; }
    }

DBContext-Fluent API


            modelBuilder.Entity<UserPoject>(entity =>
            {
                entity.HasKey(e => new { e.UserId, e.ProjectId })
                    .HasName("PK__user_poj__5279AEEECFA4D53D");

                entity.HasOne(d => d.Project)
                    .WithMany(p => p.UserPojects)
                    .HasForeignKey(d => d.ProjectId)
                    .HasConstraintName("FK__user_poje__proje__33D4B598");

                entity.HasOne(d => d.User)
                    .WithMany(p => p.UserPojects)
                    .HasForeignKey(d => d.UserId)
                    .HasConstraintName("FK__user_poje__user___32E0915F");
            });

I'm seeing a few things, first you will want to use a "PUT" instead of a "POST" for the updated operation. The caching policy, will allow redundant entries to be collapsed. Additionally there are ordering rules for PUT where there aren't for POST. It should be safe for a client to send POST operations in any order and get the same result.

But for the original problem, you are going to want to tell the conext that you have modified the entity.

From a Microsoft example

// PUT: api/TodoItems/5
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
{
    if (id != todoItem.Id)
    {
        return BadRequest();
    }

    _context.Entry(todoItem).State = EntityState.Modified;

    try
    {
        await _context.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!TodoItemExists(id))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }

    return NoContent();
}

If that is still giving you an issue, also remove the explicit Bindings, you have the model class proper decorated with attributes.

when I click on the edit or get details this error appear the Error: This page can't be found

public async Task<IActionResult> Details(int? id) { if (id == null) { return NotFound(); } var userPoject = await _context.UserPojects.Include(u => u.Project).Include(u => u.User).FirstOrDefaultAsync(m => m.UserId == id); if (userPoject == null) { return NotFound(); } return View(userPoject); }

As we discuss previous, by using the above code, if the id is null, it will return the NotFound page. So, the issue is related to the Edit and Detail hyper link on the Index page. Please refer the following code (in the ActionLink method, add the id parameter using the primary key):

@model IEnumerable<netcore5.Models.UserPoject>     
<table class="table">
    <thead>
           @*header*@
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.UserId)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ProjectId)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.UserRoles)
            </td>
            <td>
                @Html.ActionLink("Edit", "Edit", new {  id=item.UserId }) |
                @Html.ActionLink("Details", "Details", new {  id=item.UserId }) |
                @Html.ActionLink("Delete", "Delete", new { id=item.UserId  })
            </td>
        </tr>
}
    </tbody>
</table>

Besides, in the Startup.Configure method, if you are using the default MVC route template, like this:

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");

            endpoints.MapRazorPages();
        });

Then, you could also use the following Anchor Tag Helper to add link with the id parameter:

        <td>
            <a asp-action="Edit" asp-route-id="@item.UserId">Edit</a> |
            <a asp-action="Details" asp-route-id="@item.UserId">Details</a> |
            <a asp-action="Delete" asp-route-id="@item.UserId">Delete</a>
        </td>

The result like this:

在此处输入图像描述

Reference:

Anchor Tag Helper in ASP.NET Core

Tutorial: Implement CRUD Functionality - ASP.NET MVC with EF Core

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM