Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

ASP.Net MVC ID field not filled in database

I’m almost new to MVC and trying to create a simple app for data management (MVC 5/VS 2022) using this tutorial. I have two classes along with their controllers and views: Site and Warehouse. Warehouse is site-dependent, so this is its model:

Public Class Warehouse
    Implements IValidatableObject

    Private mId As Integer
    Private mSite As Site
    Private mWarehouseID As String
    Private mWarehouseName As String

    <Display(Name:="Site"), Required>
    Public SiteID As Integer

    <Key>
    Public Property Id As Integer
        Get
            Return mId
        End Get
        Set(value As Integer)
            mId = value
        End Set
    End Property

    <Display(Name:="Site")>
    Public Overridable Property Site As Site
        Get
            Return mSite
        End Get
        Set(value As Site)
            mSite = value
        End Set
    End Property

    <Display(Name:="Warehouse ID"), Required, Index(IsUnique:=True), MaxLength(20, ErrorMessage:="Maximum allowed length is 20 characters")>
    Public Property WarehouseID As String
        Get
            Return mWarehouseID
        End Get
        Set(value As String)
            mWarehouseID = value
        End Set
    End Property

    <Display(Name:="Warehouse Name"), Required, MaxLength(100, ErrorMessage:="Maximum allowed length is 100 characters")>
    Public Property WarehouseName As String
        Get
            Return mWarehouseName
        End Get
        Set(value As String)
            mWarehouseName = value
        End Set
    End Property

    Public Function Validate(validationContext As ValidationContext) As IEnumerable(Of ValidationResult) Implements IValidatableObject.Validate
        Dim db As New Data.KFI_IPPContext
        Dim vresult As New List(Of ValidationResult)
        Dim validatename = db.Warehouses.FirstOrDefault(Function(w) (w.WarehouseID = WarehouseID) And (w.Id <> Id))

        If validatename IsNot Nothing Then
            Dim errmsg As New ValidationResult($"Warehouse {WarehouseID} already exists.")

            vresult.Add(errmsg)
        End If

        Return vresult
    End Function
End Class

And this is controller:

Public Class WarehousesController
    Inherits System.Web.Mvc.Controller

    Private db As New KFI_IPPContext

    ' GET: Warehouses
    Public Async Function Index(SearchString As String) As Threading.Tasks.Task(Of ActionResult)
        Dim warehouses = From w In db.Warehouses

        If String.IsNullOrEmpty(SearchString) Then
            warehouses = From w In warehouses Select w
        Else
            warehouses = From w In warehouses Where w.WarehouseName.Contains(SearchString) Or w.WarehouseID.Contains(SearchString) Or w.Site.SiteName.Contains(SearchString)
        End If
        Return View(Await warehouses.ToListAsync)
    End Function

    ' GET: Warehouses/Details/5
    Function Details(ByVal id As Integer?) As ActionResult
        If IsNothing(id) Then
            Return New HttpStatusCodeResult(HttpStatusCode.BadRequest)
        End If
        Dim warehouse As Warehouse = db.Warehouses.Find(id)
        If IsNothing(warehouse) Then
            Return HttpNotFound()
        End If
        Return View(warehouse)
    End Function

    ' GET: Warehouses/Create
    Function Create() As ActionResult
        PopulateSiteList()
        Return View()
    End Function

    ' POST: Warehouses/Create
    'To protect from overposting attacks, enable the specific properties you want to bind to, for 
    'more details see https://go.microsoft.com/fwlink/?LinkId=317598.
    <HttpPost()>
    <ValidateAntiForgeryToken()>
    Function Create(<Bind(Include:="Id,WarehouseID,WarehouseName")> ByVal warehouse As Warehouse) As ActionResult
        If ModelState.IsValid Then
            db.Warehouses.Add(warehouse)
            db.SaveChanges()
            Return RedirectToAction("Index")
        End If
        PopulateSiteList(warehouse.SiteID)
        Return View(warehouse)
    End Function

    ' GET: Warehouses/Edit/5
    Function Edit(ByVal id As Integer?) As ActionResult
        If IsNothing(id) Then
            Return New HttpStatusCodeResult(HttpStatusCode.BadRequest)
        End If
        Dim warehouse As Warehouse = db.Warehouses.Find(id)
        If IsNothing(warehouse) Then
            Return HttpNotFound()
        End If
        PopulateSiteList(warehouse.SiteID)
        Return View(warehouse)
    End Function

    ' POST: Warehouses/Edit/5
    'To protect from overposting attacks, enable the specific properties you want to bind to, for 
    'more details see https://go.microsoft.com/fwlink/?LinkId=317598.
    <HttpPost()>
    <ValidateAntiForgeryToken()>
    Function Edit(<Bind(Include:="Id,WarehouseID,WarehouseName")> ByVal warehouse As Warehouse) As ActionResult
        If ModelState.IsValid Then
            db.Entry(warehouse).State = EntityState.Modified
            db.SaveChanges()
            Return RedirectToAction("Index")
        End If
        PopulateSiteList(warehouse.SiteID)
        Return View(warehouse)
    End Function

    ' GET: Warehouses/Delete/5
    Function Delete(ByVal id As Integer?) As ActionResult
        If IsNothing(id) Then
            Return New HttpStatusCodeResult(HttpStatusCode.BadRequest)
        End If
        Dim warehouse As Warehouse = db.Warehouses.Find(id)
        If IsNothing(warehouse) Then
            Return HttpNotFound()
        End If
        Return View(warehouse)
    End Function

    ' POST: Warehouses/Delete/5
    <HttpPost()>
    <ActionName("Delete")>
    <ValidateAntiForgeryToken()>
    Function DeleteConfirmed(ByVal id As Integer) As ActionResult
        Dim warehouse As Warehouse = db.Warehouses.Find(id)
        db.Warehouses.Remove(warehouse)
        db.SaveChanges()
        Return RedirectToAction("Index")
    End Function

    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        If (disposing) Then
            db.Dispose()
        End If
        MyBase.Dispose(disposing)
    End Sub

    Private Sub PopulateSiteList(Optional SelectedSite As Object = Nothing)
        Dim sitequery = From s In db.Sites Order By s.SiteID Select s

        ViewData("SiteID") = New SelectList(sitequery, "SiteID", "SiteName", SelectedSite)
    End Sub
