[英]Foreign Key Entity Framework & Razor
我正在使用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>
我得到錯誤:
關鍵字為“ CountryId”的ViewData項的類型為“ System.Int32”,但必須類型為“ IEnumerable”。
我想念的是什么,因為我不需要IEnumerable一對一的關系,這肯定可以工作嗎?
任何幫助,不勝感激。
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類型。 如果您想要int的Enumberable,則必須聲明類似
public IEnumerable<int> CurrencyId { get; set; }
如果需要下拉菜單,通常的做法是為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)
謝謝大家的反饋。 根據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>
而且所有連線都正確連接。
如果您沒有在控制器方法中提供ViewBag
或在模型中未提供List<SelectListItem>
或SelectList
,並且/或者如果未在DropDownListFor
指定它,因為您的代碼中沒有該ViewBag
,則可能會發生這種情況。 錯誤告訴您您有一個CountryId
int
,它是一個int
,但是希望您用項目集合而不是int
來填充下拉列表。 它會試圖找到ViewBag
名為CountryId
,因為它會默認嘗試做時,有沒有在你的瀏覽代碼指定的集合,如果集合不存在它無法進行,因為那只能看到的是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
屬性映射你在當前視圖模型您正在訪問的表的主鍵有領域,即CountryId
您當前視圖模型的表到的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"]
,則可以使用ViewBag
代替使用Initialize()
函數:添加ViewBag.CountryId = GetCountriesList();
在返回View之前,請使用Index()
方法。 您也可以使用ViewData["CountryId"] = GetCountriesList();
相反-這些是可以互換的。 但是,我並沒有獲得成功將值綁定到DropDownListFor
對象的任何一個,因此,這就是為什么我更喜歡使用模型對象( public SelectListItem[] CountriesList; public SelectListItem[] CurrenciesList;
)的原因,但是,我以為我會展示在下拉菜單中添加選項的各種方式。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.