Entity framework model generating weird error message - c#

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.

Related

Can't insert row with two FK to database in .NET using Oracle

I can't insert row into database. I'm using .NET with Oracle. I can insert rows to another tables but I can't insert to m:n table, that has two FKeys.
public static String SQL_INSERT = "INSERT INTO UserBet(bet_id, ticket_id) VALUES(:bet_id, :ticket_id)";
public static int Insert(int ticket_id, int bet_id, OracleDB pDb = null)
{
OracleDB db;
if (pDb == null)
{
db = new OracleDB();
db.Connect();
}
else
{
db = pDb;
}
OracleCommand command = db.CreateCommand(SQL_INSERT);
command.Parameters.Add(":bet_id", 1);
command.Parameters.Add(":ticket_id", 1);
int ret = db.ExecuteNonQuery(command);
if (pDb == null)
{
db.Close();
}
return ret;
}
The table UserBet is:
CREATE TABLE userbet
(
bet_id INTEGER NOT NULL,
ticket_id INTEGER NOT NULL
);
ALTER TABLE userbet
ADD CONSTRAINT userbet_pk
PRIMARY KEY (bet_id, ticket_id);
ALTER TABLE userbet
ADD CONSTRAINT userbet_bet_fk
FOREIGN KEY (bet_id) REFERENCES bet (bet_id);
ALTER TABLE userbet
ADD CONSTRAINT userbet_ticket_fk
FOREIGN KEY (ticket_id) REFERENCES ticket (ticket_id);
This doesn't work and I got no error messages. How I said, other inserts are working just this doesn't. It's M:N table. Any hint please?
EDIT: It turns out that the problem was caused by the constraints.
If there is a table t1 and another table t2, where t1.key is a foreign key referencing t2, then, to protect data integrity, Oracle enforces the foreign key as a constraint, that is, you cannot insert into a table a record having a foreign key pointing to a not existent record. You cannot update a foreign key to a value that does not exist. You cannot delete a record from the main table without handling this in some manner in the reference table(s). You can define cascade as the on delete action for instance.
From the above, an exception is null. If your field is nullable, then it can be null, even though that's certainly not a value for the primary key in the referenced table. It's logical though, since null is the lack of a value and therefore it's not a reference.

Using LINQ to insert data into a table that uses a sequence as primary key generator

