How to use a string as a primary key in MVC 3 Entity Framework 4.1 Code First?
I'm using the latest and greatest Entity Framework Code First and I'm running into a scenario where I want one of my classes to use a string for the primary key. I had to manually add the key to the Create View (by default it treats it like an identity). However, when I try to create a new MyAccount, I get the error below. I'm using the MVC Scaffolder Repository pattern to build the MyAccountController. Your wisdom I seek with great appreciation.
Model:
public class MyAccount
{
[Key, Required, MaxLength(80), Display(Name = "User name")]
public string UserName { get; set; }
[Required, DataType(DataType.EmailAddress), MaxLength(100), Display(Name = "Email address")]
public string Email { get; set; }
}
View:
<% using (Html.BeginForm()) { %>
<%: Html.ValidationSummary(true) %>
<legend>MyAccount</legend>
<div class="editor-label">
<%: Html.LabelFor(model => model.UserName) %>
</div>
<div class="editor-field">
<%: Html.EditorFor(model => model.UserName) %>
<%: Html.ValidationMessageFor(model => model.UserName)%>
</div>
<%: Html.Partial("CreateOrEdit", Model) %>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
<% } %>
Controller:
//
// GET: /MyAccount/Create
public ActionResult Create()
{
return View();
}
//
// POST: /MyAccount/Create
[HttpPost]
public ActionResult Create(MyAccount myaccount)
{
if (ModelState.IsValid) {
myaccountRepository.InsertOrUpdate(myaccount);
myaccountRepository.Save();
return RedirectToAction("Index");
} else {
return View();
}
}
Repository:
public class MyAccountRepository : IMyAccountRepository
{
Par4ScoreContext context = n开发者_开发百科ew Par4ScoreContext();
public IQueryable<MyAccount> All
{
get { return context.MyAccounts; }
}
public IQueryable<MyAccount> AllIncluding(params Expression<Func<MyAccount, object>>[] includeProperties)
{
IQueryable<MyAccount> query = context.MyAccounts;
foreach (var includeProperty in includeProperties) {
query = query.Include(includeProperty);
}
return query;
}
public MyAccount Find(string id)
{
return context.MyAccounts.Find(id);
}
public void InsertOrUpdate(MyAccount myaccount)
{
if (myaccount.UserName == default(string)) {
// New entity
context.MyAccounts.Add(myaccount);
} else {
// Existing entity
context.Entry(myaccount).State = EntityState.Modified;
}
}
public void Delete(string id)
{
var myaccount = context.MyAccounts.Find(id);
context.MyAccounts.Remove(myaccount);
}
public void Save()
{
context.SaveChanges();
}
}
public interface IMyAccountRepository
{
IQueryable<PlayerAccount> All { get; }
IQueryable<PlayerAccount> AllIncluding(params Expression<Func<MyAccount, object>>[] includeProperties);
MyAccount Find(string id);
void InsertOrUpdate(MyAccount playeraccount);
void Delete(string id);
void Save();
}
Error in MyAccountRepository.Save():
System.Data.Entity.Infrastructure.DbUpdateConcurrencyException was unhandled by user code:
"Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries."
StackTrace:
at System.Data.Entity.Internal.InternalContext.SaveChanges()
at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
at System.Data.Entity.DbContext.SaveChanges()
at MyProject.Models.MyAccountRepository.Save()
....
InnerException: System.Data.OptimisticConcurrencyException
Message=Store update, insert, or delete statement affected an unexpected number of rows (0).
Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.
Source=System.Data.Entity
StackTrace:
at System.Data.Mapping.Update.Internal.UpdateTranslator.ValidateRowsAffected(Int64 rowsAffected, UpdateCommand source)
at System.Data.Mapping.Update.Internal.UpdateTranslator.Update(IEntityStateManager stateManager, IEntityAdapter adapter)
at System.Data.EntityClient.EntityAdapter.Update(IEntityStateManager entityCache)
at System.Data.Objects.ObjectContext.SaveChanges(SaveOptions options)
at System.Data.Entity.Internal.InternalContext.SaveChanges()
Since MVC model binder will assign an empty string to UserName
you can check whether its new or not by using string.IsNulOrEmpty(playeraccount.UserName)
. You can use IsNullOrWhiteSpace if you treat spaces as empty.
public void InsertOrUpdate(MyAccount myaccount)
{
if (string.IsNulOrEmpty(myaccount.UserName)) {
// New entity
context.MyAccounts.Add(myaccount);
} else {
// Existing entity
context.Entry(myaccount).State = EntityState.Modified;
}
}
精彩评论