Seeding Membership with custom data Entity framework - c#

I have the fallowing Models
namespace Prometheus.Models
{
[Table("People")]
public class Person : IPerson
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Name
{
get
{
return FirstName + " " + LastName;
}
set{}
}
public string Email { get; set; }
public DateTime? LastModified { get; set; }
}
}
And one that inherits it
namespace Prometheus.Models
{
[Table("UserProfile")]
public class UserProfile : Person
{
public string UserName { get; set; }
public string CNP { get; set; }
public virtual Faculty Faculty { get; set; }
public bool? IsUSAMV { get; set; }
public virtual ICollection<Result> Results { get; set; }
public virtual ICollection<Project> Projects { get; set; }
}
}
And the seed method
private void AddUser(string user, string password,
SimpleMembershipProvider membership)
{
if (membership.GetUser(user, false) == null)
{
WebSecurity.CreateUserAndAccount(user, password, new
{
CNP = "1890531111111",
IsUSAMV = true,
});
}
}
When i try to run the seed method without UserProfile extending Person everything is ok, but when it extends it i keep getting the fallowing error.
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.UserProfile_dbo.People_Id". The conflict occurred in database "PrometheusDb", table "dbo.People", column 'Id'.
The statement has been terminated.
Any help would be greatly appreciated thanks.
I tried updateing my function to
private void AddUser(string firstName,string lastName, string password,
SimpleMembershipProvider membership)
{
var user = firstName + "." + lastName;
if (membership.GetUser(user, false) == null)
{
var profile = new UserProfile()
{
Email = "test#email.com",
FirstName = firstName,
LastName = lastName
};
_context.Users.Add(profile);
_context.SaveChanges();
WebSecurity.CreateAccount(user, password);
}
}
But now i get:
- An error occurred while updating the entries. See the inner exception for details.
System.Data.SqlClient.SqlException: Cannot insert explicit value for identity column in table 'UserProfile' when IDENTITY_INSERT is set to OFF.

You are having a problem in the order that items are created and the integrity checks on the db. I have done something similar and it has involved saving the user first then the account. My code looks like:
var user = new User { UserName = model.UserName, Organisation = userOrg };
this.repository.SaveUser(user);
string token = WebSecurity.CreateAccount(model.UserName, model.Password, true);
Notice the use of the CreateAccount rather than the CreateUserAndAccount method

Related

Seeding data using EF

I'm trying to create an ApplicationUser which has a User as a child object, this is what the models look like:
ApplicationUser:
public class ApplicationUser : IdentityUser
{
public User User { get; set; }
}
User:
public class User
{
public int Id { get; set; }
[ForeignKey("AspNetUser")]
public string AspNetUserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ApplicationUser AspNetUser { get; set; }
}
Within my DbContext I have:
public class IdentityDbContext : IdentityDbContext<ApplicationUser>
{
public IdentityDbContext(DbContextOptions<IdentityDbContext> options)
: base(options)
{
}
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.ApplyConfiguration(new AdminConfiguration());
}
}
AdminConfiguration:
public class AdminConfiguration : IEntityTypeConfiguration<ApplicationUser>
{
public void Configure(EntityTypeBuilder<ApplicationUser> builder)
{
var id = "bc62cdff-77ca-4473-a467-210eb36fdd5d";
var admin = new ApplicationUser
{
Id = id,
UserName = "admin",
NormalizedUserName = "ADMIN",
Email = "admin#dotvvm.com",
NormalizedEmail = "ADMIN#DOTVVM.COM",
EmailConfirmed = true,
SecurityStamp = new Guid().ToString("D")
};
admin.PasswordHash = GeneratePassword(admin, "Admin12345!");
builder.HasData(admin);
builder.OwnsOne(a => a.User).HasData(new User
{
Id = 1,
AspNetUserId = id,
FirstName = "Test",
LastName = "Test"
});
}
private string GeneratePassword(ApplicationUser user, string password)
{
var passHash = new PasswordHasher<ApplicationUser>();
return passHash.HashPassword(user, password);
}
}
With this code, I create a migration and try to execute Update-Database but I get this error:
To change the IDENTITY property of a column, the column needs to be dropped and recreated
I can't figure out what I'm doing wrong, does anyone know?
I'm almost sure that you're using .OwnsOne wrong (but i doubt it is root cause, i speak about it later)
Owned types are Value objects. Value objects have no identity on their own and exist only as a part of their owner like
//this is entity, it has identity
public class Person
{
public Guid Id { get; set; }
public Name Name { get; set; }
}
//and this is value object and could be owned type
public class Name
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
If you want both ApplicationUser and User to be entities (make sense) you could consider One-to-One relationship betwen them, like this
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ApplicationUser>()
.HasOne(a => a.User)
.WithOne(b => b.ApplicationUser)
.HasForeignKey<ApplicationUser>(b => b.AspNetUserId);
}
and then your
builder.HasData(new User
{
Id = 1,
AspNetUserId = id,
FirstName = "Test",
LastName = "Test"
});
might be valid and ... might not
because another possible source of you problem could be Autoincrement Id field (is it autoincrement in your User class?)
If so -
builder.OwnsOne(a => a.User).HasData(new User
{
Id = 1, //<<---- try removing this
AspNetUserId = id,
FirstName = "Test",
LastName = "Test"
});
this could solve your issue

