1

.Net 6 Code first DB with EF

I have two MVC-pairs, one to create + display an "Incident/Event" and one to create + display "Transaction/Person" tied to a specific event. What I'm looking to achieve is that when you create Transaction/Person you pick one of the existing already created (isActive) Incident/Event from a dropdownlist during creation.

After creation I want to display the created Transaction/Person object with both it's own properties as well as the chosen incident/event in a view(table). How do you suggest I solve this the best way? I will try to supply some code but let me know if it's insufficient, still very new .NET MVC and a fair amount of it is autogenerated.

If some variable is missing or name doesn't match completely it's me trying to remove a lot of variables and stuff to make it easier to read. the code runs fine just that Incident/Event and Transaction/Person are currently completely seperate and needs to be tied together.

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using CRUD1.Models;
using ClosedXML.Excel;
using System.Data;

namespace CRUD1.Controllers
{
    public class TransactionController : Controller
    {
        private readonly myDbContext _context;

        public TransactionController(myDbContext context)
        {
            _context = context;
        }

        public IActionResult Index()
        {
            List<Transaction> transactions = (from Transaction in _context.Transactions
                                        select Transaction).ToList();
            return View(transactions);
        }


        // GET: Transaction/Details/5
        public async Task<IActionResult> Details(int? id)
        {
            if (id == null || _context.Transactions == null)
            {
                return NotFound();
            }

            var transaction = await _context.Transactions
                .FirstOrDefaultAsync(m => m.CallerId == id);
            if (transaction == null)
            {
                return NotFound();
            }

            return View(transaction);
        }

        // GET: Transaction/Create
        public IActionResult Create()
        {            
            return View();
        }

        // POST: Transaction/Create

        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Create([Bind("CallerId,Date,CallerName")] Transaction transaction)
        {            
            if (ModelState.IsValid)
            {
                _context.Add(transaction);
                await _context.SaveChangesAsync();
                return RedirectToAction(nameof(Index));
            }
            return View(transaction);
        }

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

            var transaction = await _context.Transactions.FindAsync(id);
            if (transaction == null)
            {
                return NotFound();
            }
            return View(transaction);
        }

        // POST: Transaction/Edit/5
       
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Edit(int id, [Bind("CallerId,Date,CallerName")] Transaction transaction)
        {
            if (id != transaction.CallerId)
            {
                return NotFound();
            }

            if (ModelState.IsValid)
            {
                try
                {
                    _context.Update(transaction);
                    await _context.SaveChangesAsync();
                }
                catch (DbUpdateConcurrencyException)
                {
                    if (!TransactionExists(transaction.CallerId))
                    {
                        return NotFound();
                    }
                    else
                    {
                        throw;
                    }
                }
                return RedirectToAction(nameof(Index));
            }
            return View(transaction);
        }

        // GET: Transaction/Delete/5
        public async Task<IActionResult> Delete(int? id)
        {
            if (id == null || _context.Transactions == null)
            {
                return NotFound();
            }

            var transaction = await _context.Transactions
                .FirstOrDefaultAsync(m => m.CallerId == id);
            if (transaction == null)
            {
                return NotFound();
            }

            return View(transaction);
        }

        // POST: Transaction/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> DeleteConfirmed(int id)
        {
            if (_context.Transactions == null)
            {
                return Problem("Entity set 'TransactionDbContext.Transactions'  is null.");
            }
            var transaction = await _context.Transactions.FindAsync(id);
            if (transaction != null)
            {
                _context.Transactions.Remove(transaction);
            }
            
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }

        private bool TransactionExists(int id)
        {
          return (_context.Transactions?.Any(e => e.CallerId == id)).GetValueOrDefault();
        }
    }
}

The Incident-model

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace CRUD1.Models
{
    public class Incident
    {
        [Key]
        public int IncidentId { get; set; }

        public string IncidentName { get; set; }
 
        public bool isActive { get; set; }
    }
}

Transaction model

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace CRUD1.Models
{
    public class Transaction
    {
        [Key]
        public int CallerId { get; set; }
        
        public DateTime Date { get; set; } = DateTime.Now;
  
        public string CallerName { get; set; }    

    }
}

