Insert multiple tables cause IDs to be null in Entity Framework - c#

C#, Entity Framework, MSSQL
Table is defined as the following:
id uniqueidentifier (not null, PK, default (newid()) )
createdAt datetimeoffset(3)
updatedAt datetimeoffset(3)
version timestamp
deleted bit
Image image
I do have a many to one relationship assigned between the two tables. on tblArea.fkImages -> tblImages.id
If I insert a record by using this query the newid() appears to work and it assigns a random unique identifier.
insert into tblimages (image) values (null)
However, if I use the following C# code:
Guid gRestID = Guid.Parse(tbxSelectedLoc.Text);
tblImages newImage;
tblArea newArea = new tblArea
{
Name = tbxAddMenu.Text,
fkLoc = gRestID,
};
if (pbxLoc.Image != null)
{
newImage = new tblImages
{
Image = Helpers.ImageToByte(pbxLoc.Image),
};
db.tblImages.Add(newImage);
newArea.tblImages = newImage;
}
db.tblArea.Add(newArea);
db.SaveChanges();
Both record's ID's are 0's in tblArea and in tblImages which then prevents any subsequent additions to the two tables (constraints on the pk).
Anyone know why this is happening?

Related

Insert Unique Primary Key into Database using Entity-Framework LINQ

I want to enter unique index value into my database when insert other table data. In this case my table index column ID is unique but not auto-increment.
I also want to check the maximum of index value in target table. If the table is empty then index value starts from 0, OR if it contains 1 row then the index value starts from 2.
Already I can successfully do this operation using a stored procedure. But I want to do this operation using Entity Framework when saving data.
My stored procedure is:
ALTER PROCEDURE [dbo].[CreatePerson]
#Name Nvarchar(50),
#Code Nvarchar(50)
AS
BEGIN
Declare #Id int
SET #Id = ISNULL(((SELECT MAX([id]) FROM dbo.tbl_Person)+1),'1')
Insert Into dbo.tbl_Person([Id], [Name], [Code])
Values (#id, #Name, #Code)
END
Can anyone tell me what the LINQ Command for this index value save process?
A straight translation of your SQL stored procedure into Linq would basically be something like this:
var newId = ctx.Persons.Any() ? ctx.Persons.Select(p => p.Id).Max() + 1 : 1;
var newPerson = new Person { Id = newId, Name = someName, Code = someCode };
ctx.Persons.Add(newPerson);
ctx.SaveChanges();
Note: this solution is certainly NOT recommended for inserting unique id's in a database, consider carefully the suggestions of Sharped and Magnus in the comments below the question to better use auto-increment or random guids to solve your problem.
With an auto-incremented id column your code would look like this:
var newPerson = new Person { Name = someName, Code = someCode };
ctx.Persons.Add(newPerson);
ctx.SaveChanges();
var newId = newPerson.Id;
Note that EF will update the id column of the newly created entity for you in code automagically.

Ask for the ID (Identity) that created their own to add a new record in an SQL table. Silverlight

I need help, I have a SQL table (Order) has a field ID int Identity, and another table (OrderDetail) where one of his fields is this ID
The Order table struct is:
ID (PK, int, Identity, Not Null)
Service (char(10), Not Null)
TypeReposition (char(10), Null)
And the OrderDetail table struct is:
IDOrder (PK, FK, int, Not Null) <- this must be equal to corresponding ID in Order
Status (PK, char(25), Not Null)
StatusDate (PK, datetime, Not Null)
to insert a new record in the Order table, I make this:
Order newOrder = new Order();
newOrder.Service = ((TypeService)cbTypeService.SelectedItem).Service;
newOrder.TypeReposition = null;
OrderDomainDataSource.DataView.Add(newOrder);
OrderDomainDataSource.DomainContext.SubmitChanges(so =>
{
if (so.HasError)
{
//Handle errors from submit
so.MarkErrorAsHandled();
}
if (OrderDomainDataSource.CanLoad)
OrderDomainDataSource.Load();
}, null);
The ID is generated automatically when the insert is done.
Immediately after this, I need insert the corresponding OrderDetail record, but for that I need to know the ID generated for the Order record, how I know this??
I'm using Silverlight 5 with WCF Ria Services
I think I found a solution:
newOrderDetail.IDOrder = ((Order)OrderDomainDataSource.DataView.CurrentItem).ID;
Because OrderDomainDataSource.DataView.CurrentItem, after SubmitChanges, points to the Order record that I added previously

Violation of primary key constraints, Cannot insert a duplicate key - In new empty tables

I know this is a common issue and I've read about it here and elsewhere but in most cases the problem is that a row already exists in the database (which I'm checking for...). In my case it's a new db with empty tables (well.. except for Publishers table that has one row in Id = 0), the code goes like this (Entity Framework V5):
public void InsertPublisherResult(PublisherResult pResult)
{
using (mydbEntities e = new mydbEntities())
{
if (e.Publishers.Any(c => c.Name == pResult._Publisher.Name))
continue;
e.Publishers.Add(scraperResult._Publisher);
foreach (RelatedPublisher rp in pResult._RelatedPublishers)
{
e.RelatedPublishers.Add(rp);
}
foreach (Download d in pResult._Downloads)
{
e.Downloads.Add(d);
}
foreach (PublisherDomain pd in pResult._PublisherDomains)
{
e.PublisherDomains.Add(pd);
}
e.SaveChanges();
}
}
The rest of the tables (RelatedPublishers, Downloads and PublisherDomains) are empty, because they have non of the mentioned objects in the first pResult, with Primary Key - Id which is the defined entity key.
The first pResult is skipped because it exists, and the second one throws the exception on the PK violation.
Could it be that I'm trying to insert the 2nd pResult in the first row (id=0 on Publishers)? and if not, then what am I doing wrong?
Thanks to the commenter that asked me if Id is auto incremented I checked it and got to the answer:
The column wasn't auto incremented and I had to drop the column or in my case I dropped the table and created it again using the correct SQL statement:
CREATE TABLE [mydb].[dbo].[Publishers]
(
Id Integer IDENTITY(1,1) primary key,
PublisherGuid uniqueidentifier,
Name nvarchar(100),
Link nvarchar(100)
);
After that I Updated the Model from Database and it worked! That comment saved me lots of time...

