Exception in SaveChanges() method (Entity.Framework) - asp.net-mvc

There is class User
namespace TaskManager.Models
{
public class User
{
private const int NAME_LENGTH = 200;
private const int EMAIL_LENGTH = 100;
private const int PASSWORD_MIN_LENGTH = 5;
private const int PASSWORD_MAX_LENGTH = 20;
public int Id { get; set; }
[Required]
[StringLength(NAME_LENGTH)]
public string Name { get; set; }
[Required]
[EmailAddress]
[StringLength(EMAIL_LENGTH)]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
[StringLength(PASSWORD_MAX_LENGTH, MinimumLength = PASSWORD_MIN_LENGTH)]
public string Password { get; set; }
public string PasswordSalt { get; set; }
[Required]
[DefaultValue(UserType.User)]
public UserType Type { get; set; }
public ICollection<Task> Tasks { get; set; }
}
public enum UserType
{
Admin = 0,
User = 1
}
}
and I try to create custom user registration using SimpleCrypto.
Here is my method
[HttpGet]
public ActionResult Registration()
{
return View();
}
[HttpPost]
public ActionResult Registration(User user)
{
if (ModelState.IsValid)
{
if (!IsUserExist(user.Email))
{
var crypto = new SimpleCrypto.PBKDF2();
var encrpPass = crypto.Compute(user.Password);
var newUser = _db.Users.Create();
newUser.Name = user.Name;
newUser.Email = user.Email;
newUser.Type = UserType.User.ToString();
newUser.Password = encrpPass;
newUser.PasswordSalt = crypto.Salt;
_db.Users.Add(newUser);
_db.SaveChanges();
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("", "User already exists");
}
}
else
{
ModelState.AddModelError("", "Data is incorrect");
}
return View(user);
}
If I use this table schema, registration works correctly
but if I try to set Id as Primary Key and set Identity == true and Identity Increment = 1
I got an Exception in _db.SaveChanges(); method.
What do I do wrong? How to fix it?
Exception
System.Data.SqlServerCe.SqlCeException: The column cannot be modified. [ Column name = Id ]
I tried to change model
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
but I got the same exception.

Related

Cannot insert the value NULL into column from unrelated entity

I'm getting a "Cannot insert null into column" error from an unrelated table when trying to add a new record for the "original" table.
I have the following two (relevant) entities:
public class Complex
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public Guid OwnerId { get; set; }
[ForeignKey("OwnerId")]
public Owner Owner { get; set; }
public Guid AddressId { get; set; }
[ForeignKey("AddressId")]
public virtual Address Address { get; set; }
public virtual ICollection<Unit> Units { get; set; }
public virtual ICollection<StaffMember> StaffMembers { get; set; }
public Complex()
{
this.Id = System.Guid.NewGuid();
this.Units = new HashSet<Unit>();
this.StaffMembers = new HashSet<StaffMember>();
}
public void AddUnit(Unit unit)
{
Units.Add(unit);
}
public void AddStaff(StaffMember staffMember)
{
StaffMembers.Add(staffMember);
}
}
and
public class Owner
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public Guid ContactInfoId { get; set; }
[ForeignKey("ContactInfoId")]
public ContactInfo ContactInfo { get; set; }
public ICollection<StaffMember> Employees { get; set; }
public ICollection<Complex> Complexes { get; set; }
public Owner()
{
this.Id = System.Guid.NewGuid();
this.Employees = new HashSet<StaffMember>();
this.Complexes = new HashSet<Complex>();
}
public void AddEmployee(StaffMember employee)
{
Employees.Add(employee);
}
public void AddComplex(Complex complex)
{
Complexes.Add(complex);
}
}
I'm trying to add a new owner in the following code:
if (ModelState.IsValid)
{
Owner newOwner = new Owner();
ContactInfo newContactInfo = new ContactInfo();
Address newAddress = new Address();
newAddress.Address1 = viewModel.ContactInfo.Address.Address1;
newAddress.Address2 = viewModel.ContactInfo.Address.Address2;
newAddress.City = viewModel.ContactInfo.Address.City;
newAddress.State = viewModel.ContactInfo.Address.State;
newAddress.Zip = viewModel.ContactInfo.Address.Zip;
newContactInfo.Address = newAddress;
newContactInfo.Email = viewModel.ContactInfo.Email;
newContactInfo.Phone1 = viewModel.ContactInfo.Phone1;
newContactInfo.Phone2 = viewModel.ContactInfo.Phone2;
newOwner.Name = viewModel.Name;
newOwner.ContactInfo = newContactInfo;
using (REMSDAL dal = new REMSDAL())
{
dal.Owners.Add(newOwner);
var result = await dal.SaveChangesAsync();
if (result > 0)
{
viewModel.ActionStatusMessageViewModel.StatusMessage = "Owner " + viewModel.Name + " added.";
viewModel.Name = "";
return View(viewModel);
}
}
}
...but getting this error:
Exception Details: System.Data.SqlClient.SqlException: Cannot insert the value NULL into column 'OwnerId', table 'REMS.dbo.Complexes'; column does not allow nulls. UPDATE fails.
The statement has been terminated.
How can I be getting an error regarding Complexes when I'm trying to add an Owner?

