Rellenar la lista desplegable en MVC

En mi aplicación MVC, tengo una llamada de servicio (http://dev-service.test.com/api/brands?active=true) que devuelve el siguiente XML

  1 20 ABC   2 30 XYZ   

En uno de mis controles de usuario, me gustaría rellenar una lista desplegable con los valores de BrandName. Ya tengo un ViewModel que contiene un montón de propiedades. ¿Cómo puedo rellenar el desplegable con los valores de este XML?

PD: soy nuevo en MVC y todavía estoy aprendiendo los conceptos básicos de los modelos de vista, etc.

Realmente hay 2 partes en tu pregunta. La parte de análisis XML (que no tiene nada que ver con ASP.NET MVC) y la parte ASP.NET MVC. Ya que su pregunta está etiquetada con asp.net-mvc , primero respondamos esta parte. Así que mencionas un modelo de vista. Algo como esto:

 public class BrandsViewModel { public string Brand { get; set; } public IEnumerable Brands { get; set; } } 

entonces una acción del controlador:

 public ActionResult Index() { BrandsViewModel model = ... return View(model); } 

y finalmente la parte vista:

 @model BrandsViewModel @using (Html.BeginForm()) { @Html.DropDownListFor(x => x.Brand, Model.Brands)  } 

Muy bien, aquí es donde la parte ASP.NET MVC termina en tu pregunta. Ahora viene la parte de análisis XML. Hay varias formas de analizar XML en C #. Por ejemplo, podrías usar la clase XDocument .

Por supuesto, antes de poder analizar XML necesitas tener XML. Lo que has mostrado en tu pregunta no es XML. Es una cuerda. Primero debes arreglarlo y tener un XML válido. Me gusta esto:

   1 20 ABC   2 30 XYZ   

Ahora que tiene un XML válido, avancemos y usemos un analizador XML.

 var brands = from brand in XDocument.Load("brands.xml").Descendants("Brand") select new SelectListItem { Value = brand.Element("BrandId").Value, Text = brand.Element("BrandName").Value }; 

y ahora vamos a hacer que los 2 trabajen juntos:

 public ActionResult Index() { var brandsFile = Server.MapPath("~/app_data/brands.xml"); var brands = from brand in XDocument.Load(brandsFile).Descendants("Brand") select new SelectListItem { Value = brand.Element("BrandId").Value, Text = brand.Element("BrandName").Value }; var model = new BrandsViewModel { Brands = brands }; return View(model); } 

Aquí es donde podemos ver que hemos acoplado fuertemente la lógica de acción del controlador con la lógica de análisis XML, lo cual es malo. Podría introducir una abstracción (interfaz) que se inyectará en el constructor del controlador y luego la acción lo usará. Luego, podría proporcionar una implementación específica de esta abstracción que realizará el análisis XML real y configurará el marco de dependency injections para pasarlo al controlador.

Así que vamos a hacer eso. Definamos un modelo de dominio que representará nuestras marcas:

 public class Brand { public string Id { get; set; } public string Name { get; set; } } 

Guay. Ahora, ¿qué queremos hacer con esas marcas? Recupera una lista de ellos. Vamos a definir nuestro contrato:

 public interface IBrandsRepository { Brand[] Get(); } 

OK, hemos especificado que operaciones necesitamos con nuestras marcas. Ahora podríamos hacer que nuestro controlador se vea así:

 public class BrandsController: Controller { private readonly IBrandsRepository _repository; public BrandsController(IBrandsRepository repository) { _repository = repository; } public ActionResult Index() { var brands = _repository.Get().Select(b => new SelectListItem { Value = b.Id, Text = b.Name }); var model = new BrandsViewModel { Brands = brands }; return View(model); } } 

Todavía hay espacio para mejorar en esta acción del controlador. Observe que estamos consultando el repository y obteniendo una lista de modelos de dominio ( Brand ) y convirtiendo este modelo de dominio en un modelo de vista. Esto es engorroso y contamina nuestra lógica de control. Sería mejor externalizar este mapeo en una capa separada. Personalmente uso AutoMapper para eso. Es un marco de Lightwight que le permite definir las asignaciones entre diferentes clases de una manera fluida y luego simplemente pasar las instancias del tipo de origen y le escupirá las instancias del tipo de destino:

 public class BrandsController: Controller { private readonly IBrandsRepository _repository; public BrandsController(IBrandsRepository repository) { _repository = repository; } public ActionResult Index() { var brands = _repository.Get(); var model = new BrandsViewModel { Brands = Mapper.Map, IEnumerable>(brands) }; return View(model); } } 

Así que estamos haciendo progresos aquí. Ahora podríamos tener una implementación de nuestro contrato:

 public class BrandsRepositoryXml: IBrandsRepository { private readonly string _brandsFile; public BrandsRepositoryXml(string brandsFile) { _brandsFile = brandsFile; } public Brand[] Get() { return (from brand in XDocument.Load(_brandsFile).Descendants("Brand") select new Brand { Id = brand.Element("BrandId").Value, Name = brand.Element("BrandName").Value }) .ToArray(); } } 

Y el último paso del rompecabezas es configurar un marco DI para inyectar la implementación adecuada de nuestro contrato en el controlador. Hay como un brillo de los marcos DI para .NET. Sólo tienes que elegir uno. Realmente no importa Prueba el Ninject.MVC3 NuGet. Es un poco genial y fácil de configurar. O si no desea utilizar marcos de DI de terceros, simplemente escriba un resolvedor de dependencias personalizado.

Para analizar el Xml puede usar este ejemplo rápido y sucio:

  string xml = @"   1 20 ABC   2 30 XYZ   "; System.Xml.XmlDocument doc = new System.Xml.XmlDocument(); doc.LoadXml(xml); var brands = ( from node in doc.SelectNodes("//Brands/Brand").Cast() select new { BrandId = node.SelectSingleNode("BrandId").InnerText, BrandNo = node.SelectSingleNode("BrandNo").InnerText, BrandName = node.SelectSingleNode("BrandName").InnerText }).ToList(); 

Si fuera yo, crearía una clase fuertemente tipada llamada Marca y le daría propiedades que coincidan con lo que está analizando y que analicen los nodos.