問題:
ユーザーが新しいアカウントを作成します。必須フィールドの 1 つは ですBusinessGroup
。BusinessGroup
ナビゲーション参照プロパティです。ユーザーBusinessGroup
がドロップダウン ボックスから を選択すると、コードがBusinessGroup
データベースで を検索して取得し、アカウントに割り当てます。かなり単純に聞こえますよね?
何らかの理由で、新しいアカウントを保存するたびBusinessGroup
に、データベースから取得してアカウントに直接割り当てたときに既に存在していても、BusinessGroups のデータベース テーブルに別のアカウントが挿入されます。EFコンテキストはまだ新しいものだと考えています。
ところで、TDD アプローチに従っているため、Code-First を使用します。
ここに私のPOCOがあります:
[Table("Account")]
public class Account
{
[HiddenInput]
public int Id { get; set; }
[Required(ErrorMessage = "The Name is required")]
public string Name { get; set; }
public Guid? BusinessGroupId { get; set; }
[Required(ErrorMessage = "The Business Group is required")]
public BusinessGroup BusinessGroup { get; set; }
}
データベースで生成される外部キーの命名規則をオーバーライドしたいので、上記の外部キー BusinessGroupId も指定しました。
BusinessGroup POCO は次のとおりです。
[Table("BsuinessGroup")]
public class BusinessGroup
{
[HiddenInput]
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
[Required]
public string Name { get; set; }
}
ユーザーがドロップダウン ボックスから選択したものに基づいてデータベースから BusinessGroup を取得するコードを次に示します。ID は GUID です (このコードは MVC カスタム モデル バインダー内に存在するため、ModelBinderContext を公開することに注意してください。
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
//see if there is an existing model to update and create one if not
var account = (Account) bindingContext.Model ?? new Account();
//find out if the value provider has a required prefix
bool hasPrefix = bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName);
var prefix = hasPrefix ? String.Format("{0}.", bindingContext.ModelName) : String.Empty;
_context = bindingContext;
_prefix = prefix;
//map the fields of the model object
account.Id = Convert.ToInt32((GetValue("Id")));
account.Name = (GetValue("Name"));
account.Phone = (GetValue("Phone"));
account.Fax = (GetValue("Fax"));
account.Email = (GetValue("Email"));
account.Website = (GetValue("Website"));
account.Audit = new Audit{Created = DateTime.Now, CreatedBy = "renso"};
//I changed this to assign the ID rather than assign the BusinessGroup itself since that did not work
//and causes a new BusinessGroup to be inserted into the DB on every save of an account,
// this solution seems to work but I am not sure if this is the correct behavior.
**var tempBusinessGroup = ((Controllers.AccountController)controllerContext.Controller).
BusinessGroupRepository.Find(GetGuidValue("BusinessGroup"));
account.BusinessGroupId = tempBusinessGroup.Id;**
return account;
}
private Guid GetGuidValue(string key)
{
var vpr = _context.ValueProvider.GetValue(_prefix + key);
return vpr == null ? new Guid() : new Guid(vpr.AttemptedValue);
}
private string GetValue(string key)
{
var vpr = _context.ValueProvider.GetValue(_prefix + key);
return vpr == null ? null : vpr.AttemptedValue;
}
.............
}
上記の太字のコードでは、値 (GUID) を BusinessGroupId に割り当てるのではなく、BusinessGroup
正しく動作しているようです。それはEFの意図した動作ですか、バグですか?
BusinessGroup
以下のコードに変更すると、新しいアカウントを保存するときに新しいアカウントが作成されるという問題が発生します。
account.BusinessGroup = ((Controllers.AccountController)controllerContext.Controller).
BusinessGroupRepository.Find(GetGuidValue("BusinessGroup"));
ここに私のアクションがあります:
[HttpPost]
public ActionResult Create(Account account)
{
if (ModelState.IsValid)
{
AccountRepository.InsertOrUpdate(account);
AccountRepository.Save();
return RedirectToAction("List");
}
var viewData = new AccountControllerViewData { Account = account, BusinessGroups = BusinessGroupRepository.All };
return View(viewData);
}
保存も失敗します。何らかの理由で、AccountRepository の保存で ValidateOnSaveEnabled=false をオフにする必要があります。それが機能する理由がまったくわかりません。BusinessGroup
アカウントの BusinessGroupId プロパティを設定すると、欠落しているというエラーが表示されます。
public void InsertOrUpdate(Account account)
{
if (account.Id == default(Int32))
{
// New entity
_context.Account.Add(account);
}
else
{
// Existing entity
_context.Entry(account).State = EntityState.Modified;
}
}
public void Save()
{
//need the validation switched off or it fails with an error
_context.Configuration.ValidateOnSaveEnabled = false;
_context.SaveChanges();
}
ここで何かが欠けているように感じます。EF 2ed、Code-First、または DbContext の本で答えを見つけることができませんでした。POCO で独自の外部キーを定義すると、私の例で Account から BusinessGroup にナビゲーション参照を割り当てたいときはいつでも、BusinessGroup に直接割り当てることはできず、値/キーを割り当てる必要があるようです外部キー。この例では BusinessGroupId です。私は何か間違ったことをしていますか?