Code First adding default field values in database (ASP.NET MVC)

There are two models
User
public class User
{
private const int NameLength = 200;
private const int EmailLength = 100;
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[StringLength(NameLength)]
public string Name { get; set; }
[Required]
[EmailAddress]
[StringLength(EmailLength)]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
[StringLength(200)]
public string Password { get; set; }
public string PasswordSalt { get; set; }
[Required]
[DefaultValue(UserType.User)]
public UserType Type { get; set; }
[DefaultValue(true)]
public bool IsActive { get; set; }
public virtual List<Task> Tasks { get; set; }
public User()
{
Tasks = new List<Task>();
}
}
[Flags]
public enum UserType
{
Admin = 0,
User = 1
}
and RegisterUserModel which I use to register user
public class RegisterUserModel
{
private const int NameLength = 200;
private const int EmailLength = 100;
private const int PasswordMinLength = 5;
private const int PasswordMaxLength = 20;
[Key]
public int Id { get; set; }
[Required]
[StringLength(NameLength)]
public string Name { get; set; }
[Required]
[EmailAddress]
[StringLength(EmailLength)]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
[StringLength(PasswordMaxLength, MinimumLength = PasswordMinLength)]
public string Password { get; set; }
}
and this is method from my controller
[HttpPost]
[AjaxAction]
public ActionResult Registration(RegisterUserModel registerUser)
{
if (ModelState.IsValid)
{
if (!IsUserExist(registerUser.Email))
{
var crypto = new SimpleCrypto.PBKDF2();
var encrpPass = crypto.Compute(registerUser.Password);
var newUser = db.Users.Create();
newUser.Name = registerUser.Name;
newUser.Email = registerUser.Email;
newUser.Type = UserType.User;
newUser.IsActive = true;
newUser.Password = encrpPass;
newUser.PasswordSalt = crypto.Salt;
db.Users.Add(newUser);
db.SaveChanges();
FormsAuthentication.SetAuthCookie(newUser.Email, false);
return Json(new {status = "OK", message = "Success"}, JsonRequestBehavior.AllowGet);
}
return Json(new { status = "ERROR", message = "User already exists" }, JsonRequestBehavior.AllowGet);
}
return Json(new { status = "ERROR", message = "Data is incorrect" }, JsonRequestBehavior.AllowGet);
}
And I don't like I need to set values
newUser.Type = UserType.User;
newUser.IsActive = true;
in controller manually although I have these default values in my User model, I think it's not very good practice, but I don't know how to avoid it?
DefaultValueAttribute doesn't do what you think it does, namely, actually set an actual value on the property. If you want the property to have a default value, then you need a custom getter and setter:
private UserType? type;
public UserType Type
{
get { return type ?? UserType.User; }
set { type = value; }
}
In that scenario you can either use the properties to set the values for the User or you can create a constructor and set those properties inside..
If you don't like setting properties manually you can use a mapping tool such as AutoMapper (http://automapper.org/) to do the mapping for you.
Regards,

StackOverflowException was unhandled: An unhandled exception of type 'System.StackOverflowException' occurred in mscorlib.dll