I have a table which generates its primary key from a sequence (that just counts up from 0):
CREATE TABLE [dbo].[testTable](
[id] [int] NOT NULL,
[a] [int] NOT NULL,
CONSTRAINT [PK_testTable] PRIMARY KEY CLUSTERED ([id] ASC))
ALTER TABLE [dbo].[tblTestTable] ADD CONSTRAINT [DF_tblTestTable_id] DEFAULT (NEXT VALUE FOR [seq_PK_tblTestTable]) FOR [id]
I've used Visual Studio's O/R Designer to create the mapping files for the table; the id field is defined as:
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_id", DbType="Int NOT NULL", IsPrimaryKey=true)]
public int id {…}
and now I'm trying to insert data via LINQ.
var testTableRecord = new testTable()
{
a = 1,
};
db.Connection.Open();
db.testTables.InsertOnSubmit(testTableRecord);
db.SubmitChanges();
Console.WriteLine($"id: {testTableRecord.id}");
The problem I'm encountering is, that LINQ seems unable to handle the id generation via sequence as it sets the id implicitly to 0 when inserting.
When I set the id to CanBeNull, the insert fails because it tries to insert NULL into a non-nullable field.
When I set the id to IsDbGenerated, the insert works but it expects an IDENTITY field and tries to load the generated id with SELECT CONVERT(Int,SCOPE_IDENTITY()) AS [value]',N'#p0 int',#p0=1 and than sets the id in the object to null because SCOPE_IDENTITY() returns null…
I've been thinking about just using IsDbGenerated, destroying the LINQ object and querying the DB for the id, but I don't have anything unique to search for.
Unfortunately changing the id creation mechanism to IDENTITY is not an option.
Do I have to explicitly query the DB for the next sequence value and set the id manually?
Whats the best way to handle these inserts via LINQ?
PS: I need the id because I have to insert more data that uses the id as FK.
Looking at solutions from the raw sql perspective:
1.
INSERT INTO [dbo].[testTable] VALUES (NEXT VALUE FOR [dbo].[seq_PK_tblTestTable], 1)
Simply can't be done in LINQ to SQL as far as I can tell
2.
INSERT INTO [dbo].[testTable] (a) VALUES (1)
This can be achieved in LINQ to SQL by excluding the id property from the testTable entity.
If you need to retrieve ids from the table, you could create separate entities for inserting and querying:
public class testTableInsert {
[ColumnAttribute(...)]
public int a
}
public class testTableResult {
[ColumnAttribute(...)]
public int id
[ColumnAttribute(...)]
public int a
}
3.
DECLARE #nextId INT;
SELECT #nextId = NEXT VALUE FOR [dbo].[seq_PK_tblTestTable];
INSERT INTO [dbo].[testTable] VALUES (#nextId, 1)
As you mentioned, this can be essentially achieved by manually requesting the next id before each insert. If you go this route there are multiple ways to achieve it in your code, you can consider stored procedures or use the LINQ data context to manually execute the sql to retrieve the next sequence value.
Here's a code sample demonstrating how to extend the generated DataContext using partial methods.
public partial class MyDataContext : System.Data.Linq.DataContext
{
partial void InsertTestTable(TestTable instance)
{
using (var cmd = Connection.CreateCommand())
{
cmd.CommandText = "SELECT NEXT VALUE FOR [dbo].[seq_PK_TestTable] as NextId";
cmd.Transaction = Transaction;
int nextId = (int) cmd.ExecuteScalar();
instance.id = nextId;
ExecuteDynamicInsert(instance);
}
}
}
Once the above is implemented, you can safely insert entities like this, and they will generate the correct sequence id.
TestTable entity = new TestTable { a = 2 };
dataContext.TestTables.InsertOnSubmit(entity);
dataContext.SubmitChanges();
Your only hope is a pretty profound refactoring and use a stored procedure to insert records. The stored procedure can be mapped to the class's Insert method in the data context designer.
Using your table definition, the stored is nothing but this:
CREATE PROCEDURE InsertTestTable
(
#id int OUTPUT,
#a AS int
)
AS
BEGIN
INSERT dbo.testTable (a) VALUES (#a);
SET #id = (SELECT CONVERT(int, current_value)
FROM sys.sequences WHERE name = 'seq_PK_tblTestTable')
END
You can import this stored procedure into the context by dragging it from the Sql Object Explorer onto the designer surface, which will then look like this:
The next step is to click the testTable class and click the ellipses button for the Insert method (which got enabled by adding the stored procedure to the context):
And customize it as follows:
That's all. Now LINQ-to-SQL will generate a stored procedure call to insert a record, for example:
declare #p3 int
set #p3=8
declare #p5 int
set #p5=0
exec sp_executesql N'EXEC #RETURN_VALUE = [dbo].[InsertTestTable] #id = #p0 OUTPUT,
#a = #p1',N'#p0 int output,#p1 int,#RETURN_VALUE int output',
#p0=#p3 output,#p1=123,#RETURN_VALUE=#p5 output
select #p3, #p5
Of course you may have to wonder how long you're going to hang on to LINQ-to-SQL. Entity Framework Core has sequence support out of the box. And much more.

Any other optimal way to update the parent table's columns when a child table is updated?

I have a few table structure look as below:
CREATE TABLE Person
(
PersonID INT PRIMARY KEY,
Name NVARCHAR(255),
LastUpdatedBy INT,
LastUpdatedDate DATETIME
);
CREATE TABLE Info
(
InfoID INT PRIMARY KEY,
PersonID INT,
Info NVARCHAR(255),
LastUpdatedBy INT,
LastUpdatedDate DATETIME
);
CREATE TABLE Setting
(
SettingID INT PRIMARY KEY,
PersonID INT,
Setting NVARCHAR(255),
LastUpdatedBy INT,
LastUpdatedDate DATETIME
);
I face a new procedure to follow that if there is any updates on Info or Setting table, I will need to do relevant updates to Person table on columns LastUpdatedBy and LastUpdatedDate.
What first come to my mind is to create a SQL trigger that automatically update Person table when Info or Setting table does. But take a quick glance through for a few articles stating that a SQL trigger should be avoided as it's an very expensive process when creating it,
While some people recommends to change in application code. For an example,
using (var db = new DbContext())
{
var result = db.Info.SingleOrDefault(x => x.InfoID == infoID);
if (result != null)
{
result.Info = "Some new value";
result.LastUpdatedBy = userID;
result.LastUpdatedDate = DateTime.UtcNow;
db.SaveChanges();
}
}
need to change and become like this.
using (var db = new DbContext())
{
var result = db.Info.SingleOrDefault(x => x.InfoID == infoID);
if (result != null)
{
result.Info = "Some new value";
result.LastUpdatedBy = userID;
result.LastUpdatedDate = DateTime.UtcNow;
var person = db.Person.SingleOrDefault(x => x.PersonID == result.PersonID);
if (person != null)
{
person.LastUpdatedBy = result.LastUpdatedBy;
person.LastUpdatedDate = result.LastUpdatedDate;
}
db.SaveChanges();
}
}
in reality, the application code is massive, a lot of code modification need to be made.
Assume there are 30+ tables, and each of them contain at least 100k of records. If creating of triggers are possible, it will be as the following:
CREATE TRIGGER TriggerName ON dbo.Info
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
UPDATE dbo.Person
SET LastUpdatedBy = INSERTED.LastUpdatedBy ,
LastUpdatedDate = INSERTED.LastUpdatedDate
FROM INSERTED
WHERE dbo.Person.PersonID = INSERTED.PersonID
END
GO
Is the SQL trigger should really be avoided in this scenario? Please explain based on your answer if can. Any alternative solution is welcome, performance first.
Trigger is optimal (from a performance perspective) here; it's simply like running an update statement on a bunch of rows from the front end code. I don't see why you think there is a performance penalty. Your trigger code should look more like this though:
CREATE TRIGGER TriggerName ON dbo.Info
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
UPDATE dbo.Person
SET LastUpdatedBy = INSERTED.LastUpdatedBy ,
LastUpdatedDate = INSERTED.LastUpdatedDate
FROM dbo.Person
INNER JOIN
INSERTED
ON dbo.Person.PersonID = INSERTED.PersonID
END
GO
There are other ways, such as making a Stored procedure that updates all tables in a transaction, or updating front end data access layer (if your front end has a lot to update, it implies it is structured wrong: one place should have responsibility for writing to this table. If your front end code has update statements peppered all through it, well.. that's a bad design) so a dedicated class maintains these two tables properly..
Right now I'd say a trigger is your easiest way out of the problem.. they aren't well liked, though not because of performance, but because they start to add confusing consequences.. imagine you as a c# developer with limited database experience, didn't know what a trigger was, and you're complaining "every time I update just this one table, all these other 27 tables change by magic! What's going on? Am I going crazy or what?" - triggers break rules like "keep all your data updating code in one place" and this is why people who engineer systems where specific parts have specific jobs, don't like them

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...

How do I change a primary key value in SubSonic 3.0?

Is it possible to update a primary key after it's been created in SubSonic 3.0??
Consider this MySql table:
create table items (
Type varchar(30) NOT NULL,
Attribute varchar(30) NOT NULL PRIMARY KEY);
And I am trying to edit an item object (so called row)
item i = item.SingleOrDefault(i => i.Attribute == "123");
i.Attribute = "234";
i.Save();
The above snippet throws a syntax error MySQLException. After profiling the same, the query that was being executed at back end was this:
UPDATE ITEMS WHERE ATTRIBUTE="123";
Any suggestions/help please.

Categories

Resources