Transaction/Person - Create view (Here is where I'm looking to create a dropdownbox to chose from Incidents)

@model CRUD1.Models.Transaction

@{
    ViewData["Title"] = "Create";
}

<h4>New Person/transaction</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Create">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            
            <div class="form-group">
                <label asp-for="CallerName" class="control-label"></label>
                <input asp-for="CallerName" class="form-control" />
                <span asp-validation-for="CallerName" class="text-danger"></span>
            </div>
   
            <div class="form-group">
                <input type="submit" value="Send" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>


@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}


Completely autogenerated "Incident/Event" - Controller

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using CRUD1.Models;

namespace CRUD1.Controllers
{
    public class IncidentController : Controller
    {
        private readonly myDbContext _context;

        public IncidentController(myDbContext context)
        {
            _context = context;
        }

        // GET: Incident
        public async Task<IActionResult> Index()
        {
              return View(await _context.Incidents.ToListAsync());
        }

        // GET: Incident/Details/5
        public async Task<IActionResult> Details(int? id)
        {
            if (id == null || _context.Incidents == null)
            {
                return NotFound();
            }

            var incident = await _context.Incidents
                .FirstOrDefaultAsync(m => m.IncidentId == id);
            if (incident == null)
            {
                return NotFound();
            }

            return View(incident);
        }

        // GET: Incident/Create
        public IActionResult Create()
        {
            return View();
        }

        // POST: Incident/Create

        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Create([Bind("IncidentId,IncidentName,isActive")] Incident incident)
        {
            if (ModelState.IsValid)
            {
                _context.Add(incident);
                await _context.SaveChangesAsync();
                return RedirectToAction(nameof(Index));
            }
            return View(incident);
        }

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

            var incident = await _context.Incidents.FindAsync(id);
            if (incident == null)
            {
                return NotFound();
            }
            return View(incident);
        }

        // POST: Incident/Edit/5

        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Edit(int id, [Bind("IncidentId,IncidentName,isActive")] Incident incident)
        {
            if (id != incident.IncidentId)
            {
                return NotFound();
            }

            if (ModelState.IsValid)
            {
                try
                {
                    _context.Update(incident);
                    await _context.SaveChangesAsync();
                }
                catch (DbUpdateConcurrencyException)
                {
                    if (!IncidentExists(incident.IncidentId))
                    {
                        return NotFound();
                    }
                    else
                    {
                        throw;
                    }
                }
                return RedirectToAction(nameof(Index));
            }
            return View(incident);
        }

        // GET: Incident/Delete/5
        public async Task<IActionResult> Delete(int? id)
        {
            if (id == null || _context.Incidents == null)
            {
                return NotFound();
            }

            var incident = await _context.Incidents
                .FirstOrDefaultAsync(m => m.IncidentId == id);
            if (incident == null)
            {
                return NotFound();
            }

            return View(incident);
        }

        // POST: Incident/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> DeleteConfirmed(int id)
        {
            if (_context.Incidents == null)
            {
                return Problem("Entity set 'myDbContext.Incidents'  is null.");
            }
            var incident = await _context.Incidents.FindAsync(id);
            if (incident != null)
            {
                _context.Incidents.Remove(incident);
            }
            
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }

        private bool IncidentExists(int id)
        {
          return _context.Incidents.Any(e => e.IncidentId == id);
        }
    }
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Eyetee
  • 15
  • 3

1 Answers1

1

Here is a sample about how to create a Transaction with Incident dropdownlist and display the created Transaction with selected Incident:

Model

public class Incident
{
    [Key]
    public int IncidentId { get; set; }
    public string? IncidentName { get; set; }
    public bool isActive { get; set; }
    public List<Transaction>? Transaction { get; set; }
}
public class Transaction
{
    [Key]
    public int CallerId { get; set; }
    public DateTime Date { get; set; } = DateTime.Now;
    public string? CallerName { get; set; }
    public Incident? Incident { get; set; }
}

View

1.Create.cshtml

@model Transaction

