私はこれに対する答えを2日間探していました。何が悪いのか正確にはわかりませんが、原因の可能性を特定したのではないかと思います。
私のプログラムは、私の会社の機器を追跡するデータベーストラッカー/マネージャーです。ユーザーは、機器の照会、作成、および編集を行うことができます。現在、データベースのクエリは機能しますが、データベースに追加またはデータベースを更新しようとすると、プログラムは検証エラーをスローします(これについては以下で詳しく説明します)。
多くのnull許容属性を持つ機器クラスがあり(ユーザーは新しいエントリを作成するときにこれらのプロパティの多くを知らない可能性があるため、ほとんどすべての属性をnull可能のままにしました)、接続されています(外部キーと遅延読み込み/「仮想」を使用) )データベースの正規化を支援するために作成された他のいくつかのテーブルに。
役立つ簡略化されたコードを次に示します。
機器モデル:
public class Equipment
{
public int EquipmentId { get; set; } //required
public string Company { get; set; } //required
public bool Verified { get; set; } //required
(...other non-required attributes...)
//The following two attributes are where my problems are I believe
[ForeignKey("PAU")]
public int PAUId { get; set; } //required and Foreign Key
//This is not required (but is FK to the related table) if the user doesn't know
[ForeignKey("Division")]
public Nullable<int> DivisionId { get; set; }
//These are the lazy loading connections to the other tables in the database
public virtual Division Division { get; set; }
public virtual PAU PAU { get; set; }
}
部門モデル:この表には、当社の5つの異なる部門と関連するIDのみが含まれています。
public class Division
{
public Division()
{
this.Equipments = new HashSet<Equipment>();
}
//These are non-nullable/required fields
public int DivisionId { get; set; }
public string DivisionName { get; set; }
public virtual ICollection<Equipment> Equipments { get; set; }
}
PAUモデル:このモデルには、PAU番号と関連する説明およびIDが含まれています。
public class PAU
{
public PAU()
{
this.Equipments = new HashSet<Equipment>();
}
//These are non-nullable/required fields
public int PAUId { get; set; }
public string PAUNumber { get; set; }
public string PAUDescription { get; set; }
public virtual ICollection<Equipment> Equipments { get; set; }
}
機器コントローラー:
public class EquipmentController : Controller
{
TLCP_DEVEntities4 db = new TLCP_DEVEntities4();
(...)
public ActionResult Edit(int id)
{
ViewData["divisions"] = new SelectList(db.Equipments.
OrderBy(x => x.Division.DivisionName).
Select(x => x.Division).ToList().
Distinct(), "DivisionId", "DivisionName");
ViewData["PAUNumbers"] = new SelectList(db.Equipments.
OrderBy(x => x.PAU.PAUNumber).
Select(x => x.PAU).ToList().
Distinct(), "PAUId", "PAUNumber");
return View(db.Equipments.Find(id));
}
[HttpPost]
public ActionResult Edit(Equipment equipment)
{
if (ModelState.IsValid)
{
db.Entry(equipment).State = EntityState.Modified;
db.SaveChanges(); //fails here
return RedirectToAction("Maintenance");
}
ViewData["divisions"] = new SelectList(db.Equipments.
OrderBy(x => x.Division.DivisionName).
Select(x => x.Division).ToList().
Distinct(), "DivisionId", "DivisionName");
ViewData["PAUNumbers"] = new SelectList(db.Equipments.
OrderBy(x => x.PAU.PAUNumber).
Select(x => x.PAU).ToList().
Distinct(), "PAUId", "PAUNumber");
return View(equipment);
}
public ActionResult AddEquipment()
{
ViewData["divisions"] = new SelectList(db.Equipments.
OrderBy(x => x.Division.DivisionName).
Select(x => x.Division).ToList().
Distinct(), "DivisionId", "DivisionName");
ViewData["PAUNumbers"] = new SelectList(db.Equipments.
OrderBy(x => x.PAU.PAUNumber).
Select(x => x.PAU).ToList().
Distinct(), "PAUId", "PAUNumber");
return View();
}
public ActionResult AddEquipment(Equipment equipment)
{
try
{
db.Equipments.Add(equipment);
db.SaveChanges();
return RedirectToAction("Maintenance");
}
catch (Exception ex)
{
ViewData["divisions"] = new SelectList(db.Equipments.
OrderBy(x => x.Division.DivisionName).
Select(x => x.Division).ToList().
Distinct(), "DivisionId", "DivisionName");
ViewData["PAUNumbers"] = new SelectList(db.Equipments.
OrderBy(x => x.PAU.PAUNumber).
Select(x => x.PAU).ToList().
Distinct(), "PAUId", "PAUNumber");
return View();
}
}
(...)
}
ビューには、データベースにすでに存在する可能性のあるオプション(上記のViewDataに格納されている)が入力されているこれらの属性のドロップダウンリストがあります。このデータをモデルに保存するいくつかの異なる方法を試しましたが、この方法が最も効果的であるようです。
<%: Html.DropDownListFor(model => model.DivisionId,
ViewData["divisions"] as SelectList, "")%>
さて、多くのデバッグの後、問題は、遅延ロードされたプロパティにnull属性があり、それらのテーブルでは許可されていないためと思われます。私の例では、新しい機器オブジェクトの作成について詳しく説明しますが、編集の場合も問題は同じだと思います。
部門の例:したがって、EquipmentクラスのDivisionIdはnullになる可能性があり(ユーザーが知らないか、機器が特定の部門に属していない)、Equipmentテーブル/クラスはそれについて文句を言いません。しかし、SaveChanges()を実行しようとすると、仮想Divisionオブジェクトがnull(デフォルトのDivisionIdが0でDivisionNameがnull)であるため、プログラムが文句を言います。Equipment.DivisionIdがnullであるため、このEquipmentオブジェクトのDivisionテーブルを更新したくありません。これを回避する方法はありますか?遅延読み込みを行うべきではありませんか?
PAUの例:PAUIdはすべてのEquipmentオブジェクトの必須属性ですが、Divisionの例と同様に、PAU.PAUNumberとPAU.PAUDescriptionはnull(およびPAU.PAUIdはデフォルトの0)であるため、エラーが発生します。これは最初のものと同じですが、必須の属性があります(したがって、別の方法で処理する必要があるかどうかはわかりません)。
ですから、この問題を解決する方法がよくわかりません。
Id / Foreign Keyがnullでない場合にのみ、Equipmentオブジェクトを他のテーブルに接続したいので、遅延読み込みで問題が発生しますか?
IDに基づいて他のテーブルから機器オブジェクトの値を更新する呼び出し可能なメソッドはありますか?それは私が自分で作成する必要がある方法ですか?
遅延読み込みを削除した場合でも、正規化されたデータ(DivisionNameなど)にアクセスできるようにクエリを作成する方法を説明していただけますか?
超ロングポストをお詫び申し上げます。重要かもしれないものを見逃したくなかっただけです。
編集
PAUの例には大まかな解決策があります(ただし、Equipment内でヌルを許可しているが、個々のテーブルでは許可していないため、Divisionの例では機能しないと思います)。
public ActionResult AddEquipment(Equipment equipment)
{
try
{
equipment.PAU.PAUId = equipment.PAUId;
equipment.PAU.PAUNumber = db.PAUs.Find(equipment.PAUId).PAUNumber;
equipment.PAU.PAUDescription = db.PAUs.Find(equipment.PAUId).PAUDescription;
(...)
}
しかし、それはあまりエレガントではありません。リフレクションについてのチュートリアルを見てみましたが、あまり意味がありませんでした。それがよりエレガントな解決策であるならば、誰かがそれを説明しようとすることができますか?
私はまだDivisionの例の解決策を探しています(Equipment.DivisionIdはnull可能ですが、Division.DivisionIdはnull可能ではないため、これは機能しません)。感謝!