簡體   English   中英

外鍵實體框架和剃刀

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM