我使用的是Entity Framework 6,MVC 5和最新的ASP.NET Razor。
我有以下代码:
[ForeignKey("Country")]
[Display(Name = "CountryId", ResourceType = typeof(Internationalization.Resources.TenantFinanceConfiguration))]
public int CountryId { get; set; }
[Required(ErrorMessageResourceName = "Country_ValidationError", ErrorMessageResourceType = typeof(Internationalization.Resources.TenantFinanceConfiguration))]
[Display(Name = "Country", ResourceType = typeof(Internationalization.Resources.TenantFinanceConfiguration))]
public virtual Country Country { get; set; }
[ForeignKey("Currency")]
[Display(Name = "CurrencyId", ResourceType = typeof(Internationalization.Resources.TenantFinanceConfiguration))]
public int CurrencyId { get; set; }
[Required(ErrorMessageResourceName = "Currency_ValidationError", ErrorMessageResourceType = typeof(Internationalization.Resources.TenantFinanceConfiguration))]
[Display(Name = "Currency", ResourceType = typeof(Internationalization.Resources.TenantFinanceConfiguration))]
public virtual Currency Currency { get; set; }
无论我在哪里放置外键,都会正确创建表格;但是当我创建视图时:
<div class="form-group">
@Html.LabelFor(model => model.CountryId, "CountryId", htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownList("CountryId", null, htmlAttributes: new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.CountryId, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.CurrencyId, "CurrencyId", htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownList("CurrencyId", null, htmlAttributes: new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.CurrencyId, "", new { @class = "text-danger" })
</div>
</div>
我收到错误:
具有键&#39; CountryId&#39;的ViewData项。属于&#39; System.Int32&#39;但必须属于&#39; IEnumerable&#39;。
我缺少的,当然这应该有效,因为我不需要IEnumerable来实现一对一的关系吗?
非常感谢任何帮助。
答案 0 :(得分:0)
The ViewData item that has the key 'CountryId' is of type 'System.Int32' but must be of type 'IEnumerable'.
[ForeignKey("Currency")]
[Display(Name = "CurrencyId", ResourceType = typeof(Internationalization.Resources.TenantFinanceConfiguration))]
public int CurrencyId { get; set; }
由于您在此处定义了int CurrencyID,因此您将获得int类型。如果你想要Enumberable of int那么你必须声明像
这样的东西 public IEnumerable<int> CurrencyId { get; set; }
答案 1 :(得分:0)
当您想要下拉列表时,通常的做法是为View使用另一个类(类似于ViewModel)。该类还将具有下拉列表的所有可能值。 (您也可以在Model类中使用它们,并将属性标记为[NotMapped]
)
public class YourViewModel : YourModel
{
public IEnumerable<int> CurrencyIds { get; set; }
}
然后在控制器中填充CurrencyIds
,稍后在您的视图中使用:
@Html.DropDownListFor(model => model.CurrencyId, new SelectList(Model.CurrencyIds ))
只有这会显示ID下拉列表。用户实际上可能对货币名称或其他财产感兴趣。因此:
public class YourViewModel : YourModel
{
public IEnumerable<Currency> Currencies { get; set; }
}
查看:
@Html.DropDownListFor(x => x.CurrencyId, new SelectList(Model.Currencies, "CurrencyId", "DisplayName", Model.CurrencyId))
(注意,我直接绑定到SelectList
中的@Model)
答案 2 :(得分:0)
谢谢大家的反馈。根据Microsoft文档,我只需要指定[ForeignKey]属性和虚拟属性。这当然是我过去与Entity Framework Code First实现这种一对一关系的方式。我一直在阅读大量关于一对一关系的文章,甚至下载示例代码并且它们都有效,它似乎是我的解决方案中的一些内容但我无法理解。我下载了一个Contoso大学的例子,他们已将这种关系指定为:
public int DepartmentID { get; set; }
public virtual Department Department { get; set; }
并且有视图
<div class="form-group">
<label class="control-label col-md-2" for="DepartmentID">Department</label>
<div class="col-md-10">
@Html.DropDownList("DepartmentID", null, htmlAttributes: new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.DepartmentID, "", new { @class = "text-danger" })
</div>
</div>
这一切都正确连线。
答案 3 :(得分:0)
如果您未在控制器方法中提供ViewBag
或在模型中未提供List<SelectListItem>
或SelectList
和/或未在其中指定,则可能会发生这种情况您的DropDownListFor
,因为您的代码中没有CountryId
。错误告诉您您拥有这个int
,它是一个int
,但是希望您用一系列项目而不是ViewBag
来填充下拉列表。它将尝试查找名称为CountryId
的{{1}},因为在您的View代码中未指定任何集合时,它将默认尝试执行此操作;如果没有,则它将无法继续进行在那里,因为这样只能看到CountryId
指向您要设置下拉菜单ID值的整数值。
我发现避免问题的最好方法是添加:
public SelectListItem[] CountriesList;
public SelectListItem[] CurrenciesList;
添加到ViewModel,然后在控制器中相应地添加项目:
public ActionResult Index()
{
MyViewModel mvm = new MyViewModel();
mvm = Initialize(mvm);
return View(mvm);
}
public MyViewModel Initialize(MyViewModel mvm)
{
if (_CurrenciesList == null)
mvm.CurrenciesList = GetCurrencyList();
else
mvm.CurrenciesList = _CurrenciesList;
if (_CountriesList == null)
mvm.CountriesList = Get CountriesList();
else
mvm.CountriesList = _CountriesList;
return mvm;
}
private static SelectListItem[] _CurrenciesList;
private static SelectListItem[] _CountriesList;
/// <summary>
/// Returns a static category list that is cached
/// </summary>
/// <returns></returns>
public SelectListItem[] GetCurrenciesList()
{
if (_CurrenciesList == null)
{
var currencies = repository.GetAllCurrencies().Select(a => new SelectListItem()
{
Text = a.Name,
Value = a.CurrencyId.ToString()
}).ToList();
currencies.Insert(0, new SelectListItem() { Value = "0", Text = "-- Please select your currency --" });
_CurrenciesList = currencies.ToArray();
}
// Have to create new instances via projection
// to avoid ModelBinding updates to affect this
// globally
return _CurrenciesList
.Select(d => new SelectListItem()
{
Value = d.Value,
Text = d.Text
})
.ToArray();
}
public SelectListItem[] GetCountriesList()
{
if (_CountriesList == null)
{
var countries = repository.GetAllCountries().Select(a => new SelectListItem()
{
Text = a.Name,
Value = a.CountryId.ToString()
}).ToList();
countries.Insert(0, new SelectListItem() { Value = "0", Text = "-- Please select your country --" });
_CountriesList = countries.ToArray();
}
// Have to create new instances via projection
// to avoid ModelBinding updates to affect this
// globally
return _CountriesList
.Select(d => new SelectListItem()
{
Value = d.Value,
Text = d.Text
})
.ToArray();
}
请注意,我使用“ Initialize”函数来填充ViewModel,这样,只要需要用下拉值填充ViewModel,就可以轻松完成。
我有一个单独的“存储库”类,在其中我具有从表中获取List<T>
可枚举的功能。在这种情况下,它将如下所示:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using YourSite.Models;
using YourSite.ViewModels;
namespace YourSite
{
public class Repository
{
Model1 db = new Model1(); // this is your DB Context
// Currencies
public List<Currencies> GetAllCurrencies()
{
return db.Currencies.OrderBy(e => e.Name).ToList();
}
// Countries
public List<Countries> GetAllCountries()
{
return db.Countries.OrderBy(e => e.Name).ToList();
}
}
}
它允许您执行任何排序,排除,操作-基本上需要对表数据进行任何操作,然后再将其返回使用。
在模型中,要设置外键,应该使用与您相反的方式。 ForeignKey
属性将当前ViewModel中的字段映射到要访问的表的主键,即当前ViewModel表的CountryId
映射到{{ 1}}表,因此CountryId
属性位于该Country
表的虚拟实例之上。 [ForeignKey("CountryId")]
属性可以代替Country
:
DisplayName
我们使用Display(Name="...", ...)]
是因为我们想要一个[DisplayName("Country")]
[Required(ErrorMessage = "Country is required")]
public int CountryId { get; set; }
[ForeignKey("CountryId")]
public virtual Country Country { get; set; }
[DisplayName("Currency")]
[Required(ErrorMessage = "Currency is required")]
public int CurrencyId { get; set; }
[ForeignKey("CurrencyId")]
public virtual Currency Currency { get; set; }
并显示DisplayName
,而不是LabelFor(model => model.CountryId)
。我们将永远不会显示虚拟Country
表,因此不必在上面放置CountryId
。验证错误也应该在Countries
字段上方,并包含要给出的实际错误(如上所示)。附带说明:如果尚未进行模型验证,则可以在控制器函数中使用以下代码行:
DisplayName
在视图上,下拉代码如下:
Id
例如,如果要使用ValidationContext vc = new ValidationContext(sta);
ICollection<ValidationResult> results = new List<ValidationResult>(); // results of the validation
bool isValid = Validator.TryValidateObject(mvm, vc, results, true); // Validates the object and its properties
int newId = 0;
if (isValid)
{
// Save MVM
newId = repository.SaveMVM(mvm); // function should save record and return ID
return View("Edit", new { mvm.Id = newId });
}
else
RedirectToAction("Edit", mvm);
,而不是使用<div class="form-group">
@Html.LabelFor(model => model.CurrencyId, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownListFor(
model => model.CurrencyId, // Specifies the selected CountryId
//(IEnumerable<SelectListItem>)ViewData["CurrencyId"],
Model.CurrenciesList, // supply the pre-created collection of SelectListItems
htmlAttributes: new { @class = "form-control" }
)
@Html.ValidationMessageFor(model => model.CurrencyId, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.CountryId, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownListFor(
model => model.CountryId, // Specifies the selected CountryId
//(IEnumerable<SelectListItem>)ViewData["CountryId"],
Model.CountriesList, // supply the pre-created collection of SelectListItems
htmlAttributes: new { @class = "form-control" }
)
@Html.ValidationMessageFor(model => model.CountryId, "", new { @class = "text-danger" })
</div>
</div>
函数,请使用(IEnumerable)<SelectListItem>ViewData["CountryId"]
:在{{1 }}方法,然后返回View。您也可以改用Initialize()
-它们是可互换的。但是,我并没有获得将值绑定到ViewBag
对象中的任何一个方面的成功,所以这就是为什么我更喜欢使用模型对象(ViewBag.CountryId = GetCountriesList();
)的原因,但是我想显示在下拉菜单中添加选项的各种方式。