-1

I am new to .Net Core (and Blazor) and I just configured a project that uses the latest version of .net core (3.1.xxx) following a tutorial and checking Microsoft Docs.

I have a couple of entities in my project and I'm able to save the entities composed with simple attributes (primitive types) using the API. But I am facing a problem when I try the same with an Entity that has other entities as attributes (so nested objects).

And the error I get is not helping at all, it's complaining about a conversion from Char to String as you can see on the stack trace below:

fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1] An unhandled exception has occurred while executing the request. System.InvalidCastException: Unable to cast object of type 'System.Char' to type 'System.String'. at System.ComponentModel.DataAnnotations.StringLengthAttribute.IsValid(Object value) at System.ComponentModel.DataAnnotations.ValidationAttribute.IsValid(Object value, ValidationContext validationContext) at System.ComponentModel.DataAnnotations.ValidationAttribute.GetValidationResult(Object value, ValidationContext validationContext) at Microsoft.AspNetCore.Mvc.DataAnnotations.DataAnnotationsModelValidator.Validate(ModelValidationContext validationContext) at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.ValidateNode() at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.VisitSimpleType() at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.Visit(ModelMetadata metadata, String key, Object model) at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.VisitChildren(IValidationStrategy strategy) at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.VisitComplexType(IValidationStrategy defaultStrategy) at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.Visit(ModelMetadata metadata, String key, Object model) at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.VisitChildren(IValidationStrategy strategy) at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.VisitComplexType(IValidationStrategy defaultStrategy) at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.Visit(ModelMetadata metadata, String key, Object model)

This only happens with nested entities. Is this the case where I should write a custom model binding class?

My Code is this:

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

namespace src.Data.Entities
{
    [Table("Screening")]
    public class Screening
    {
        public Screening()
        {
            this.Beneficiary = new Beneficiary();
        }
        public int ScreeningId { get; set; }
        public DateTime ScreeningDate { get; set; } = DateTime.Now;
        public virtual Beneficiary Beneficiary { get; set; }
        public int BeneficiaryId { get; set; }
        public virtual SimpleEntity ActionTaken { get; set; }
        public int ActionTakenId { get; set; }

        public virtual SimpleEntity ReasonForVisit { get; set; }
        public int ReasonForVisitId { get; set; }

    }
}

Controller class

using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using src.Data;
using src.Data.Entities;
using src.Data.DTO;
using src.Helpers;
using System.Linq;

namespace src.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class ScreeningController : ControllerBase
    {
        private readonly ApplicationDbContext context;

        public ScreeningController(ApplicationDbContext context)
        {
            this.context = context;
        }

        [HttpGet]
        public async Task<ActionResult<List<Screening>>> Get([FromQuery] PaginationDTO pagination)
        {
            var queryable = context.Screenings.AsQueryable().OrderBy(x => x.ScreeningDate);
            await HttpContext.InsertPaginationParameterInResponse(queryable, pagination.RecordsPerPage);
            return await queryable.Paginate(pagination).ToListAsync();
        }

        [HttpGet("{id}", Name = "GetScreening")]
        public async Task<ActionResult<Screening>> Get(int id)
        {
            return await context.Screenings.FirstOrDefaultAsync(x => x.ScreeningId == id);
        }

        [HttpPost]
        public async Task<ActionResult> Post(Screening screening)
        {
            context.Add(screening);
            await context.SaveChangesAsync();
            return new CreatedAtRouteResult("GetScreening", new { id = screening.ScreeningId }, screening);
        }

        [HttpPut]
        public async Task<ActionResult> Put(Screening screening)
        {
            context.Entry(screening).State = EntityState.Modified;
            await context.SaveChangesAsync();
            return NoContent();
        }

        [HttpDelete("{id}")]
        public async Task<ActionResult> Delete(int id)
        {
            var screening = new Screening { ScreeningId = id };
            context.Remove(screening);
            await context.SaveChangesAsync();
            return NoContent();
        }
    }
}

Views

