【问题标题】:Populate dropdown list in MVC在 MVC 中填充下拉列表
【发布时间】:2012-04-11 19:24:53
【问题描述】:

在我的 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>

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

P.S:我是 MVC 新手,还在学习视图模型等基础知识。

【问题讨论】:

    标签: c# jquery asp.net asp.net-mvc


    【解决方案1】:

    您的问题确实有两个部分。 XML 解析部分(与 ASP.NET MVC 完全无关)和 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
        };
    

    现在让我们让两者一起工作:

    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。它是一个轻量级的框架,允许您以流畅的方式定义不同类之间的映射,然后只需将源类型的实例传递给它,它就会向您吐出目标类型的实例:

    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 框架,只需编写自定义 dependency resolver

    【讨论】:

    • 我不明白“不是 XML,它是一个字符串”部分。也许我很密集,但是您的 XML 与 OP 的有何不同?
    • @CharlieKilian,仔细看。你看到这个&lt;BrandName&gt;XYZ&lt;/Domain&gt;了吗?您还看到 OP 的 XML 中的结束 &lt;/Brands&gt; 标记吗?我没有。
    • 啊。不,我没有,但我现在有。对你来说很好。我很抱歉。
    【解决方案2】:

    要解析 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 的强类型类,并为其提供与您正在解析的内容相匹配的属性,然后它们会解析节点。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-02
      相关资源
      最近更新 更多