@{
    ViewData["Title"] = "Create";
}

<h1>Create</h1>

<h4>Transaction</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Create">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Date" class="control-label"></label>
                <input asp-for="Date" class="form-control" />
                <span asp-validation-for="Date" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="CallerName" class="control-label"></label>
                <input asp-for="CallerName" class="form-control" />
                <span asp-validation-for="CallerName" class="text-danger"></span>
            </div>
             <div class="form-group">
                <label asp-for="Incident.IncidentId" class="control-label"></label>
                <select asp-for="Incident.IncidentId" asp-items="@ViewBag.Incident" class="form-control">
                    <option>select an option</option>
                </select>
                <span asp-validation-for="CallerName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

2.Index.cshtml

@model IEnumerable<Transaction>

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Date)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.CallerName)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Incident.IncidentName)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Date)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.CallerName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Incident.IncidentName)
            </td>
            <td>
                <a asp-action="Edit" asp-route-id="@item.CallerId">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.CallerId">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.CallerId">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Controller

public class TransactionsController : Controller
{
    private readonly MvcProjContext _context;

    public TransactionsController(MvcProjContext context)
    {
        _context = context;
    }

    // GET: Transactions
    public async Task<IActionResult> Index()
    {
          return View(await _context.Transactions.Include(a=>a.Incident).ToListAsync());
    }

    
    // GET: Transactions/Create
    public IActionResult Create()
    {
        //display the dropdownlist
        ViewBag.Incident = new SelectList(_context.Incidents.ToList(), "IncidentId", "IncidentName");
        return View();
    }

    // POST: Transactions/Create       
    [HttpPost]
    public async Task<IActionResult> Create([FromForm]Transaction transaction)
    {
        if (ModelState.IsValid)
        {
            transaction.Incident = _context.Incidents.Find(transaction.Incident.IncidentId);
            _context.Add(transaction);
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }
        ViewBag.Incident = new SelectList(_context.Incidents.ToList(), "IncidentId", "IncidentName");
        return View(transaction);
    }
}
Rena
  • 30,832
  • 6
  • 37
  • 72
  • Extremly helpful, but one issue remains. ModelState.IsValid returns false in TransactionController - the Post method(POST: Transactions/Create), I get an error that IncidentName is required(And SMS-text one of the variables I cut out). The IncidentName isn't required. The only change I made to your code was: -> Callername to Incident.IncidentId in the Create view – Eyetee Oct 12 '22 at 08:26
  • Managed to solve this by adding: ModelState.Remove("Incident.SMSText"); and ModelState.Remove("Incident.IncidentName"); I dont full understand it but I take it that it's checking to see if it can display the attributes but since they aren't interesting to show in the Transaction view I simply removed them from the model. Let me know if this is a terrible idea and why. – Eyetee Oct 12 '22 at 10:11
  • Hi @Eyetee, did you check my model? I think you need check again. In asp.net 6, non-nullable property will be required by default, so I use `?` in the property: `public string? IncidentName`. – Rena Oct 13 '22 at 02:13
  • Yes, I did follow it exactly which is why I was surpised that it would call the value required. Also completely removed and reran all migrations/update DB just incase something didn't properly update on the DB-side on the latest migration/update but it still threw an error until I used ModelState.Remove("Incident.IncidentName"). I have one more question, how would you add an "IF" so it only populates the dropdownlist with options where Incident.isActive is true? Assume it would be included on this row ViewBag.Incident = new SelectList(_context.Incidents.ToList(), "IncidentId", "IncidentName"); – Eyetee Oct 13 '22 at 05:56
  • You want to populate the dropdown where Incident.isActive is true? You mean `ViewBag.Incident = new SelectList(_context.Incidents.Where(a=>a.isActive ==true).ToList(), "IncidentId", "IncidentName");`? – Rena Oct 13 '22 at 06:06
  • Hi @Eyetee, any update now? Did my answer help you resolve the issue? – Rena Oct 14 '22 at 00:32
  • 1
    Worked perfectly @Rena all your help has been greatly appreciated! – Eyetee Oct 14 '22 at 05:13