1

Buen día.

Estoy creando una PartialView para poder crear un control estándar de búsqueda que se pueda utilizar en varias partes de un sitio WEB.

Creé la PartialView en la carpeta Shared y, siguiendo las convenciones de nombre de las PartialView, la llame _Search.cshtml.

PartialView en el proyecto

Ésta PartialView la diseñe siguiendo el patrón MVC (Con la pequeña variante de incluir un ViewModel ya que vengo de desarrollar en Xamarin y pues me parece más cómodo tener separado el Model (Con las propiedades del View) y la ViewModel (Con los métodos y operaciones).

Model y ViewModel

También tengo un Controller ubicado en la carpeta de Controllers

Controladores

La PartialView _Search.cshtml tiene el siguiente código.

@model Equipos.ViewModel.Controles.SearchViewModel
@{
    ViewData["Title"] = "Search";
}

<script>
    function FnPerformSearch(pStrSearchType) {

        var StrSearchTerm = document.getElementById("TxtSearchTerm").value;
        var StrUrl = "@Url.Action("Search", "Search")";

        var oData = {
            pStrSearchTerm: StrSearchTerm,
            pStrSearchType: pStrSearchType
        };

        $.ajax({
            data: oData,
            url: StrUrl,
            type: 'POST',
            dataType: 'json',
            contentType: 'application/json; charset=utf-8',
            success: function (pStrData) {
                var oData = JSON.parse(pStrData);
                var TblSearchResults = $('#TblSearchResults');
                TblSearchResults.empty();
                oData.forEach(function (item) {
                    var oRow = TblSearchResults.insertRow();

                    var oCell = oRow.insertCell();
                    oCell.innerHTML = oData["Value"];

                    var oCell = oRow.insertCell();
                    oCell.innerHTML = oData["Code"];

                    var oCell = oRow.insertCell();
                    oCell.innerHTML = oData["Text"];
                });
            },
            error: function (xhr, status, error) {
                alert("Request: " + xhr.responseText + "\n" + "Status: " + status + "\n" + "Error: " + error);
            }
        }).fail(function (JqXHR, StrStatus, StrError) {
            alert("Request: " + JqXHR.responseText + "\n" + "Status: " + StrStatus + "\n" + "Error: " + StrError);
        });

    }
</script>

<div id="Search" class="modal fade" tabindex="0">
    <div class="modal-dialog modal-dialog-centered">
        <div class="modal-content border border-secondary">
            <div class="modal-header bg-secondary bg-gradient bg-opacity-50">
                <h5 class="modal-title" id="tituloModal">Buscar</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <div class="container">
                <div class="row mb-2">
                    <div class="col-12 mt-2">
                        <div class="d-flex">
                            <input type="text" id="TxtSearchTerm" name="TxtSearchTerm" placeholder="Escriba para realizar la búsqueda" style="width: 100%" />
                            <button type="button" class="btn btn-secondary ms-1" onclick="FnPerformSearch('Empleados')">Buscar</button>
                        </div>
                    </div>
                </div>
                <div class="row">
                    <div class="col-sm">
                        <table id="TblSearchResults" class="table table-hover">
                            <thead>
                                <tr>
                                    <td colspan="3">
                                        <h6 class="modal-header bg-gray">Elementos Encontrados</h6>
                                    </td>
                                </tr>
                            </thead>
                            <tbody>
                                <tr>
                                    <th scope="col" width="20%">
                                        Value
                                    </th>
                                    <th scope="col" width="20%">
                                        Code
                                    </th>
                                    <th scope="col" width="80%">
                                        Text
                                    </th>
                                </tr>
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>
        </Div>
    </div>
</div>

La ViewModel SearchViewModel.cs tiene el siguiente código

using Equipos.AccessLayer;
using Equipos.Models.Controles;
using Microsoft.Data.SqlClient;
using System.Data;

namespace Equipos.ViewModel.Controles {

    public class SearchViewModel {

        private readonly SqlConnection LcSqlCn;

        public SearchModel LcModel { get; set; }

        public SearchViewModel() {
            LcSqlCn = DatabaseConection.Conexion();
            LcModel = new SearchModel();
        }

        public async Task<IEnumerable<SearchedListItem>> FnSearchAsync(SearchType pType, string pStrSearchTerm) {

            //Implementar aquí la búsqueda sobre la Base de Datos en función del pType
            return pType switch {
                SearchType.Empleado => await FnSearchEmpleadoAsync(pStrSearchTerm),
                _ => throw new Exception($"El Tipo: {pType} no es válido."),
            };

        }

        private async Task<IEnumerable<SearchedListItem>> FnSearchEmpleadoAsync(string pStrSearchTerm) {

            try {
                SqlCommand oCmd = new("SpEquiposEmpleadosBuscar", LcSqlCn) {
                    CommandType = CommandType.StoredProcedure
                };

                oCmd.Parameters.AddWithValue("@SearchTerm", pStrSearchTerm);

                await LcSqlCn.OpenAsync();

                DataTable oDt = new();
                SqlDataAdapter oAdapter = new(oCmd);
                oAdapter.Fill(oDt);

                return new List<SearchedListItem>(
                    from oDr in oDt.AsEnumerable()
                    select new SearchedListItem() {
                        Value = oDr["IdEmpleado"].ToString(),
                        Text = $"{oDr["Nombres"]} {oDr["ApPaterno"]} {oDr["ApMaterno"]}",
                        Code = oDr["IdEmpleado"].ToString()
                    }
                );
            }
            finally {
                if (LcSqlCn != null && LcSqlCn.State == ConnectionState.Open) {
                    await LcSqlCn.CloseAsync();
                }
            }

        }

    }

}

La Model SearchModel.cs tiene el siguiente código.

namespace Equipos.Models.Controles {

    public enum SearchType {
        Empleado, Equipo
    }

    public class SearchModel {

        /// <summary>
        /// Término de la búsqueda a realizar
        /// </summary>
        public string? SearchTerm {
            get => LcSearchTerm;
            set => LcSearchTerm = value;
        }
        private string? LcSearchTerm;

        /// <summary>
        /// Tipo de búsqueda a realizar
        /// </summary>
        public SearchType SearchType {
            get => LcSearchType;
            set => LcSearchType = value;
        }
        private SearchType LcSearchType;

        /// <summary>
        /// Lista de Items localizados
        /// </summary>
        public IEnumerable<SearchedListItem>? Results {
            get => LcResults;
            set => LcResults = value;
        }
        private IEnumerable<SearchedListItem>? LcResults;

    }

    public class SearchedListItem {

        /// <summary>
        /// Valor del Item localizado (Generalmente el ID)
        /// </summary>
        public string? Value {
            get => LcStrValue;
            set => LcStrValue = value;
        }
        private string? LcStrValue;

        /// <summary>
        /// Código del Item localizado
        /// </summary>
        public string? Code {
            get => LcStrCode;
            set => LcStrCode = value;
        }
        private string? LcStrCode;

        /// <summary>
        /// Texto del Item localizado
        /// </summary>
        public string? Text {
            get => LcStrText;
            set => LcStrText = value;
        }
        private string? LcStrText;

    }

}

y finalmente, el Controller SearchController.cs tiene el siguiente código.

using Equipos.Models.Controles;
using Equipos.ViewModel.Controles;
using Microsoft.AspNetCore.Mvc;

namespace Equipos.Controllers {

    public class SearchController : Controller {

        private readonly ILogger<SearchController> _logger;

        public SearchController(ILogger<SearchController> pLogger) => _logger = pLogger;

        [HttpPost(Name = "Search")]
        public async Task<JsonResult> FnSearchAsync(string pStrSearchTerm, string pStrSearchType) {

            SearchViewModel oSearch = new ();
            pStrSearchType = pStrSearchType.ToUpper().Replace(" ", "");
            SearchType oSearchType = pStrSearchType == "EMPLEADOS" ? SearchType.Empleado : SearchType.Equipo;

            return Json(await oSearch.FnSearchAsync(oSearchType, pStrSearchTerm));
            
        }

    }

}

PROBLEMA

Por alguna razón, al presionar el botón que debería realizar la búsqueda en el control, el JavaScript se ejecuta pero, al llegar al AJAX, no pasa nada. Indagando en el depurador del navegador, veo que aparece un error que dice que el recurso no se ha encontrado.

Error en el Navegador, 404: Resource not found

Sinceramente no sé que ocurre, el resto de los controladores en el sitio funcionan sin problema, y ya tengo un rato que no encuentro la solución.

¿Me podrían tirar un cable?

Muchísimas gracias!

EDICIÓN 1 Pantalla de RED solicitada por YUSEFF

Imagen de RED solicitada por Yussef

EDICIÓN 2 Prueba de Script solicitada por YUSSEF

Prueba de Script solicitada por YUSSEF

EDICIÓN 3 Salida de Carga útil solicitada por YUSEFF

Salida de Carga útil solicitada por YUSEFF

EDICIÓN 4 Agregar la etiqueta Route("[Controller]") al SearchController.cs

Agregar la etiqueta Route("[Controller]") al SearchController.cs

EDICIÓN 5 Evidencia de agregado de etiqueta Route a la clase SearchController.cs

Evidencia de agregado de etiqueta Route a la clase SearchController.cs

1 respuesta 1

1

El problema (tal como sugirió @Yussef) tenía que ver con la firma del método Search de mi controlador. La cuestión es que, al recibir 2 parámetros, al parecer debo declararlo así para que se pueda localizar la función Search en el controller. La magia la hizo ésta sentencia que vi por ahí en otra publicación.

Solución

1

¿No es la respuesta que buscas? Examina otras preguntas con la etiqueta o formula tu propia pregunta.