在我的MVC应用程序中,我有一个服务调用(http://dev-service.test.com/api/brands?active=true),它返回以下XML
<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的新手,还在学习viewmodels等基础知识。答案 0 :(得分:10)
你的问题确实有2个部分。 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框架的gazzilion。选择一个。这并不重要。试试Ninject.MVC3 NuGet。它有点酷,易于设置。或者,如果您不想使用第三方DI框架,只需编写自定义dependency resolver。
答案 1 :(得分:0)
要解析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的强类型类,并为其提供与您正在解析的内容匹配的属性,并解析节点。