L'uso del ponteggio standard visualizza solo gli indici nel menu a discesa delle relative tabelle. Come visualizzare più campi in modo che l'elenco a discesa sia significativo?
Ho tentato di utilizzare una soluzione delineata da Shyju nella sezione "Ottenere i dati dalla tabella del database utilizzando l'entità framework". ( Selezionare Tag Helper in ASP.NET Core MVC ).
Ho 2 lezioni nel modello (Libri con autore correlato):
namespace SimpleAppwithTwoTables.Data
{
public class Book
{
public int BookID { get; set; }
[StringLength(255)]
[Display(Name ="Book Title")]
public string Title { get; set; }
public int AuthorID { get; set; }
public Author Author { get; set; }
}
}
e
namespace SimpleAppwithTwoTables.Data
{
public class Author
{
public int AuthorID { get; set; }
[Display(Name = "First Name")]
[StringLength(50)]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
[StringLength(50)]
public string LastName { get; set; }
public ICollection<Book> Books { get; set; } = new List<Book>();
}
}
BooksController ha un metodo Create () simile a quello descritto nel post di Shyju.
public IActionResult Create()
Author vm = new Author();
vm.Books = _context.Book.Select(a => new SelectListItem()
{ Value = a.AuthorID.ToString(), Text = a.Author.LastName }).ToList();
return View(vm);
}
Questa linea nel controller
vm.Books = _context.Book.Select(a => new SelectListItem()
{ Value = a.AuthorID.ToString(), Text = a.Author.LastName }).ToList();
Genera questo errore:
"Impossibile convertire in modo implicito il tipo 'System.Collections.Generic.List <Microsoft.AspNetCore.Mvc.Rendering.SelectListItem>' in 'System.Collections.Generic.ICollection <SimpleAppwithTwoTables.Data.Book>'. Esiste una conversione esplicita (sei tu manca un cast?).
Nota che sono nuovo in C # e capisco che si tratta di un problema di conversione del tipo e che la riga con l'errore prevede che il modello Autore contenga un ICollection <>.
Sto cercando il codice necessario nella riga vm.books che non esegue una conversione di tipo e funziona con ICollection.
Stai cercando di SelectListItem
un elenco di SelectListItem
in una proprietà definita come un elenco di Book
. SelectListItem
ovviamente non è la stessa cosa di Book
, quindi fallisce.
La soluzione semplice è che hai bisogno di una proprietà sul tuo modello di visualizzazione specifica per contenere le opzioni dell'elenco selezionato:
public IEnumerable<SelectListItem> AuthorOptions { get; set; }
Avrai anche bisogno di qualcosa a cui associare gli elementi selezionati, che saranno tipi primitivi, non istanze reali Author
:
public List<int> SelectedAuthorIds { get; set; }
A tuo avviso, quindi:
<select asp-for="SelectedAuthorIds" asp-items="AuthorOptions">
Per posta, dovrai quindi utilizzare quella raccolta di ID per interrogare gli oggetti dell'autore effettivo, se devi associarli a un libro o qualcos'altro:
var selectAuthors = await _context.Authors.Where(x => vm.SelectedAuthorIds.Contains(x.Id)).ToListAsync();
Ho seguito il consiglio che Chris Pratt ha fornito sopra. Ci è voluto un po 'di tempo per capire ma quasi lavorare. Vedi i dettagli di seguito.
Vedi il modello per libro e autore sopra.
Creato un nuovo ViewModel:
namespace SimpleDropDownList.Models
{
[NotMapped]
public class AuthorViewModel
{
//Property to hold the list of authors in the GET
public IEnumerable<SelectListItem> AuthorOptions { get; set; }
//Property to bind the selected author used in the POST
public List<int> SelectedAuthorIds { get; set; }
}
}
Modificato il metodo Create () su BooksController in:
public IActionResult Create()
{
//ViewData["AuthorID"] = new SelectList(_context.Set<Author>(), "AuthorID", "AuthorID");
AuthorViewModel vm = new AuthorViewModel();
vm.AuthorOptions = _context.Book.Select(x => new SelectListItem()
{ Value = x.AuthorID.ToString(), Text = x.Author.LastName }).ToList();
return View(vm);
}
Cambia la vista Crea in:
@model SimpleDropDownList.Models.Book
@{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Book</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="Title" class="control-label"></label>
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="AuthorID" class="control-label"></label>
@*<select asp-for="AuthorID" class ="form-control" asp-items="ViewBag.AuthorID"></select>*@
<select asp-for="@Model.AuthorViewModel.SelectedAuthorIds" asp-items="@Model.AuthorViewModel.AuthorOptions"></select>
</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");}
}
Model: AuthorViewModel
Quando eseguo questo e faccio clic sul pulsante Crea, viene generato il seguente errore:
Si è verificata un'eccezione non gestita durante l'elaborazione della richiesta. InvalidOperationException: l'elemento del modello passato in ViewDataDictionary è di tipo "SimpleDropDownList.Models.AuthorViewModel", ma questa istanza ViewDataDictionary richiede un elemento del modello di tipo "SimpleDropDownList.Models.Book".
Sto contrassegnando questa domanda come completata e ho creato una nuova domanda per il problema descritto sopra. Vedi qui: Errore all'apertura della pagina Rasoio e un relativo elenco a discesa