Entity framework model generating weird error message

I have a PostgreSql 9.04 database that contains a postgres implementation of the ASPNet Membership Services database. This implementation lives in the same database as my own tables, but in a different schema called "security".
For business reasons, I have included the aspnet_Users, aspnet_Membership, and aspnet_Profiles table in my entity model. Below is the DDL for these tables.
CREATE TABLE security.aspnet_users (
userid UUID NOT NULL,
applicationname VARCHAR(255) NOT NULL,
username VARCHAR(255),
lastactivitydate TIMESTAMP,
isanonymous INT,
CONSTRAINT aspnet_users_userid_pk PRIMARY KEY (userid),
CONSTRAINT aspnet_users_username_pk UNIQUE (username, applicationname)
);
CREATE TABLE security.aspnet_membership (
userid UUID NOT NULL,
password VARCHAR(255),
passwordsalt VARCHAR(255),
passwordformat INT,
email VARCHAR(255),
passwordquestion VARCHAR(255),
passwordanswer VARCHAR(255),
comments VARCHAR(255),
isapproved INT,
islockedout INT,
creationdate TIMESTAMP,
lastlogindate TIMESTAMP,
lastpasswordchangeddate TIMESTAMP,
lastlockoutdate TIMESTAMP,
failedpasswordattemptcount INT,
failedpasswordattemptstart TIMESTAMP,
failedpasswordanswercount INT,
failedpasswordanswerstart TIMESTAMP,
CONSTRAINT aspnet_membership_userid_pk PRIMARY KEY (userid),
CONSTRAINT aspnet_membership_userid_ref FOREIGN KEY (userid) REFERENCES security.aspnet_users (userid)
);
CREATE TABLE security.aspnet_profiles (
userid UUID,
propertynames BYTEA NOT NULL,
propertyvaluesstring BYTEA NOT NULL,
propertyvaluesbinary BYTEA NULL,
lastupdateddate TIMESTAMP NOT NULL,
CONSTRAINT aspnet_profiles_userid_ref FOREIGN KEY (userid) REFERENCES security.aspnet_users (userid),
CONSTRAINT aspnet_profiles_userid_key UNIQUE (userid)
);
Again, these are the only tables from this schema that are in my model.
Now, when I insert a row into these tables, and I call SaveChanges, I get the following error message:
Unable to determine the principal end of the 'Membership_Users' relationship. Multiple added entities may have the same primary key.
Below is a sample of code that I'm using to insert a new or update an existing row. This is representative of everything I'm doing regarding updating the database.
public static void SaveUserProfile( CarSystemEntities context, UserProfileData data ) {
if ( context == null )
throw new ArgumentNullException( "context", "You must pass a non-null CarSystemEntities instance." );
if ( data == null )
throw new ArgumentNullException( "data", "You must pass a non-null UserProfileData instance." );
try {
Profile profile = null;
if ( !context.Profiles.Any( p => p.Userid == data.ID ) ) {
profile = new CarSystem.Profile{
LastUpdatedDate = data.LastUpdatedDate.LocalDateTime,
PropertyNames = data.PropertyNames,
PropertyValuesBinary = data.PropertyValuesBinary,
PropertyValuesString = data.PropertyValuesString,
Userid = data.ID
};
context.Profiles.AddObject( profile );
} else {
profile = QueryUerProfiles( context ).Where( p => p.Userid == data.ID ).Single();
if ( profile.LastUpdatedDate != data.LastUpdatedDate ) profile.LastUpdatedDate = data.LastUpdatedDate.LocalDateTime;
if ( profile.PropertyNames != data.PropertyNames ) profile.PropertyNames = data.PropertyNames;
if ( profile.PropertyValuesBinary != data.PropertyValuesBinary ) profile.PropertyValuesBinary = data.PropertyValuesBinary;
if ( profile.PropertyValuesString != data.PropertyValuesString ) profile.PropertyValuesString = data.PropertyValuesString;
}
} catch ( Exception ex ) {
throw new DataAccessException( DataAccessOperation.SaveProfile, FailureReason.DatabaseError, ex );
}
}
What is the cause of this error? How do I fix this?
Thanks
Tony
P.S.
Upon further investigation, the problem isn't when inserting a row, it's when trying to update a row in the same transaction as the one that inserted the row.
Essentially, there's a queue of objects to be written to the database. Each object is removed from the queue in turn and a method like the one above is called to save it to the proper table.
The first time a specific ID is seen, the method inserts it. That is, the Any check returns false. The code creates the new entity object and calls the AddObject method for the entity set that corresponds to the table. So far so good.
After that, it is assumed that the Any check will return true, indicating that there is a row with the given ID in it already. It is then supposed to update the row. But it appears that the Any check is returning false the second time, too, even though I called AddObject on the Entity set that corresponds to the table on the first call.
Any idea what I am doing wrong?
PPS
I've done some more testing with a Sql monitor open. We are using the Devart dotConnect for PostgreSql library and they have a tool you can download called DbMonitor. This allows you to track the SQL that is emitted & executed by the Entity Framework.
It turns out (predictably, as it happens) that the calls to .Any are executed as queries against the database immediately, while all of the inserts are queued up and applied once you call SaveChanges. The Any calls do not seem to take any rows that are pending inserting into account, just what the database calls return. As the rows to be inserted haven't been inserted yet, this query will always return false until SaveChanges is called.
As a result, my logic as it stands won't work. When a row that is pending insertion is updated (that is, it appears a second time in the queue), The insert logic would run a second time. Sometimes I'd get an exception indicating a unique or primary key constraint was being violated, other times I'd get the message I originally posted about.
I need all of the inserts and updates to be done in a single transaction, and I need the inserts to happen in the database at the time of the call to AddObject. Yet I still need all of the inserts & updates to rollback if something goes wrong inserting or updating a single row, or if some other error occurs in the C# code.
Anybody have any ideas how I can do this with the Entity Framework?
It doesn't look like the primary key is being set on the Profile object you are adding to the db. The default value for a Guid is Guid.Empty, so the first one gets saved but each subsequent object fails.
the code below sets the primary key
if ( !context.Profiles.Any( p => p.Userid == data.ID ) ) {
profile = new CarSystem.Profile{
LastUpdatedDate = data.LastUpdatedDate.LocalDateTime,
PropertyNames = data.PropertyNames,
PropertyValuesBinary = data.PropertyValuesBinary,
PropertyValuesString = data.PropertyValuesString,
Userid = data.ID
//add the next line to ensure there is a pk field
ID = Guid.NewGuid()
};
I've managed to resolve this issue the way I want it to work.
The cause of all of my problems at first was that the entity framework waits until you call SaveChanges to write any changes to the database. But when you execute a .Any() call, the SQL is generated and executed immediately, ignoring any changes that are pending being written to the database. So my code always thought it had to insert new rows even though one was a genuine insert and the other was supposed to be an update.
Next I tried using a TransactionScope object, calling SaveChanges() after inserting or updating each object. Then I'd call the TransactionScope instance's Completed() method to "commit" the changes. Well, low & behold, this ends up working exactly the same was waiting to call SaveChanges! Nothing gets written to the database until you call scope.Completed()!
The solution I found that works is shown in the code below:
using ( CarSystemEntities context = new CarSystemEntities() ) {
if ( context.Connection.State != ConnectionState.Open ) {
context.Connection.Open();
}
DbTransaction transaction = context.Connection.BeginTransaction();
try {
<Data processing code here>
transaction.Commit();
} catch ( Exception ex ) {
transaction.Rollback();
<More exception code here as needed>
}
}
Combined with calling SaveChanges after each context..AddObject or property update, everything works exactly as I need it to work. The INSERT & Update statements are generated & executed when SaveChanges is called. The calls to .Any also take into account any rows that were inserted into the table by a previous iteration. Everything is inside one real database transaction that can be committed or rolled back as a whole.

How to set an FK column value without retrieve it

i want to set a new value to my entity objects FK column, but i cant find the property to set. I dont want to get record from db.
I have a db like that
Db Tables:
Concept ConceptType
-Id (PK) -Id(PK)
-Name -Name
-ConceptTypeId(FK) (ALLOW NULL)
Code:
Concept conceptToUpdate = new Concept() { Id = 1 };
ConceptType conceptType = new ConceptType() { Id = 5 };
db.AttachTo("Concept", conceptToUpdate);
db.AttachTo("ConceptType", conceptType);
conceptToUpdate.ConceptType = conceptType;
db.SaveChanges();
This code is working if ConceptTypeId(FK) column is NULL before. If it is not NULL it gives exception. I trace the sql query, the problem is on sql query because it is checking that old value is NULL :S
SQL QUERY: (from SQL Profiler)
exec sp_executesql N'update [dbo].[Concept]
set [ConceptTypeId] = #0
where (([Id] = #1) and [ConceptTypeId] is null)
',N'#0 int,#1 int',#0=5,#1=1
The reason it fails is that in 3.5 SP1 is that for relationships the old FK value is part of the concurrency check, as you found via SQL profiler.
What you need is something like this:
Concept conceptToUpdate = new Concept() {
Id = 1 ,
ConceptType = new ConceptType {Id = OriginalFKValue}
};
ConceptType conceptType = new ConceptType() { Id = 5 };
db.AttachTo("Concept", conceptToUpdate);
db.AttachTo("ConceptType", conceptType);
conceptToUpdate.ConceptType = conceptType;
db.SaveChanges();
This means you need to know not just the Id of the thing you want to update, but also it's original FK values, which is of course a real pain.
Hence a new feature in EF 4.0 called FK Associations. With FK Associations the original value of the FK is not part of the concurrency check.
Hope this helps
Alex

Categories

Resources