I'm trying to do something simple, but this dont work in EF 4.1,it does in EF 6, but I cant update version because I need this application in an old server and doesnt support it.
This is my code:
chat = new Chat()
{
AdminId = 99
};
db.Chat.Add(chat);
db.SaveChanges();
var cp = new List<ChatPeople>();
foreach (int user in usersids)
{
cp.Add(new ChatPeople
{
ChatId = chat.Id,
UserId = user
});
}
cp.ForEach(c => db.ChatPeople.Add(c));
and this is the model:
[Table("CHATS", Schema = "SCHEMA")]
public class Chat
{
[Column("ID")]
public decimal Id { get; set; }
[Column("ADMINID")]
public decimal AdminId { get; set; }
public virtual ICollection<ChatPeople> ChatPeople { get; set; }
public virtual ICollection<ChatHistory> ChatHistory { get; set; }
}
(I need decimal Id because int dont work in Oracle.)
My problem is when I save Chat, this dont return the current Id of this registry in database and when in ChatPeople try to do this ChatId = chat.Id, I dont have the value. In DB Chat are saving ok with AdminId = 99 and Id = (Autonumeric-Identity)
What can I do to get Id of registry that are being saved?
Try adding the attribute [Key] above your ID This should set it as the primary key for the table that's generated by EF.
[Key]
[Column("ID")]
public decimal Id { get; set; }
I need put this other attribute [DatabaseGenerated(DatabaseGeneratedOption.Identity)] and works ok.
Answer in this post:
Entity Framework Code First Using One column as Primary Key and another as Auto Increment Column
Finally:
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column("ID")]
public decimal Id { get; set; }
Related
I tried to describe the problem I'm facing in Entity Framework Core as simply as possible to make it easier to understand. If you can help me - thank you. I'm having nightmares with this problem.
I have 2 tables in the database, Users and UserProfile, defined by these classes:
[PrimaryKey(nameof(Id))]
public class User
{
public int Id { set; get; }
public string Password { set; get; }
public UserProfile UserProfile { get; set; }
}
[PrimaryKey(nameof(UserId))]
public class UserProfile
{
public int UserId { set; get; }
public string Name { set; get; }
}
When adding a User, I would like to add the information about him in the other table UserProfile in the same SaveAsync() call, so as not to have the possibility of a table being created and another not.
Code that I imagine should work:
User user = new()
{
Id = 1,
Password = "123",
UserProfile = new()
{
UserId = 1,
Name = "Hello World"
}
};
await context.Users.AddAsync(user);
await context.SaveChangesAsync();
I've tried dozens (or hundreds) of different ways to add data to both tables at the same time and I get multiple errors. How do I add at the same time? Thanks!
Entity layout contains int value of venue (VenueId prop), its own id and other information.
CONSTRAINT [FK_Venue_Layout] FOREIGN KEY ([VenueId]) REFERENCES [dbo].[Venue] ([Id])
When I trying to add two layouts with the same VenueId, I'm getting this error
The changes to the database were committed successfully, but an error occurred while updating the object context. The ObjectContext might be in an inconsistent state. Inner exception message: Saving or accepting changes failed because more than one entity of type 'DataAccess.Models.LayoutModel' have the same primary key value. Ensure that explicitly set primary key values are unique. Ensure that database-generated primary keys are configured correctly in the database and in the Entity Framework model. Use the Entity Designer for Database First/Model First configuration. Use the 'HasDatabaseGeneratedOption" fluent API or 'DatabaseGeneratedAttribute' for Code First configuration."
My entity code:
[Table("Layout")]
public class LayoutModel
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int Id { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int VenueId { get; set; }
public string Description { get; set; }
}
Insertion code:
var layouts = new List<LayoutModel>
{
new LayoutModel { VenueId = 1, Description = "First layout" },
new LayoutModel { VenueId = 1, Description = "Second layout" },
};
_context.Layouts.AddRange(layouts);
_context.SaveChanges();
I'm not allowed to use navigation properties
Id column or property is marked as identity column in the definition of LayoutViewModel
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int Id { get; set; }
So, no need to assign it explicitly as it will be populated by Database automatically after the row is inserted into Layout table. Please update your layouts population as below to remove Id assignment:
var layouts = new List<LayoutModel> {
new LayoutModel { /*Id = 1,*/ VenueId = 1, Description = "First layout" },
new LayoutModel { /*Id = 2, */ VenueId = 1, Description = "Second layout" }
};
// code smell
foreach(var layout in layouts)
{
context.Entry(layout).State = EntityState.Added;
}
_context.Layouts.AddRange(layouts);
_context.SaveChanges();
Also, please update your LayoutModel as below:
public class LayoutModel
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
[Column(Order = 0)]
public int Id { get; set; }
[Key]
[Column(Order = 1)]
//[ForeignKey("Venue")]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int VenueId { get; set; }
//public virtual VenueModel Venue { get; set; } //Please correct Venue property type
}
Also, please verify whether Venue is loaded into _context.Layouts or not.
I'm seeing a strange behavior from Entity Framework. I'm using code-first approach to define a One-To-Many relationship between two entities:
public class IncomingCheck : AuditedEntityBase
{
[Key]
public int Id { get; set; }
[Required]
public virtual CheckType Type { get; set; }
[Required]
public virtual Bank Bank { get; set; }
public string Branch { get; set; }
public virtual IList<IncomingCheckHistory> History { get; set; }
}
public class IncomingCheckHistory
{
[Key]
public int Id { get; set; }
public string LongDescription { get; set; }
}
And here's I I'm trying to add an item to the History:
using (var db = new CheckDataContext())
{
foreach (var check in SelectedItems)
{
var dbCheck = await db.IncomingChecks.FindAsync(check.Id);
var history = new IncomingCheckHistory()
{
LongDescription = "something",
};
dbCheck.History.Add(history);
await db.SaveChangesAsync(); //throws the exception
}
}
But it throws an exception saying that "Cannot insert the value NULL into column 'Id'". However I've always done it like this. The database is supposed to fill the Id column itself with a unique number.
What am I missing?
Update:
Using SSProfiler, I got the query that runs on the database. It's as follows:
exec sp_executesql N'INSERT [dbo].[IncomingCheckHistories]([LongDescription], [IncomingCheck_Id])
VALUES (#0, #1)
SELECT [Id]
FROM [dbo].[IncomingCheckHistories]
WHERE ##ROWCOUNT > 0 AND [Id] = scope_identity()',N'#0 nvarchar(max) ,#1 int',#0=N'Something',#1=1
Note that scope_identity() should be getting the Id itself, correct?
Shouldn't the Id be set as the Identity?
Like this:
public class IncomingCheckHistory
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Key]
public string LongDescription { get; set; }
}
Hope that this is the solution to your problem!
Well for some strange reason the exact code worked after completely deleting the database and migrations and starting from scratch. I'm not sure why it wouldn't work without migrations. Also I had tried deleting the database before but at the time I was trying a little different code.
In any case the posted code/query should work. Also I checked the database and the columns to see if anything is different and it wasn't. I would appreciate if someone could shed some light on why it wasn't working and it is now.
I am trying to write to two tables in my database in a function that takes in lists as a parameter from the previous page. I call the db to retrieve the purchase_order_no because the column is an IDENTITY primary key that is generated on entry.
Models:
purchase_order
[Key]
[Column(Order = 0)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int client_no { get; set; }
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int purchase_order_no { get; set; }
[StringLength(60)]
public string name { get; set; }
[Key]
[Column(Order = 2)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int order_group_no { get; set; }
[StringLength(24)]
public string purchase_order_reference { get; set; }
[Key]
[Column(Order = 3)]
public DateTime order_timestamp { get; set; }
order_detail
[Key]
[Column(Order = 0)]
public long order_detail_no { get; set; }
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int purchase_order_no { get; set; }
[Key]
[Column(Order = 2)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int inventory_no { get; set; }
[Key]
[Column(Order = 3)]
public decimal quantity { get; set; }
public int? vendor_no { get; set; }
I receive this error when trying to insert my new 'purchase_order' model into the db:
Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=472540 for information on understanding and handling optimistic concurrency exceptions.
[HttpPost]
public ActionResult orderForm (List<int> quantity, List<string> itemName, List<int> inventory_no, List<int> client_no, List<int> vendorCode, int orderGroupNo)
{
using (var db = new db_model())
{
var ctx = ((IObjectContextAdapter)db).ObjectContext;
purchaseOrderVM poVM = new purchaseOrderVM();
List<order_detail> tempList = new List<order_detail>();
purchase_order po = new purchase_order();
po.client_no = client_no[0];
var purchaseOrder = db.purchase_order.Where(x => x.client_no == po.client_no).Max(x => x.purchase_order_no);
po.order_group_no = orderGroupNo;
po.order_timestamp = DateTime.Now;
db.purchase_order.Add(po);
try
{
db.SaveChanges(); <!-- This is where I get the error -->
}
catch (OptimisticConcurrencyException e)
{
ctx.Refresh(RefreshMode.ClientWins, db.purchase_order);
throw e;
}
for (int i = 0; i < itemName.Count(); i++)
{
order_detail od = new order_detail();
od.purchase_order_no = db.purchase_order.Where(x => x.client_no == po.client_no).Max(x => x.purchase_order_no);
od.inventory_no = inventory_no[i];
od.quantity = quantity[i];
od.vendor_no = vendorCode[i];
db.order_detail.Add(od);
try
{
db.SaveChanges();
}
catch (OptimisticConcurrencyException e)
{
ctx.Refresh(RefreshMode.ClientWins, db.order_detail);
throw e;
}
tempList.Add(od);
}
poVM.purchase_order = po;
poVM.orderList = tempList;
return View(poVM);
}
}
I think the problem is in your model class. In purchase_order, why do you have so many columns flagged as [Key]? As purchase_order_no is declared as an identity column, it should be the primary key by itself. I don't even know why EF doesn't complain at startup, because your configuration makes no sense.
Remove the [Key] attribute on all other columns, and it should work. If you need to ensure uniqueness, you can create a unique index on the other columns.
I will not question your model as to why you have so many columns flagged as [Key] in the first place. That's not helping you with your problem. Moreover, you might want all those columns in your primary key for performance reasons: when using code first with EF, the primary key is created as clustered by default.
The problem is probably not the number of columns with attribute [Key]. I had the same problem after incorporating just one extra column of type DateTime in the primary key, and you also have a DateTime column in your primary key combination for entity purchase_order.
It looks like EF is not handling DateTime columns very well, when marked as part of the primary key. I found an interesting webpage that confirms this: https://social.msdn.microsoft.com/Forums/en-US/6f545446-aa26-4a21-83e9-60f7fa5ed3b0/optimisticconcurrencyexception-when-trying-to-insert-a-new-record-in-table-using-entity-framework?forum=adodotnetentityframework
I can think of two solutions for your problem:
If you want to stick with your primary key definition: truncate your [order_timestamp] to a value your database does accept. In case of SQL Server you'll be doing fine when truncating to 10 msec, C#:
order_timestamp = DateTime.Now.Truncate(TimeSpan.FromMilliseconds(10));
If you don't want to truncate your timestamp but accept to change your PK combination to an index: consider adding an autonumber column for the primary key:
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int Id { get; set; }
and changing the other key attributes to:
[Index("IX_purchase_order", Order = 1)],
[Index("IX_purchase_order", Order = 2)], etc.
For performance, you might want the index to be created as clustered:
[Index("IX_purchase_order", IsClustered = true, Order = 1)], etc.
But when using Migrations, bear in mind that unfortunately EF will still try to create your PK as clustered as well. This would give errors when issuing command update-database, you first will have to change your migration code a little. Although in the code you will find something like:
.Index(t => new{client_no, ..., order_timestamp}, clustered: true, name: "IX_purchase_order")
you explicitly must declare the primary key non-clustered, by changing the migration code for the PK part to:
.PrimaryKey(t => t.Id, clustered: false)
I hope this helps.
Sorry for my bad English.
I have EF 6 codefirst models for database.
3 models is usually static, loaded at program startup (using aka foreign keys for last table).
Last model is dynamical - data is loaded too, stored in c# collection, but user can add, edit rows and save added/edited to DB.
For 3 first models i have checkboxes with selecteditem binding.
User can edit last table model entity, select items from checkboxes and save to DB.
This is simple and standart solution.
Partial models without trash fields (to reference from last table).
[Table("Users")]
public partial class User
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ClientId { get; set; }
[StringLength(160)]
public string ClientName { get; set; }
public virtual ICollection<Repair> Repairs { get; set; }
public User()
{
Repairs = new List<Repair>();
}
}
[Table("RepairStatuses")]
public partial class RepairStatus
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[StringLength(10)]
public string Status { get; set; }
public virtual ICollection<Repair> Repairs { get; set; }
public RepairStatus()
{
Repairs = new List<Repair>();
}
}
[Table("CurrentStatuses")]
public partial class CurrentStatus
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int StatusId { get; set; }
[StringLength(10)]
public string Status { get; set; }
public virtual ICollection<Repair> Repairs { get; set; }
public CurrentStatus()
{
Repairs = new List<Repair>();
}
}
And main editable table model (partial too w/o trash fields).
[Table("Repairs")]
public partial class Repair
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
[Column(TypeName = "date")]
public DateTime Date { get; set; }
[StringLength(255)]
public string HardwareInfo { get; set; }
public virtual User User { get; set; }
public virtual RepairStatus RepairStatus { get; set; }
public virtual CurrentStatus CurrentStatus { get; set; }
}
In my AddEntity method all working (attach unchanged items from combobox to DbContext, add new row, save changes). Eager loading.
using (ServiceDBContext cntx = new ServiceDBContext())
{
cntx.Users.Attach(SelectedRepair.User);
cntx.CurrentStatuses.Attach(SelectedRepair.CurrentStatus);
cntx.RepairStatuses.Attach(SelectedRepair.RepairStatus);
cntx.Entry(SelectedRepair.RepairStatus).State = EntityState.Modified;
cntx.Entry(SelectedRepair.CurrentStatus).State = EntityState.Modified;
cntx.Entry(SelectedRepair.User).State = EntityState.Modified;
cntx.Repairs.Attach(SelectedRepair);
cntx.Entry(SelectedRepair).State = EntityState.Added;
...
cntx.SaveChanges();
...
But with EditEntity method i have strange behavior (sorry for stupid code...)
using (ServiceDBContext wrk = new ServiceDBContext())
{
var tmp = (((((wrk.Repairs.Where(x => x.Id ==SelectedRepair.Id)).Include(y => y.CurrentStatus)).Include(y => y.RepairStatus)).Include(y => y.Engineer)).Include(y => y.User)).FirstOrDefault();
if (tmp.User.ClientId != SelectedRepair.User.ClientId)
{
tmp.User = SelectedRepair.User;
wrk.Users.Attach(tmp.User);
wrk.Entry(tmp.User).State = EntityState.Modified;
}
if (tmp.RepairStatus.Id != SelectedRepair.RepairStatus.Id)
{
tmp.RepairStatus = SelectedRepair.RepairStatus;
wrk.RepairStatuses.Attach(tmp.RepairStatus);
wrk.Entry(tmp.RepairStatus).State = EntityState.Modified;
}
if (tmp.CurrentStatus.StatusId != SelectedRepair.CurrentStatus.StatusId)
{
tmp.CurrentStatus = SelectedRepair.CurrentStatus;
wrk.CurrentStatuses.Attach(tmp.CurrentStatus);
wrk.Entry(tmp.CurrentStatus).State = EntityState.Modified;
}
...
wrk.Entry(tmp).State = EntityState.Modified;
wrk.SaveChanges();
}
For example: CurrentStatuses table have 2 entities ("1. OK", "2. Bad").
Then user first time changing in Repair table in selected row CurrentStatus foreign key (for example, with id =1 to foreign key with id=2) all is OK.
In VS debugger i can see...
UPDATE [dbo].[CurrentStatuses] SET [Status] = #0 WHERE ([StatusId] = #1)
UPDATE [dbo].[Repairs] SET ... WHERE (([Id] = #12) AND ([CurrentStatus_StatusId] = #13))
If user want to change second time this entity from id=2 to id=1 (reverse) its throwing error "An error occurred while saving entities that do not expose foreign key properties for their relationships..."
AND in debugger we can see some magic with "Reader (INSERT)" attempts to all database relative tables o_O and attempt to INSERT in Repair table dublicate entry (which was been selected to edit).
One INSERT example (Repair, RepairStatus and User have like this INSERTS too):
DECLARE #0 AS SQL_VARIANT;
SET #0 = NULL;
INSERT [dbo].[CurrentStatuses]([Status])
VALUES (#0)
SELECT [StatusId]
FROM [dbo].[CurrentStatuses]
WHERE ##ROWCOUNT > 0 AND [StatusId] = scope_identity()
After program restart we can change CurrentStatus foreign key from id=2 to id=1 normally (but only 1 time too).
Can someone help me to solve this problem?
Thanks!