System Exception: 'Cannot create a table without columns, does it have public properties?

I am creating a Xamarin form where I have to create a table to save details (user and password).
When I run this on emulator I end up with this error:
System Exception: cannot create table without columns
In VIEWS:
private async void _saveButton_Clicked(object Sender, EventArgs e)
{
var db = new SQLiteConnection(_dbPath);
db.CreateTable<User>();
var maxPk = db.Table<User>().OrderByDescending(c => c.Id).FirstOrDefault();
User user = new User()
{
Id = (maxPk == null ? 1 : Convert.ToInt32(maxPk.Id) +1),
Email = _emailEntry.Text,
Password = _passwordEntry.Text
};
db.Insert(user);
await DisplayAlert(null, "Saved", "Ok");
await Navigation.PopAsync();
}
In MODELS:
using SQLite;
namespace LoginPage.Models
{
public class LoginPage
{
[PrimaryKey]
public int Id { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public override string ToString()
{
return this.Email + "," + this.Password;
}
}
}
In Models change the name of your class to User. That is the Table Model you are trying to create.
using SQLite;
namespace LoginPage.Models
{
public class User
{
[PrimaryKey]
public int Id { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public override string ToString()
{
return this.Email + "," + this.Password;
}
}
}

EntityFramework 7 Error when updating record

Every time I want to updated my record, I am getting the following error:
"The instance of entity type 'User' cannot be tracked because another
instance of this type with the same key is already being tracked. When
adding new entities, for most key types a unique temporary key value
will be created if no key is set (i.e. if the key property is assigned
the default value for its type). If you are explicitly setting key
values for new entities, ensure they do not collide with existing
entities or temporary values generated for other new entities. When
attaching existing entities, ensure that only one entity instance with
a given key value is attached to the context."
Here is my code:
public void SaveRecipient(Recipient myRecipient)
{
if (myRecipient.RecipientGUID == Guid.Empty)
{
myRecipient.RecipientGUID = Guid.NewGuid();
foreach (ContactMethod tmpCM in myRecipient.ContactMethods)
{
context.Entry(tmpCM.Type).State = EntityState.Unchanged;
}
context.Entry(myRecipient.LastModifiedBy).State = EntityState.Unchanged;
context.Entry(myRecipient.Owner).State = EntityState.Unchanged;
context.Entry(myRecipient.CreatedBy).State = EntityState.Unchanged;
context.Recipients.Add(myRecipient);
}
else
{
var dbRecipient = context.Recipients
.Include(a => a.ContactMethods).ThenInclude(t => t.Type)
.Include(b => b.CreatedBy)
.Include(c => c.LastModifiedBy)
.Include(d => d.Owner).ThenInclude(o => o.Users)
.FirstOrDefault(x => x.RecipientGUID == myRecipient.RecipientGUID);
if (dbRecipient != null)
{
dbRecipient.FirstName = myRecipient.FirstName;
dbRecipient.LastName = myRecipient.LastName;
dbRecipient.Company = myRecipient.Company;
foreach (ContactMethod tmpCM in myRecipient.ContactMethods)
{
var dbCM = dbRecipient.ContactMethods.FirstOrDefault(x => x.ContactMethodGUID == tmpCM.ContactMethodGUID);
if (dbCM != null)
{
dbCM.CountryCode = tmpCM.CountryCode;
dbCM.Identifier = tmpCM.Identifier;
dbCM.IsPreferred = tmpCM.IsPreferred;
}
else
{
dbRecipient.ContactMethods.Add(tmpCM);
}
}
//Only update this if it has changed.
if (dbRecipient.LastModifiedBy.UserGUID != myRecipient.LastModifiedBy.UserGUID)
{
dbRecipient.LastModifiedBy = myRecipient.LastModifiedBy;
}
dbRecipient.LastModifiedOn = myRecipient.LastModifiedOn;
}
}
context.SaveChanges();
}
The relevant classes:
User:
public class User
{
[Key]
public Guid UserGUID { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public bool IsSiteAdmin { get; set; }
public bool IsActive { get; set; }
public DateTime? CreatedOn { get; set; }
public DateTime? LastLogin { get; set; }
}
Recipient:
public class Recipient
{
[Key]
public Guid RecipientGUID { get; set; }
[Required(ErrorMessage = "Please enter a Recipient's First Name.")]
public string FirstName { get; set; }
[Required(ErrorMessage = "Please enter a Recipient's Last Name.")]
public string LastName { get; set; }
public string Company { get; set; }
public UserGroup Owner { get; set; }
public virtual ICollection<ContactMethod> ContactMethods { get; set; }
public User CreatedBy { get; set; }
public DateTime CreatedOn { get; set; }
public User LastModifiedBy { get; set; }
public DateTime LastModifiedOn { get; set; }
public bool IsActive { get; set; }
}
Contact Methods:
public class ContactMethod
{
[Key]
[HiddenInput(DisplayValue = false)]
public Guid ContactMethodGUID { get; set; }
[ForeignKey("ContactMethodTypeGUID")]
public virtual ContactMethodType Type { get; set; }
public string CountryCode { get; set; }
[Required]
public string Identifier { get; set; }
public bool IsPreferred { get; set; }
[ForeignKey("RecipientGUID")]
public virtual Recipient Owner { get; set; }
}
This issue happens when I want to update a recipient, and it is another user doing the updating. So say user abcd did the last update, but now user zyx updates the record. So the Recipeint.LastUpdatedBy is set to the current Session User. When I do that, I get the above error. I cannot figure out how to get beyond this.
A small note: if I add this:
context.Entry(myRecipient.LastModifiedBy).State = EntityState.Unchanged;
in the if (dbRecipient.LastModifiedBy.UserGUID != myRecipient.LastModifiedBy.UserGUID)
statement, and say user lastmodifiedby is set to user abc. Now User asfg updates this recipient for the first time, it goes through, and LastModifiedBy will be set to user asfg, but say user abc goes back and changes the recipient again, so lastmodifiedby goes back to abc, it fails, with the same error.
this is driving me nuts and I cannot figure it out!!!
I got the answer to this from Arthur Vickers at Microsoft. I wanted to share.
The code that sets the navigation property dbRecipient.LastModifiedBy is setting it to an entity instance that is not being tracked by the context. It seems like in this case the context is already tracking another instance for this same entity--presumably because it was brought in by the query through including the CreatedBy navigation.
EF can't track two instances of the same entity, which is why the exception is thrown, so you will need to give EF additional information here to know what to do. This can be complicated in the general case.
For example: if the tracked instance has properties that have been modified in the other instance.
However, assuming that isn't the case, then you can just lookup the instance that is being tracked and use it instead, For example:
if (dbRecipient.LastModifiedBy.UserGUID != myRecipient.LastModifiedBy.UserGUID)
{
dbRecipient.LastModifiedBy = test.Set<User>().Find(myRecipient.LastModifiedBy.UserGUID);
}

Entity Framework throws invalid column name User_Id error

I am trying to create and associate the entities in my project. The User can have many Roles, Claims and Logins. On the other hand, A claim or login can have only one user, while a role can also be with many user. I have the relationship defined with fluent API:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
modelBuilder.Entity<User>().HasMany(u => u.Claims).WithRequired(cl => cl.User).Map(m => m.MapKey("User"));
modelBuilder.Entity<Claim>().HasRequired(cl => cl.User).WithMany(u => u.Claims).Map(m => m.MapKey("User"));
modelBuilder.Entity<User>().HasMany(u => u.Logins).WithRequired(lg => lg.User).Map(m => m.MapKey("User"));
modelBuilder.Entity<Login>().HasRequired(lg => lg.User).WithMany(u => u.Logins).Map(m => m.MapKey("User"));
modelBuilder.Entity<User>().HasMany(u => u.Roles).WithMany(ro => ro.Users).Map(userRoles =>
{
userRoles.ToTable("Users_Roles");
userRoles.MapLeftKey("User");
userRoles.MapRightKey("Role");
});
modelBuilder.Entity<Role>().HasMany(ro => ro.Users).WithMany(u => u.Roles).Map(userRoles =>
{
userRoles.ToTable("Users_Roles");
userRoles.MapLeftKey("User");
userRoles.MapRightKey("Role");
});
}
As you see, my database tables do not follow Entity Framework's convention, instead of using 'User_Id' as foreign key, the foreign key is simply named 'User'. However, I keep getting this 'Invalid Column Name: User_Id' exception message.
I tried to define the foreign key column name with the above code by calling the methods Map() and MapKey(), but with no prevail. Why is this happening? Am I writing the mapping code wrong? Can someone help?
PS: The exception error message is very unhelpful that I do not know which table this column name error is associated with. Does anyone know how to make the exception message show what the table name is, not just the column name? Thanks.
Also I've added code I used for Claim, Login and Role entities(with unrelated methods removed).
public class Claim
{
public string Id { get; protected set; }
public string Type { get; protected set; }
public string Value { get; protected set; }
public virtual User User { get; protected set; }
public Claim() { }
public Claim(string id, User user, string type, string value)
{
Id = id;
User = user;
Type = type;
Value = value;
}
}
public class Login
{
public string Id { get; protected set; }
public string Provider { get; protected set; }
public string Key { get; protected set; }
public DateTime? DateLoggedin { get; protected set; }
public virtual User User { get; protected set; }
public Login() { }
public Login(string id, string provider, string key, DateTime? dateLoggedIn = null)
{
Id = id;
Provider = provider;
Key = key;
DateLoggedin = dateLoggedIn;
}
}
public class Role
{
public string Id { get; protected set; }
public string Title { get; protected set; }
public string Description { get; protected set; }
public bool IsAdmin { get; protected set; }
public bool IsBanned { get; protected set; }
public IList<User> Users { get; protected set; }
public Role() { }
public Role(string id, string title, string description, bool isAdmin, bool isBanned)
{
Id = id;
Title = title;
Description = description;
IsAdmin = isAdmin;
IsBanned = isBanned;
}
}
The User_Id column is being created by EF convention because you map your key to a property by the same name as your navigation proeprty.
You could either remove the .Map() part and let EF handle the key, or define a key property on your Claim and Login classes, and map that in the fluent api. I would personally do the latter.
E.g:
public class Claim
{
public string Id { get; protected set; }
public string Type { get; protected set; }
public string Value { get; protected set; }
public virtual User User { get; protected set; }
public int UserId{get;set;}
public Claim() { }
}
And in your ModelCreating
modelBuilder.Entity<User>().HasMany(a=>a.Claims).WithRequired(a=>a.User).HasForeignKey(a=>a.UserId);
If you create a key field of 'EntityNameId' (UserId, ClaimId, etc) then EF convention will automatically map that as your Foreign Key, without the fluent mapping. However, if you name is something else (UsernameId, or whatever), you have to explicitly provide the mapping. Some reading on that.

ASP.NET MVC insert in two separate tables

Hello I am a newbie in ASP.NET MVC.
I have three classes Login, User and TheTradersContext as you can see below:
namespace SimpleUser.Models
{
[Table("login")]
public class Login
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[Required]
[StringLength(50)]
[Display(Name = "Email")]
public string email { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string password { get; set; }
public string status { get; set; }
public string salt { get; set; }
}
}
namespace SimpleUser.Models
{
[Table("userdata")]
public class User
{
public int ID { get; set; }
public string surname { get; set; }
public string name { get; set; }
public string sex { get; set; }
public string city { get; set; }
public string address { get; set; }
public string zipcode { get; set; }
public string tel { get; set; }
public string bdate { get; set; }
public string country { get; set; }
}
}
namespace SimpleUser.Models
{
public class TheTradersContext : DbContext
{
public DbSet<Login> loginusers { get; set; }
public DbSet<User> allusers { get; set; }
}
}
Then i created a LoginController.cs that has a register function in which i try to pass in two different tables of my database the elements that i take from formcollection as you can see below. The problem is that in my database are passing only in the table login and not in the userdata.
[HttpPost]
public ActionResult Registration(FormCollection forms){
var db = new TheTradersContext();
var crypto = new SimpleCrypto.PBKDF2();
var p = forms["password"].ToString();
String encryptPass = crypto.Compute(p);
var newUser = db.loginusers.Create();
var nuser = db.allusers.Create();
newUser.email = forms["email"].ToString();
newUser.password = encryptPass;
newUser.status = "user";
newUser.salt = crypto.Salt;
//nuser.ID=Convert.ToInt32("18");
nuser.surname = forms["lastname"].ToString();
nuser.name = forms["firstname"].ToString();
nuser.sex = forms["gender"].ToString();
nuser.city = forms["city"].ToString();
nuser.address = forms["addr"].ToString();
nuser.zipcode =forms["zip"].ToString();
nuser.tel = "fdgfdgf".ToString();
nuser.country = forms["country"].ToString();
nuser.bdate ="dsafdsaf".ToString();
try
{
db.loginusers.Add(newUser);
db.SaveChanges();
var useri = db.loginusers.Single(u => u.email == newUser.email);
if (useri == null) {
throw new Exception();
}
nuser.ID = Convert.ToInt32(useri.ID);
db.allusers.Add(nuser);
db.SaveChanges();
}
catch (Exception x)
{
ModelState.AddModelError("", "This username is in use");
}
return View();
}
My table in database has exactly the same names of the fields on user.
Of course I tried to exclude the code that has to do with the login and pass only the values of the userdata in database but i saw the exception : System.Data.Entity.Infrastructure.DbUpdateException.
I have tried a lot of things until now... any idea?
from your code it seems that there is a relation 1 to many between Login and User, each login has 1 or more user ( i figured this out since you were trying to put nuser.ID = Convert.ToInt32(useri.ID);)
in your class User put a navigational property called public Login Login{get; set;}
and the User class should has a primary key let us say ( UserId) unless you marked ID as primary key then your User table is a weak entity.
and mark the ForeignKey attribute for the new property Login as ForeignKey("ID")
after doing this, then easily you can do the following
var login=new Login();
// fill out the login data
db.loginusers.Add(login)
db.allusers.Add(new User(){
surname = forms["lastname"].ToString(),
name = forms["firstname"].ToString(),
sex = forms["gender"].ToString(),
city = forms["city"].ToString(),
address = forms["addr"].ToString(),
zipcode =forms["zip"].ToString(),
tel = "fdgfdgf".ToString(),
country = forms["country"].ToString(),
bdate ="dsafdsaf".ToString(),
Login=login
});
db.SaveChanges();
hope that his will help you
note: your classes design can be improved and normalized to reflect the database relationship

Categories