i am using asp.net mvc 4 and entity framework 5 in a project. i have a base Entity that all entities derived from it:
public abstract class BaseEntity
{
[Required]
public virtual int Id { get; set; }
[Required]
public virtual DateTime CreatedOn { set; get; }
public virtual string CreatedBy { set; get; }
[Required]
public virtual DateTime ModifiedOn { set; get; }
public virtual string ModifiedBy { set; get; }
}
First the Account Entity is a class for application user:
public class Account : BaseEntity
{
public string UserName { get; set; }
public string Password { get; set; }
public byte[] AvatarBinary { get; set; }
public string AvatarMimeType { get; set; }
public virtual IList<AccountInRole> AccountRoles { get; set; }
}
Role of the User :
public class Role : BaseEntity
{
public string RoleName { get; set; }
public virtual IList<AccountInRole> AccountRoles { get; set; }
}
each User can have multiple Role and vice versa:
public class AccountInRole : BaseEntity
{
public int AccountId { get; set; }
public int RoleId { get; set; }
public virtual Account Account { get; set; }
public virtual Role Role { get; set; }
}
when i want to give roles for an specific user, call GetRoles method in Accountrepository. this is implemented in this way:
public class AccountRepository : IAccountRepository
{
#region Properties
private CharityContext DataContext { get; set; }
public IQueryable<Account> Accounts
{
get { return DataContext.Accounts; }
}
#endregion
#region Ctors
public AccountRepository() : this(new CharityContext())
{
}
public AccountRepository(CharityContext db)
{
DataContext = db;
}
#endregion
#region Methods
public List<Role> GetRoles(string userName)
{
var acc = DataContext.Accounts;
var query = from u in DataContext.Accounts
from r in DataContext.Roles
from ur in DataContext.AccountInRoles
where ur.AccountId == u.Id && ur.RoleId == r.Id && u.UserName == userName
select r;
return query.ToList();
}
#endregion
}
in this method, an exception has thrown when the compiler want to run above LINQ query. this exception is:
StackOverflowException was unhandled
An unhandled exception of type 'System.StackOverflowException' occurred in mscorlib.dll
{Cannot evaluate expression because the current thread is in a stack overflow state.}
the GetRoles method are call two time :
one time from the Custom Authorize Attribute:
public class CustomAuthorize : AuthorizeAttribute
{
//private readonly IAccountRepository _accountRepository;
private string[] roles;
//public CustomAuthorize(params string[] roles)
//{
// this.roles = roles;
//}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null)
throw new ArgumentNullException("httpContext");
if (!httpContext.User.Identity.IsAuthenticated)
return false;
if (Roles == string.Empty)
return true;
var lstRoles = Roles.Split(',');
AccountRepository _accountRepository = new AccountRepository();
var userRoles = _accountRepository.GetRoles(httpContext.User.Identity.Name);
foreach (var role in lstRoles)
{
bool isFound = false;
foreach (var userRole in userRoles)
{
if (userRole.RoleName == role)
isFound = true;
}
if (!isFound) return false;
}
return true;
}
}
and second time from the Application_AuthenticateRequest method in the Global.asax.cs :
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
string cookie = FormsAuthentication.FormsCookieName;
HttpCookie httpCookie = Request.Cookies[cookie];
if (httpCookie == null) return;
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(httpCookie.Value);
if(ticket == null || ticket.Expired) return;
FormsIdentity identity = new FormsIdentity(ticket);
var _accountRepository = new AccountRepository();
var roles = _accountRepository.GetRoles(identity.Name);
var principal = new CharityAccount(identity.Name, roles.Select(x => x.RoleName).ToArray());
Context.User = Thread.CurrentPrincipal = principal;
}
CharityAccount that ou can see it in above method is implemented in this way:
public class CharityAccount : IPrincipal
{
private string[] roles;
private IIdentity identity;
public IIdentity Identity
{
get { return identity; }
}
public bool IsInRole(string role)
{
return Array.IndexOf(roles, role) >= 0;
}
public CharityAccount(String name, String[] roles)
{
identity = new GenericIdentity(name, "Custom authentication");
this.roles = roles;
}
}
According to your idea, what is the problem?
regards
You have done few things which can lead you to troubles. The one I can see is the circular reference of Accounts, roles in AccountinRoles and vice versa.
I have simplified your code though it's not the best design(But I believe in keeping things simple and stupid). You can keep your virtual properties if you really mean what the virtual properties are for in entities.
This working and running fine.
public abstract class BaseEntity
{
public int Id { get; set; }
public DateTime CreatedOn { set; get; }
}
public class Account : BaseEntity
{
public string UserName { get; set; }
public string Password { get; set; }
}
public class Role : BaseEntity
{
public string RoleName { get; set; }
}
public class AccountInRole
{
public int AccountId { get; set; }
public int RoleId { get; set; }
}
public class Operation
{
public List<Role> GetRoles()
{
List<Account> lstAccount = new List<Account>();
List<Role> lstRole = new List<Role>();
List<AccountInRole> lstAccountInRoles = new List<AccountInRole>();
Account ac1 = new Account
{
Id = 1,
UserName = "Jack",
Password = "somePassword2",
CreatedOn = DateTime.Now
};
Account ac2 = new Account
{
Id = 2,
UserName = "Sam",
Password = "somePassword1",
CreatedOn = DateTime.Now
};
lstAccount.Add(ac1);
lstAccount.Add(ac2);
Role r1 = new Role
{
Id = 1,
RoleName = "TestRole1",
CreatedOn = DateTime.Now
};
Role r2 = new Role
{
Id = 2,
RoleName = "TestRole2",
CreatedOn = DateTime.Now
};
lstRole.Add(r1);
lstRole.Add(r2);
AccountInRole acRole1 = new AccountInRole
{
AccountId = ac1.Id,
RoleId = r1.Id
};
AccountInRole acRole2 = new AccountInRole
{
AccountId = ac2.Id,
RoleId = r2.Id
};
lstAccountInRoles.Add(acRole1);
lstAccountInRoles.Add(acRole2);
string userName = "Sam";
// Query the data
var roles = from u in lstAccount
where u.UserName == userName
from acc in lstAccountInRoles
from r in lstRole
where acc.AccountId == u.Id
&& r.Id == acc.RoleId
select r;
return roles.ToList();
}
}