End Class

And this is view for creating warehouses (Only "Using" part):

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

@Using (Html.BeginForm())
    @Html.AntiForgeryToken()

    @<div class="form-horizontal">
    <h4>Warehouse</h4>
    <hr />
    @Html.ValidationSummary(True, "", New With {.class = "text-danger"})
    <div class="form-group">
        <label for="SiteID" class="custom-select150">Site</label>
        <div class="col-md-10">
            @Html.DropDownList("SiteID", Nothing, htmlAttributes:=New With {.class = "custom-select150"}) 
            @Html.ValidationMessageFor(Function(model) model.SiteID, "", New With {.class = "text-danger"})
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(Function(model) model.WarehouseID, htmlAttributes:=New With {.class = "control-label col-md-2"})
        <div class="col-md-10">
            @Html.EditorFor(Function(model) model.WarehouseID, New With {.htmlAttributes = New With {.class = "form-control"}})
            @Html.ValidationMessageFor(Function(model) model.WarehouseID, "", New With {.class = "text-danger"})
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(Function(model) model.WarehouseName, htmlAttributes:=New With {.class = "control-label col-md-2"})
        <div class="col-md-10">
            @Html.EditorFor(Function(model) model.WarehouseName, New With {.htmlAttributes = New With {.class = "form-control"}})
            @Html.ValidationMessageFor(Function(model) model.WarehouseName, "", New With {.class = "text-danger"})
        </div>
    </div>

    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" value="Create" class="btn btn-default" />
        </div>
    </div>
</div>
End Using

Now the problem is that when I add a new warehouse, Site_Id is NULL in database (I have filled the first one manually via SSMS):

enter image description here

This causes empty site name/ID when I load warehouses from database. How can I fix this?

>Solution :

Entity Framework only binds database TABLE and VIEW columns to properties, not fields (EF Core does support updating fields directly, but I assume you’re using EF, not EF Core, because to my knowledge EF Core does not support VB.NET).

Change your class’ member from this….

    <Display(Name:="Site"), Required>
    Public SiteID As Integer

…to this:

    <Display(Name:="Site"), Required>
    Public Property SiteId As Integer

…or this (if you’re a verbose-code masochist…):

    Private mSiteId As Integer

    ' [etc]

    <Display(Name:="Site"), Required>
    Public Property SiteId As Integer
        Get
            Return mSiteId
        End Get
        Set(value As Integer)
            mSiteId = value
        End Set
    End Property

Other points:

  • You should not be using EF entity classes as DTOs or ViewModels for form-bindings – it introduces security vulnerabilities ("overposting") as well as makes software harder to maintain because you can’t version DTOs and forms separately from entity classes.
  • Validation is not verification. You should not be accessing your database directly from within your IValidatableObject.Validate method: that’s the responsibility of your Controller Action or some other part of your application’s ASP.NET requesty-processing pipeline – the Validate method is intended to be synchronous and fast which implies you should not be doing any IO in there.
    • Validation is the process of ensuring submitted data conforms with some invariant constraints on that data (e.g. phone-number formatting, non-empty names, etc).
    • Verification involves doing actual leg-work to actually verify that submitted data is correct, instead of merely valid.
      • "Correct" data is a subset of the set of valid data.
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading