在 MVC 中填充下拉列表

本文关键字:下拉列表 填充 MVC | 更新日期: 2023-09-27 18:30:51

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

<Brands>
  <Brand>
  <BrandId>1</BrandId>
  <BrandNo>20</BrandNo>
  <BrandName>ABC</Domain>
  </Brand>
  <Brand>
  <BrandId>2</BrandId>
  <BrandNo>30</BrandNo>
  <BrandName>XYZ</Domain>
  </Brand>
<Brands>

在我的一个用户控件中,我想用品牌名称值填充下拉列表。我已经有一个包含一堆属性的视图模型。如何使用此 XML 中的值填充下拉列表?

PS:我是MVC的新手,仍在学习视图模型等的基础知识。

在 MVC 中填充下拉列表

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

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

然后是控制器操作:

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

最后是视图部分:

@model BrandsViewModel
@using (Html.BeginForm())
{
    @Html.DropDownListFor(x => x.Brand, Model.Brands)      
    <button type="submit">OK</button>
}

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

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

<Brands>
  <Brand>
    <BrandId>1</BrandId>
    <BrandNo>20</BrandNo>
    <BrandName>ABC</BrandName>
  </Brand>
  <Brand>
    <BrandId>2</BrandId>
    <BrandNo>30</BrandNo>
    <BrandName>XYZ</BrandName>
  </Brand>
</Brands>

现在您已经有了有效的 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
    };

现在让我们让 2 个一起工作:

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 分析并配置依赖项注入框架以将其传递给控制器。

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

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。这是一个 lightwight 框架,它允许您以流畅的方式定义不同类之间的映射,然后简单地传递给源类型的实例,它会吐出目标类型的实例:

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<Brand>, IEnumerable<SelectListItem>>(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框架。选一个就行了。这并不重要。试试Ninject.MVC3 NuGet。它有点酷且易于设置。或者,如果您不想使用第三方 DI 框架,只需编写自定义依赖项解析程序即可。

要解析 XML,您可以使用以下快速而脏的示例:

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

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