Screening Form Component


    @inject HttpClient http

    <RadzenCard>
        <EditForm Model="@Screening" OnValidSubmit="@OnValidSubmit">
            <DataAnnotationsValidator/>
            <div class="from-field">
                <label>Screening date:</label>
                <div>
                    <RadzenDatePicker @bind-Value="Screening.ScreeningDate"/>
                </div>
            </div>
            <div class="from-field">
                <label>Client name:</label>
                <div>
                    <RadzenTextBox Style="margin-bottom: 20px" @bind-Value="@Screening.Beneficiary.FirstName"/>
                </div>
            </div>
            <div class="from-field">
                <label>Client surname:</label>
                <div>
                    <RadzenTextBox Style="margin-bottom: 20px" @bind-Value="@Screening.Beneficiary.Surname"/>
                </div>
            </div>
            <div class="from-field">
                <label>Reason for visit:</label>
                <div>
                    <RadzenDropDown 
                        AllowClear="true" 
                        TValue="int" 
                        FilterCaseSensitivity="FilterCaseSensitivity.CaseInsensitive" 
                        AllowFiltering="true" 
                        Data="@ReasonForVistList" 
                        TextProperty="Description" 
                        ValueProperty="SimpleEntityID" 
                        Style="margin-bottom: 20px"  />
                </div>
            </div>
            <div class="from-field">
                <label>Action taken:</label>
                <div>
                    <RadzenDropDown 
                        AllowClear="true" 
                        TValue="int" 
                        FilterCaseSensitivity="FilterCaseSensitivity.CaseInsensitive" 
                        AllowFiltering="true" 
                        Data="@ActionTakenList" 
                        TextProperty="Description" 
                        ValueProperty="SimpleEntityID" 
                        Style="margin-bottom: 20px"  />
                </div>
            </div>
            <hr/>
            <button type="submit" class="btn btn-success">
                Create
            </button>
        </EditForm>
    </RadzenCard>

    @code {
        [Parameter] public  Screening Screening{get;set;}
        [Parameter] public  string ButtonText{get;set;} = "Save";
        [Parameter] public  EventCallback OnValidSubmit{get;set;}
        IEnumerable<SimpleEntity> ReasonForVistList;
        IEnumerable<SimpleEntity> ActionTakenList;

        protected override async Task OnInitializedAsync() 
        {
            var reasonForVisitType = "reason-for-visit";
            var actionTaken = "screening-action-taken";
            ReasonForVistList =  await http.GetJsonAsync<SimpleEntity[]>($"api/simpleentity/type/?type={reasonForVisitType}");
            ActionTakenList = await http.GetJsonAsync<SimpleEntity[]>($"api/simpleentity/type/?type={actionTaken}");
        }
    }


Create View


    @page "/screening/create"
    @inject HttpClient http
    @inject NavigationManager uriHelper

    <h3>New Screening Process</h3>

    <ScreeningForm ButtonText="Create" Screening="@screening" OnValidSubmit="@Save"/>

    @code{
        Screening screening = new Screening();

        async Task Save()
        {
            await http.PostJsonAsync("api/screening", screening);
            uriHelper.NavigateTo("screening");
        }
    }

Eudson
  • 1
  • 2
  • 1
    It's not clear from the code you've provided, but somewhere a `char` is being cast to a `string`. Most likely you've used single quotes somewhere where you should have used double quotes instead. In general, look for places in your code where you're either explicitly dealing with a `char` type or you've used single quotes for a "string". – Chris Pratt Dec 18 '19 at 14:39
  • @ChrisPratt thanks for your prompt comment. But I'm not dealing with `char` anywhere in my app, that's why i was so surprised with the error. I've added the view code let me know if this helps understanding the problem – Eudson Dec 18 '19 at 15:48
  • 1
    Your exception says, you are trying assign string to char. So, I would check your input data to database for the column which is expecting CHAR – sam Dec 18 '19 at 16:33
  • Thanks guys for helping me. Unfortunately I can post an answer due to my reputation but I solved my problem. Not sure if i can share here on the comments – Eudson Dec 18 '19 at 17:11

1 Answers1

0

Please check the data that is being posted from your form to action and hence to database.

fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1] An unhandled exception has occurred while executing the request. System.InvalidCastException: Unable to cast object of type 'System.Char' to type 'System.String'. at System.ComponentModel.DataAnnotations.StringLengthAttribute.IsValid(Object value) at

sam
  • 1,937
  • 1
  • 8
  • 14