I am trying to input data using a loop. In the first loop it is able to succesfully input the data. In the second loop when it comes to saving the data it comes up with an error message
An exception of type 'System.InvalidOperationException' occurred in Microsoft.EntityFrameworkCore.dll
but was not handled in user code:
'The property 'userTask.TaskScheduleId' is part of a key and so cannot be modified or marked as modified.
To change the principal of an existing
entity with an identifying foreign key, first delete the dependent and invoke 'SaveChanges', and then
associate the dependent with the new principal.'
Here is the code below.
It creates a copy of the data in taskSchedule and then copies it to all the other users.
taskSchedule table has a many to many relationship with the users table which is why it is also saving data in the userTask table.
foreach(int user in users){
TaskSchedule copyTaskSchedule = new TaskSchedule();
copyTaskSchedule = taskSchedule;
copyTaskSchedule.Id = 0;
copyTaskSchedule.Notes = null;
copyTaskSchedule.UserTasks = null;
_context.TaskSchedules.Add(copyTaskSchedule);
_context.SaveChanges(); // this is where the code stops on the 2nd loop
// add updated user to the task
userTask copyUserTask = new userTask();
copyUserTask.TaskScheduleId = copyTaskSchedule.Id;
copyUserTask.UserId = user;
_context.userTasks.Add(copyUserTask);
_context.SaveChanges();
}
TaskSchedule model
public class TaskSchedule
{
public int Id { get; set; }
public string Title { get; set; }
public DateTime? Start { get; set; }
public DateTime? End { get; set; }
public bool isClosed { get; set; }
public bool isDeleted { get; set; }
public byte priorityLevel { get; set; }
public bool hasTimeLimit { get; set; }
public Customer customer { get; set; }
public int? customerId { get; set; }
public List<Note> Notes { get; set; }
public List<AttachmentFile> Attachments { get; set; }
public List<userTask> UserTasks {get; set;}
public int? userLastEditId { get; set; }
[ForeignKey("userLastEditId")]
public User userLastEdit { get; set; }
public DateTime? userLastEditDate { get; set; }
public DateTime taskCreatedDate { get; set; }
}
looks like copyTaskSchedule.Id is a primary key or a foreign key . You are initializing the value to copyTaskSchedule.Id = 0;
I think you must change this code
copyUserTask.TaskScheduleId = copyTaskSchedule.Id;
to this
copyUserTask.TaskSchedule = copyTaskSchedule;
I removed the code below
copyTaskSchedule = taskSchedule;
Then replaced it with the following code below. I manually added each field from taskSchedule to copyTaskSchedule
copyTaskSchedule.Title = taskSchedule.Title;
copyTaskSchedule.Start = taskSchedule.Start;
copyTaskSchedule.End = taskSchedule.End;
copyTaskSchedule.userLastEditId = taskSchedule.userLastEditId;
copyTaskSchedule.priorityLevel = taskSchedule.priorityLevel;
copyTaskSchedule.isClosed = taskSchedule.isClosed;
copyTaskSchedule.hasTimeLimit = taskSchedule.hasTimeLimit;
copyTaskSchedule.Attachments = taskSchedule.Attachments;
copyTaskSchedule.customerId = taskSchedule.customerId;
copyTaskSchedule.userLastEditDate = NowDateTime;
copyTaskSchedule.isDeleted = taskSchedule.isDeleted;
copyTaskSchedule.taskCreatedDate = NowDateTime;
Related
When trying to create a new database entry of type TestForm2 I include the related object Unit Type's ID as a foreign key, except when I perform context.SaveChanges() after adding the new model I get the following SQL exception:
SqlException: Violation of PRIMARY KEY constraint 'PK_dbo.UnitTypes'. Cannot insert duplicate key in object 'dbo.UnitTypes'. The duplicate key value is (2d911331-6083-4bba-a3ad-e50341a7b128). The statement has been terminated.
What this means to me is that it thinks that the foreign entry I'm trying to relate to the new model is instead a new object that it's attempting to insert into the UnitTypes table and failing because it sees an existing entry with the same primary key.
For context (pun not intended), this is my data context, the database model, and the erroring "Create" function.
public class DataContext : IdentityDbContext<ApplicationUser>
{
public DataContext() : base("DefaultConnection")
{
}
public static DataContext Create()
{
return new DataContext();
}
public DbSet<SafetyIncident> SafetyIncidents { get; set; }
public DbSet<ProductionLine> ProductionLines { get; set; }
public DbSet<ProductionOrder> ProductionOrders { get; set; }
public DbSet<SerialOrder> SerialOrder { get; set; }
public DbSet<QualityError> QualityErrors { get; set; }
public DbSet<PSA> PSAs { get; set; }
public DbSet<TestStation> TestStations { get; set; }
public DbSet<ProductionGoal> ProductionGoals { get; set; }
public DbSet<DailyWorkStationCheck> DailyWorkStationChecks { get; set; }
public DbSet<TestForm> TestForms { get; set; }
public DbSet<User> AppUsers { get; set; }
public DbSet<Options> Options { get; set; }
public DbSet<DriveList> DriveSerials { get; set; }
public DbSet<MRPController> MRPControllers { get; set; }
public DbSet<TestOption> TestOptions { get; set; }
public DbSet<UnitType> UnitTypes { get; set; }
public DbSet<UnitTypeMap> UnitTypeMaps { get; set; }
public DbSet<TestForm2> TestForm2s { get; set; }
public DbSet<TestFormSection> TestFormSections { get; set; }
public DbSet<TestFormSectionStep> TestFormSectionSteps { get; set; }
}
public class TestForm2 : BaseEntity
{
public string SerialNumber { get; set; }
public string MaterialNumber { get; set; }
public string UnitTypeId { get; set; }
public UnitType UnitType { get; set; }
public bool UsesStandardOptions { get; set; }
public bool OptionsVerified { get; set; } // This will only be used when UsesStandardOptions is true, otherwise its value doesn't matter
public ICollection<TestOption> AllOptions { get; set; } // List of all options (at time of form creation)
public ICollection<TestOption> Options { get; set; } // The options on a unit
public ICollection<TestFormSection> Sections { get; set; }
}
public FormViewModel Create(FormViewModel vm)
{
using (var context = new DataContext())
{
List<string> optionListStrings = GetOptionListForModelNumber(vm.MaterialNumber); // returns list of option codes
List<TestOption> matchingOptions = context.TestOptions
.Where(optionInDb =>
optionListStrings.Any(trimOption => trimOption == optionInDb.OptionCode)).ToList();
var unitType = context.UnitTypes.FirstOrDefault(x => x.Name == vm.UnitType);
string unitTypeId = unitType.Id;
TestForm2 newForm = new TestForm2
{
// ID & CreatedAt instantiated by Base Entity constructor
SerialNumber = vm.SerialNumber,
MaterialNumber = vm.MaterialNumber,
UnitTypeId = unitType.Id,
UsesStandardOptions = vm.UsesStandardOptions,
OptionsVerified = vm.OptionsVerified,
//AllOptions = context.TestOptions.ToList(),
//Options = matchingOptions,
Sections = vm.Sections,
};
context.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
context.TestForm2s.Add(newForm);
context.SaveChanges(); // THIS IS WHERE THE SQL EXCEPTION IS HAPPENING
return vm;
}
return null;
}
Lastly, I'm not sure if it's relevant, but a full copy of the related UnitType is viewable as part of newForm only after context.TestForm2s.add(newForm) resolves. This is weird to me since I don't think it should be automatically relating the data object like that.
I haven't been able to try much since everything looks properly configured to me. Please let me know if this is not the case or if I should include any other info.
Found the issue. The vm.Sections was not using viewmodels to contain the section data, so the vm.Sections contained UnitType database models. Since this was instantiated in the controller (before opening the data context in the TestForm2 Create method) EF assumed that these data were new and needed to be added to the UnitType table.
Hope this thread helps someone else running into similar issues.
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#.
I am using Entity Framework code first with fluent API I have an items table with foreign keys from users and units tables
but when I load the table to ObservableCollection then bind it to a datagrid the table normal column load it's data normally into the datagrid excpet for the foreign keys which show nothing but when i insert a break point to see the data inside the ObservableCollection I can see that every thing from Users and Units table is there
private void MainContentsWindow_ContentRendered(object sender, EventArgs e)
{
using (var db2 = new DataContext())
{
var AllItems2 = new ObservableCollection<Model.Items.Item>(db2.Items);
ItemsDataGrid.ItemsSource = AllItems2;
}
}
Users
public class User
{
public User()
{
Id = Guid.NewGuid();
IsActive = false;
}
public Guid Id { get; set; }
public string Name { get; set; }
public string Password { get; set; }
public UserGroup Group { get; set; }
public bool IsActive { get; set; }
public virtual ICollection<Items.Item> Items { get; set; } = new List<Items.Item>();
}
public enum UserGroup
{
Administrator = 1,
User,
Unknown
}
base
public class NormalBaseModel : CommonBase
{
public NormalBaseModel()
{
Id = new Guid();
CreateDate = DateTime.Now;
EditDate = null;
}
public Guid Id { get; set; }
public string Notes { get; set; }
public virtual User CreateBy { get; set; }
public DateTimeOffset? CreateDate { get; set; }
public virtual User EditBy { get; set; }
public DateTimeOffset? EditDate { get; set; }
}
items
public class Item : NormalBaseModel
{
public string NameAr { get; set; }
public string NameEn { get; set; }
public int? ManualId { get; set; }
public string Barcode { get; set; }
public byte?[] Image { get; set; }
public virtual Unit Unit { get; set; }
public string MadeIn { get; set; }
public bool IsSerail { get; set; }
public bool IsExpire{ get; set; }
}
Here is a test project on Github
https://github.com/ahmedpiosol/psychic-parakeet.git
https://imgur.com/a/zimd4
When you load your items via EF it needs to create new instances of User and Item. Behind the scenes, EF will call the constructor for each new instance. Your problem is in your constructors:
public User()
{
Id = Guid.NewGuid(); // <- here
}
Your constructor reassigns a new ID each time an instance is created, this will break the referential integrity and cause all sorts of other problems.
Your code doesn't know the difference between creating a new User and recreating a User instance from the database.
I suggest removing the assignments from inside your constructor and placing this either in a static Create method or place wherever you are creating a new User or Item.
p.s. WPF is irrelevant to your problem here.
Fluent API needs to specify foreign key in code, something like
modelBuilder.Entity<Items>()
.HasRequired(o => o.User)
.WithMany(c => c.Items)
.HasForeignKey(o => o.UserId);
I'm having some troubles getting EntityFramework to do what I want. I have an object and I want to keep track of the current and previous state. So when there is an update the previous state will than be changed to the current state and the current state becomes a new object like this:
using (var db = new DBContext())
{
var currentPair = await db.CurrencyPairs.Include(c => c.CurrentRate).Include(c => c.PreviousRate).SingleAsync(p => p.CurrencyPairId == pair.CurrencyPairId);
var newRate = new ExchangeRate()
{
CurrencyPairId = currentPair.CurrencyPairId,
HighestBid = t.HighestBid,
LowestAsk = t.LowestAsk,
Last = t.LastPrice,
Volume = t.DailyVolume,
UpdateTime = DateTime.UtcNow
};
if (currentPair.AveragePrice == null || (DateTime.UtcNow - DateTime.Parse(db.Store.Single(s => s.Key == "CurrentStartTime").Value)).TotalHours < 4)
currentPair.AveragePrice = (t.DailyHigh + t.DailyLow) / 2;
currentPair.PreviousRate = currentPair.CurrentRate;
currentPair.CurrentRate = newRate;
await db.SaveChangesAsync();
}
The problem is that I'm getting an EntityFramework error when saving:
Store update, insert, or delete statement affected an unexpected
number of rows (0). Entities may have been modified or deleted since
entities were loaded.
The ExchangeRate object has an identity column so the ID should get filled automatically. This thread is the only one that is setting the CurrentRate/PreviousRate properties, there are other threads that read them.
Anyone has a clue as to what I'm doing wrong?
EDIT: ExchangeRate object
public class ExchangeRate
{
[Key]
public long ExhangeRateId { get; set; }
public double HighestBid { get; set; }
public double LowestAsk { get; set; }
public double Volume { get; set; }
public double Last { get; set; }
public DateTime UpdateTime { get; set; }
public long CurrencyPairId { get; set; }
}
You forget to add reference property to CurrencyPair. One foreign key id is not enough.
public class ExchangeRate
{
[Key]
public long ExhangeRateId { get; set; }
public double HighestBid { get; set; }
public double LowestAsk { get; set; }
public double Volume { get; set; }
public double Last { get; set; }
public DateTime UpdateTime { get; set; }
public long CurrencyPairId { get; set; }
public virtual CurrencyPair CurrencyPair { get; set; }
}
Here is some docs article
It's been quite a while since I last used EF. I've never had any problems using it before. Now I'm attempting to insert an object that has a one-many relationship with another object. But in the API call, the collection array of the child object is shown to be empty however the parent object can be seen in the api call of the child object.
I have my models as below:
Conversation Table
public class Conversation
{
public Conversation()
{
this.ChatMessages = new List<ChatMessage>();
this.DeletedConversations = new List<ConversationDeleted>();
}
public int ConversationID { get; set; }
public string toUser { get; set; }
public string FromUser { get; set; }
[InverseProperty("Conversation")]
public ICollection<ChatMessage> ChatMessages { get; set; }
public ICollection<ConversationDeleted> DeletedConversations { get; set; }
public DateTime CreatedAt { get; set; }
public int UserID { get; set; }
}
ChatMessage Table
public class ChatMessage
{
public int ChatMessageID { get; set; }
public string fromUser { get; set; }
public string toUser { get; set; }
public string Message { get; set; }
public bool DeliveryStatus { get; set; }
public DateTime CreatedAt { get; set; }
public Guid UniqueID { get; set; }
public int ConversationID { get; set; }
[ForeignKey("ConversationID")]
public virtual Conversation Conversation { get; set; }
public ICollection<MessageDeleted> MessagesDeleted { get; set; }
public int UserId { get; set; }
}
My Fluent API looks like this:
modelBuilder.Entity<ChatMessage>()
.HasRequired(x => x.Conversation)
.WithMany(x => x.ChatMessages)
.HasForeignKey(x => x.ConversationID);
I'm trying to create a conversation entity and add a chat object to it's collection. I do it like so:
public IHttpActionResult CreateConversation()
{
ChatMessage msg = new ChatMessage { CreatedAt = DateTime.UtcNow, DeliveryStatus = true, fromUser = "annettehiggs", toUser = "terrydriscoll", Message = "Hum tum", UniqueID = Guid.NewGuid(), UserId = 43 };
Conversation conv = new Conversation();
conv.ChatMessages.Add(msg);
conv.CreatedAt = DateTime.UtcNow;
conv.FromUser = "annettehiggs";
conv.toUser = "terrydriscoll";
DataModel db = new DataModel();
db.Conversations.Add(conv);
db.SaveChanges();
return Ok(conv);
}
and this is how I retrieve the conversation object:
public IQueryable<Conversation> GetConversations()
{
return db.Conversations;
}
As a result, ChatMessage API call shows the conversation it's associated to but the Conversation object doesn't show the chat in it's collection. What am I doing wrong here?
The add code is working properly (otherwice you'll not be able to see the new chat message). The problem is with your data retrieval code.
Since your ChatMessage.Conversation property is marked as virtual, most probably it gets lazy loaded, that's why you see it populated.
At the same time, your Conversation.ChatMessages is not virtual, hence you need to explicitly eager load it using the Inlclude method, or depending on your requirements, mark it virtual to get the lazy load behavior like the inverse navigation property.