填充MVC中的下拉列表

在我的MVC应用程序中,我有一个服务调用(http://dev-service.test.com/api/brands?active=true),它返回以下XML

  1 20 ABC   2 30 XYZ   

在我的一个用户控件中,我想使用BrandName值填充下拉列表。 我已经有一个包含一堆属性的ViewModel。 如何使用此XML中的值填充下拉列表?

PS:我是MVC的新手,还在学习viewmodels等基础知识。

你的问题中确实有2个部分。 XML解析部分(严格与ASP.NET MVC无关)和ASP.NET MVC部分。 由于你的问题用asp.net-mvc标记,让我们首先回答这一部分。 所以你提到了一个视图模型。 像这样的东西:

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

然后控制器动作:

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

最后是视图部分:

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

好的,这是ASP.NET MVC部分在你的问题中结束的地方。 现在是XML解析部分。 有几种方法可以在C#中解析XML。 例如,您可以使用XDocument类。

当然,在能够解析XML之前,您需要拥有XML。 您在问题中显示的不是XML。 这是一个字符串。 您需要先修复它并拥有有效的XML。 像这样:

   1 20 ABC   2 30 XYZ   

现在您已经拥有了有效的XML,让我们继续使用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 }; 

现在让我们共同努力:

 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); } 

在这里我们可以看到,我们已经将控制器动作逻辑与XML解析逻辑强烈耦合,这很糟糕。 您可以引入一个抽象(接口),它将被注入控制器的构造函数,然后由操作使用。 然后,您可以提供此抽象的特定实现,该实现将执行实际的XML解析并配置您的dependency injection框架以将其传递给控制器​​。

所以,让我们这样做。 让我们定义一个代表我们品牌的领域模型:

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

凉。 现在我们想对这些品牌做些什么? 检索它们的列表。 让我们来定义我们的合同:

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

好的,我们已经指定了我们的品牌需要哪些操作。 现在我们可以让控制器看起来像这样:

 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); } } 

此控制器操作仍有改进的余地。 请注意,我们正在查询存储库并获取域模型列表( Brand )并将此域模型转换为视图模型。 这很麻烦,污染了我们的控制器逻辑。 最好将此映射外部化为单独的层。 就个人而言,我使用AutoMapper 。 它是一个轻量级框架,允许您以流畅的方式定义不同类之间的映射,然后简单地传递源类型的实例,它将吐出目标类型的实例:

 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); } } 

所以我们在这里取得进展。 现在我们可以实现合同:

 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(); } } 

这个难题的最后一步是配置一些DI框架,以便将合同的正确实现注入控制器。 有一个用于.NET的DI框架的gazzilion。 选择一个。 这并不重要。 试试Ninject.MVC3 NuGet。 它有点酷,易于设置。 或者,如果您不想使用第三方DI框架,只需编写自定义依赖项解析程序 。

要解析Xml,您可以使用这个快速而肮脏的示例:

  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(); 

如果是我,我会创建一个名为Brand的强类型类,并为其提供与您正在解析的内容匹配的属性,并解析节点。