简体   繁体   中英

How to Allow Null input for C# Razor page SelectList

Considering the following data model:

public partial class RootEntity
{
    [Key]
    public long Id { get; set; }

    [Required(AllowEmptyStrings = false)]
    public string Name { get; set; }
    public string ThingsAboutIt { get; set; }

    public long? RelatedEntity_Id { get; set; }
    [ForeignKey("RelatedEntity_Id")]
    public RelatedEntity RelatedEntity { get; set; }

}

public partial class RelatedEntity
{
    [Key]
    public long Id { get; set; }

    [Required(AllowEmptyStrings = false)]
    public string Name { get; set; }

    public ICollection<RootEntity> RootEntities { get; set; }
}

public partial class MyContext : IdentityDbContext
{
    public MyContext(DbContextOptions<MyContext> options) : base(options) { }

    public virtual DbSet<RootEntity> RootEntities { get; set; }
    public virtual DbSet<RelatedEntity> RelatedEntities { get; set; }
}

When using the EF (Core) scaffolded Insert page in a Razor page application, the default pattern used for selecting singl-y linked entity records is:

<div class="form-group">
    <label asp-for="RootEntity.RelatedEntity_Id" class="control-label"></label>
    <select asp-for="RootEntity.RelatedEntity_Id" class ="form-control" asp-items="ViewBag.RelatedEntity_Id"></select>
</div>

and the initialization of this looks something like this:

ViewData["RelatedEntity_Id"] = new SelectList(_context.RelatedEntities, "Id", "Name");

I can configure this a tiny bit, by changing that third parameter to something like "Name" - if such a property exists.

I need way more configurabililty - I need at least null values to be selected.

In an attempt to allow nulls, I use this pattern:

List<SelectListItem> selectList = _context.RelatedEntities
    .AsNoTracking()
    .Select(x => new SelectListItem()
    {
        Value = x.Id.ToString(),
        Text = x.Name
    })
    .ToList();

selectList.Insert(0, new SelectListItem()
{
    Value = null, // <=========== Here lies the problem...
    Text = "--- Select Related Entity ---"
});

ViewData["RelatedEntity_Id"] = selectList;

The rendered HTML looks something like this:

<select class="form-control" id="RootEntity_RelatedEntity_Id" name="RootEntity.RelatedEntity_Id">
    <option selected="selected">--- select Related Entity ---</option>
    <option value="3">FOO</option>
    <option value="2">MMMBAR</option>
</select>

The problem I'm having is that the value "--- Select Related Entity ---" becomes bound to that model property. This makes it fail validation. ( ModelState.IsValid == false )

I have tried changing the SelectListItem value to a blank string, "-1", but none of it fixes the model validation error.

I've also tried to build a custom binder for entity classes, but this binds the root model itself to it - which obviously does not pertain to this Select List scenario.

What is the simplest, most elegant way to resolve this?

I have tried changing the SelectListItem value to a blank string, "-1", but none of it fixes the model validation error.

It can pass the validation if you set it as an empty string.

selectList.Insert(0, new SelectListItem()
{
    Value = "",
    Text = "--- Select Related Entity ---"
});

You can see the rendered html code, the default option with a value attribute but with no value:

在此处输入图像描述

And when submit the form, RootEntity.RelatedEntity_Id will be null.

在此处输入图像描述

This is the result.

在此处输入图像描述

The best thing I could come up with is the following line of code:

ModelState.Remove("RootEntity.RelatedEntity_Id");

before checking ModelState.IsValid . This seems to work ok, and is reasonably elegant, I guess.

I am still open to suggestions, and criticisms if I am missing some major language feature, or better practice.

It sounds like Model Validation is behaving as it should. By adding the 'select' option like you did with a null value, model validation will trigger invalid. This is because your model value is not nullable. If you expect the value to be nullable, you should probably adjust your model to accept nulls. Although it does appear to be a table key, so it's not clear why you want this behavior.

public long? Id { get; set; }

Better yet, add this to a ViewModel, along with your selectlist model so that you have a strongly typed list as well.

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