问题:
用户创建一个新帐户。必填字段之一是BusinessGroup
。BusinessGroup
是导航参考属性。用户BusinessGroup
从下拉框中选择,代码BusinessGroup
在数据库中搜索,检索它并将其分配给帐户。听起来很简单,对吧?
出于某种原因,每次您保存一个新帐户时,它也会BusinessGroup
在 BusinessGroups 的数据库表中插入另一个帐户,即使它已经存在,因为我从数据库中检索它并直接将其分配给帐户。EF上下文仍然认为它是一个新的。
顺便说一句,我使用代码优先,因为我遵循 TDD 方法。
这是我的 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。
这是商业集团 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;
}
.............
}
上面的粗体代码我为 BusinessGroupId 分配了一个值(GUID),而不是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 上的 Save 中关闭 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。难道我做错了什么?