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
Related
I have a Cinema Model:
public class Cinema
{
public int Id { get; set; }
[Required]
[StringLength(255)]
public string Name { get; set; }
[Required]
public string Address { get; set; }
[Required]
[Range(0, int.MaxValue, ErrorMessage = "Please enter valid number")]
[Display(Name = "Total Seats")]
public int TotalSeatsNumber { get; set; }
public List<Seat>TotalSeats { get; set; }
public OpeningHour OpeningHour { get; set; }
[Required]
[Display(Name = "Opens At")]
public byte OpeningHourId { get; set; }
public ClosingHour ClosingHour { get; set; }
[Required]
[Display(Name = "Closes At")]
public byte ClosingHourId { get; set; }
public Cinema() { }
I have a TotalSeatsNumber property, so when the admin fills a form (Inside the website) to create a new cinema, he has to specify how many seats the cinema should contain.
I've also created a List of Seats called TotalsSeats, which later I try to initialize with seats according to the number of seats the admin chose. You can see what I'm trying to do here:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Save(Cinema cinema)
{
if (!ModelState.IsValid)
{
var viewModel = new CinemaFormViewModel(cinema)
{
OpeningHours = _context.OpeningHours.ToList(),
ClosingHours = _context.ClosingHours.ToList()
};
return View("CinemaForm", viewModel);
}
if (cinema.Id == 0)
{
cinema.TotalSeats = SetSeats(cinema.TotalSeatsNumber);
_context.Cinemas.Add(cinema);
}
else
{
var cinemaInDb = _context.Cinemas.Single(c => c.Id == cinema.Id);
cinemaInDb.Name = cinema.Name;
cinemaInDb.Address = cinema.Address;
cinemaInDb.TotalSeatsNumber = cinema.TotalSeatsNumber;
cinemaInDb.TotalSeats = cinema.TotalSeats;
cinemaInDb.OpeningHourId = cinema.OpeningHourId;
cinemaInDb.ClosingHourId = cinema.ClosingHourId;
}
_context.SaveChanges();
return RedirectToAction("Index", "Cinemas");
}
The SetSeats function returns a list of Seats where I initialize their Id, location, and availability. Just in case, I will add my Seat Model and SetSeats function here:
public class Seat
{
public int Id { get; set; }
[Required]
public string Location { get; set; }
[Required]
public bool isAvailable { get; set; }
public Seat()
{
isAvailable = true;
}
}
public List<Seat> SetSeats(int totalSeatsNumber)
{
List<Seat> totalSeats = new List<Seat>();
char rowLetter = 'a';
int seatNumInRow = 1;
for (int i = 1; i <= totalSeatsNumber; i++, seatNumInRow++)
{
totalSeats.Add(new Seat() { Id = i, Location = rowLetter + ("" + seatNumInRow), isAvailable = true });
if ((i % 10) == 0)
{
rowLetter++;
seatNumInRow = 0;
}
}
return totalSeats;
}
The reason I'm trying to do this is that I want that the user will be able to choose a specific seat when he orders tickets for a movie in a certain cinema.
The problem is when I try to SaveChanges(), it throws me an exception:
System.Data.Entity.Infrastructure.DbUpdateException: 'An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details.'
When debugging, I can see my "cinema" instance is updated properly, exactly like I wanted. But it fails when trying to save it to the DB.
your Seat class doesnt have any relations with Cinema class, but you are trying to add a list , so add a foreign key CinemaId
public class Seat
{
public int Id { get; set; }
......
public int CinemaId { get; set; }
public virtual Cinema Cinema {get; set;}
}
you will have to migrate to db after changing
You have to migrate first to make sure the database scheme go async with your DbModels in C#.
So I have 2 classes here:
public class User
{
[Key]
public int UserId { get; set; }
public string Username { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<Message> Messages { get; set; }
}
and
public class Message
{
[Key]
public int MessageId { get; set; }
public string MessageContent { get; set; }
public DateTime DateCreated { get; set; }
public int SenderId { get; set; }
public int ReceiverId { get; set; }
}
I am trying to create a message via the following controller action, which creates a new message and adds it to the database, and then update the ICollectionMessages inside the database.
Controller here:
[Route("sendMessage")]
[HttpPost]
public async Task SendMessage([FromBody] Message message)
{
var newMessage = new Message
{
MessageContent = message.MessageContent,
DateCreated = DateTime.Now,
SenderId = message.SenderId,
ReceiverId = message.ReceiverId
};
_dbContext.Messages.Add(newMessage);
var user = _dbContext.Users.SingleOrDefault(x => x.UserId == message.SenderId);
if(user != null)
{
var userMessages = user.Messages.ToList();
userMessages.Add(newMessage);
}
await _dbContext.SaveChangesAsync();
}
however I am getting a 'Object reference not set to an instance of an object.' error and I believe this to be because 'var userMessages = user.Messages.ToList();' is NULL and therefore cannot add a value to a null list.
Having a massive brainfog, can someone suggest a way around this?
edit:
my goal is to make USERID update:
You add a new message to a copy of the messages collection
var userMessages = user.Messages.ToList()
The above statement creates a brand new list for the data, so I would call it like
user.Messages.Add(newMessage);
Change:
public class User
{
[Key]
public int UserId { get; set; }
public string Username { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<Message> Messages { get; set; } = new Collection<Message>();
}
so collection is always present,
...
var user = _dbContext.Users.Include(u=>u.Messages).SingleOrDefault(x => x.UserId == message.SenderId);
if(user != null)
{
var userMessages = user.Messages;
userMessages.Add(newMessage);
}
await _dbContext.SaveChangesAsync();
include messages, when finding user and use collection directly when adding new.
I think that you add a message to a copy of the messages collection (because of ToList), so try to do user.Messages.Add(newMessage)
user.Messages.Add(newMessage)
fixed by E. Shcherbo
thanks!
Try doing like this
//your code...
var userMessages = user.Messages;
userMessages.Add(newMessage);
//your code...
public class UserObject
{
public ObjectId _id { get; set; }// Self generated
public ulong UserID { get; set; } // Self generated
public string Username { get; set; }
public string CharClass{ get; set; }
public int CharLevel { get; set; }
public int CharColour { get; set; }
}
I want to update just the Username, CharClass, CharLevel & CharColour.
I currently have this:
var client = new MongoClient(DBString);
var database = client.GetDatabase("UserLists");
var collection = database.GetCollection<UserObject>(Convert.ToString(GuildId));
var filter = Builders<UserObject>.Filter.Eq(s => s.Username, newUserName);
var UpdatedUserObject = new UserObject
{
UserID = UserId,
Username = newUserName,
CharClass = newCharClass,
CharLevel = newCharLevel,
CharColour = newCharColour
};
collection.ReplaceOneAsync(filter, UpdatedUserObject);
If you have any suggestions that would be much appreciated, i've got the add working i just can't seem to get the update, thanks!
I ended up doing:
var client = new MongoClient(DBString);
var database = client.GetDatabase("UserLists");
var collection = database.GetCollection<UserObject>(Convert.ToString(GuildId));
var filter = Builders<UserObject>.Filter.Eq(s => s.Username, newUserName);
var UpdatedUserObject = new UserObject
{
UserID = UserId,
Username = newUserName,
CharClass = newCharClass,
CharLevel = newCharLevel,
CharColour = newCharColour
};
collection.ReplaceOne(filter, UpdatedUserObject);
Which then gave me the problem that it was trying to update the _id so i used this stack overflow to put [BsonIgnoreIfDefault] on it so it didn't try and update:
Unable to use BsonIgnoreIfDefault for property of type long
public class UserObject
{
[BsonIgnoreIfDefault]
public ObjectId _id { get; set; }// Self generated
public ulong UserID { get; set; } // Self generated
public string Username { get; set; }
public string CharClass{ get; set; }
public int CharLevel { get; set; }
public int CharColour { get; set; }
}
After this was fixed i found that the problem was that I was passing through the wrong username so it never matched & therefore didn't find anything to update, just me being stupid really, Thanks for the help
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);
}
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