Code First Many to Many : how to add a collection to an object

I have a Many to Many relationship between User and Role. They are set up as follows :
public partial class User
{
//public User()
//{
// //this.DateCreated = DateTime.Now; //set default value
// Roles = new HashSet<Role>();
//}
public ICollection<Role> Roles { get; set; } //many to many relationship
public int UserId { get; set; }
public string FirstName { get; set; }
public string Surname { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string City { get; set; }
//foreign key
public int CountryId { get; set; }
//navigation properties
public virtual Country Country { get; set; }
//foreign key
public int LanguageId { get; set; }
//navigation properties
public virtual Language Language { get; set; }
public string EmailAddress { get; set; }
public long? FacebookId { get; set; }
public DateTime DateCreated { get; set; }
}
public partial class Role
{
//public Role()
//{
// Users = new HashSet<User>();
//}
public ICollection<User> Users { get; set; } //many to many relationship
public int RoleId { get; set; }
public string RoleName { get; set; }
}
//many to many relationship
modelBuilder.Entity<User>().
HasMany(c => c.Roles).
WithMany(p => p.Users).
Map(
m =>
{
m.MapLeftKey("UserId");
m.MapRightKey("RoleId");
m.ToTable("UserRoles", schemaName: "Main");
});
In my code where I add a new user, I want to be able to add Roles to that user. But whenever I do this, new Roles are also added to the Roles table. What is the correct way to do this?
[HttpPost]
public ActionResult UserAdd(UserDTO user)
{
if (ModelState.IsValid)
{
//do mapping manually here
Country country = _repository.GetCountryByCountryId(user.CountryId);
Language language = _repository.GetLanguageByLanguageId(user.LanguageId);
User entity = new User();
entity.FirstName = user.FirstName;
entity.Surname = user.Surname;
entity.Username = user.Username;
entity.Password = user.Password;
entity.City = user.City;
entity.CountryId = country.CountryId;
entity.LanguageId = language.LanguageId;
entity.Country = country;
entity.Language = language;
entity.EmailAddress = user.EmailAddress;
entity.FacebookId = null;
entity.DateCreated = DateTime.Now;
entity.Roles = new List<Role>();
foreach (int i in user.Roles)
{
Role role = _repository.GetRoleByRoleId(i);
entity.Roles.Add(new Role { RoleId = i, RoleName = role.RoleName });
}
int newUserId = _repository.AddUser(entity);
return View();
} }
entity.Roles.Add(new Role { RoleId = i, RoleName = role.RoleName });
Here is the problem, because you create new instance, it means absolutely new role. You have to work with role from your context:
foreach (int i in user.Roles)
{
Role role = _repository.GetRoleByRoleId(i);
entity.Roles.Add(role); // don't create new instance here!
}

The model item passed into the dictionary is of type 'ViewModels.SiteModel',

I'm newbie to MVC architecture.When I'm trying to update, its showing error ,Its totally strange but the data is updating.
The model item passed into the dictionary is of type 'CMS.Domain.Models.Site', but this dictionary requires a model item of type 'CMS.Web.ViewModels.SiteModel'.'.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: The model item passed into the dictionary is of type 'CMS.Web.ViewModels.SiteModel', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[CMS.Web.ViewModels.SiteModel]'.
My code looks like:
ViewModels:
namespace CMS.Web.ViewModels
{
public class SiteModel
{
public SiteModel()
{
SiteStatus = new List<SelectListItem>();
}
[Key]
public int ID { get; set; }
[Required(ErrorMessage = "Site Name is required")]
[Display(Name = "Site Name")]
public string Title { get; set; }
[Display(Name = "Require Login")]
public bool RequiresLogin { get; set; }
[Display(Name = "Force HTTPS")]
public bool ForceHTTPS { get; set; }
[Display(Name = "Enable Approval")]
public bool Approval { get; set; }
[AllowHtml]
public IList<SelectListItem> SiteStatus { get; set; }
public bool Deleted { get; set; }
public string CreatedBy { get; set; }
public DateTime CreatedOn
{
get { return _createdOn; }
set { _createdOn = value; }
}
private DateTime _createdOn = DateTime.Now;
public string LastUpdatedBy { get; set; }
public DateTime LastUpdatedOn
{
get { return _lastUpdatedOn; }
set { _lastUpdatedOn = value; }
}
private DateTime _lastUpdatedOn = DateTime.Now;
[Display(Name = "Site State")]
public string SiteState { get; set; }
}
}
Model:
namespace CMS.Domain.Models
{
public partial class Site : Model
{
public string Title { get; set; }
public bool Approval { get; set; }
public bool RequiresLogin { get; set; }
public bool ForceHTTPS { get; set; }
public virtual string SiteStatus { get; set; }
public bool Deleted { get; set; }
}
}
Controller:
public ActionResult Index()
{
var _sites = _siterepository.FindAll();
return View(_sites);
}
public ActionResult Add()
{
var model = new SiteModel();
var _SiteStatus = _siterepository.GetSiteStatus();
foreach (var _sitestatus in _SiteStatus)
{
model.SiteStatus.Add(new SelectListItem()
{
Text = _sitestatus.StatusName,
Value = _sitestatus.StatusName.ToString()
});
}
return View(model);
}
[HttpPost]
public ActionResult Add(SiteModel _sitemodel)
{
var model = _sitemodel.ToEntity();
_siterepository.Add(model);
return View(model);
}
public ActionResult Edit(int id)
{
var model = new SiteModel();
var Site = _siterepository.Find(id);
model = Site.ToModel();
var _SiteStatus = _siterepository.GetSiteStatus();
foreach (var _sitestatus in _SiteStatus)
{
model.SiteStatus.Add(new SelectListItem()
{
Text = _sitestatus.StatusName,
Value = _sitestatus.StatusName.ToString(),
Selected = _sitestatus.StatusName == Site.SiteStatus
});
}
return View(model);
}
[HttpPost]
public ActionResult Edit(SiteModel _sitemodel)
{
var model = _sitemodel.ToEntity();
_siterepository.Update(model);
return View(model);
}
I'm struggling to resolve this , please help.
Check your View's model declaration. It is expecting an enumerable list (IEnumerable<CMS.Web.ViewModels.SiteModel>), but you are passing it a single instance of CMS.Web.ViewModels.SiteModel

Resources