In C#, SQLite randomly throws "Constraint failed\r\nColumn ... is not unique" - c#

I'm using EntityObjects in C# and my backend is SQLite. System information:
Windows XP (up-to-date), VS2010 Premium, System.Data.SQLite 1.0.88.0 (3.7.17)
The table schema is:
CREATE TABLE [transaction] (
[primary_key_col] integer PRIMARY KEY AUTOINCREMENT,
[transaction_code] int NOT NULL,
[account_code] int NOT NULL,
[category_code] int NOT NULL,
[transaction_date] date NOT NULL,
[description] varchar(50));
public partial class Transaction : EntityObject represents this table.
To add a new Transaction, I call the following code:
Transaction transaction = new Transaction()
{
description = _desc,
transaction_code = generateHash(_desc),
account_code = _acccode,
category_code = _catcode,
transaction_date = DateTime.Now
};
dbEntities.Transactions.AddObject(transaction);
dbEntities.SaveChanges(System.Data.Objects.SaveOptions.AcceptAllChangesAfterSave);
Works fine but after adding a few rows, I get the following exception:
Constraint failed\r\nColumn account_code is not unique.
If I exit the application and restart it, this error goes away but then randomly comes back again.
I know account_code is not unique, I never asked for it to be unique!!!
There are only 3 specific ways the application can create a transaction and this exception is thrown rather randomly from any of these methods. Is this something to do with SQLite? (I'm new to SQLite). Any idea why this is happening? I've nearly pulled out all my hair ...

As per comment which seems to have done the trick:
Are you using a "new" dbEntities context every time you do an insert or are you re-using the same one for multiple inserts? It shouldn't matter either way but I would try the former if you are currently using the latter.

Related

Add a non existing Primary Key in a SQL server table

I want to insert data in a table that has integers as primary keys. The primary keys will be added by a wide range of random numbers. First I want to check if the primary key exists and then insert it.
Here is my code:
public static void Insert()
{
bool a=true;
while (a != false)
{
try
{
db.test.Add(new test() //insert into test table new data
{
Id = rand.Next(1, 10),
name = "test",
surname = "test"
});
db.SaveChanges();
a=false;
}
catch (Exception)
{
Console.WriteLine("Duplicate");
}
}
}
In the first iteration if the number is not duplicate I can successfully insert the new row in database. If in the first iteration it is duplicate entry after db.SaveChanges();i jump directly in catch. But the problem arises when in the second iteration the random number is not duplicate it still jumps to catch exception without executing the a=false line of code.
Any Idea?
Note: I am not interested on using GUIDs or any other data type.
I might have an easier solution for you if you don't NEED it to be a random number.
CREATE TABLE Persons (
ID int IDENTITY(1,1) PRIMARY KEY,
Surname varchar(255) NOT NULL,
Name varchar(255)
);
INSERT INTO Persons (Name,Surname)
VALUES ('Lars','Monsen');
This way you don't even need to do anything with the PRIMARY KEY, SQL Server will handle it for you!
The issue here is that once a duplicate key has been generated, it will fail to insert, so it will keep trying to insert it each time; to quote OP:
Yes exactly, when it detects a duplicate it keeps trying it even though the next number may not be a duplicate.
There are 3 main ways of solving this:
don't generate your own keys; sorry, but this is the simplest, most obvious, and most supportable mechanism here; IDENTITY is your friend
if you fail to insert something : remove that same something from your ORM context so you don't keep failing; there is presumably a db.test.Remove method that will suffice; you may also be able to just change the value in the last row, abusing the fact that you know it was the last row that failed: db.test.Last().Id = newId; - this is very odd though; everything gets weird if you change identity keys
or alternatively, don't keep re-using the data context after failure; assuming that db is now unusable after the exception, and create a new one for any subsequent operations; IMO this should be done by the same code that originally created the db-context, in a "redo from start" way

Is it possible to get Identity Field value before saving it in entity framework

I have a customer and sales table
CUSTOMER
--------------
Id (int auto increment)
Name
SALES
---------------
Id (int auto increment)
CustomerId (int)
OrderTotal (decimal)
With Guid i can do this.
dbTransaction = dbContext.Database.BeginTransaction(isolationLevel);
var customer = new Customer()
{
Id = Guid.NewGuid(),
Name = "John Doe"
};
var sales = new Sales()
{
Id = Guid.NewGuid(),
CustomerId = customer.Id,
OrderTotal = 500
};
dbContext.SaveChanges();
dbTransaction.Commit();
How can i do this if my primary key is int (with DatabaseGeneratedOption.Identity)?
You cannot. The ID that goes into a IDENTITY column is generated by the database upon insertion, and all "tricks" to circumvent that and determine the ID yourself are probably flawed.
Short answer: If you want some say in generating an ID before you save, use a GUID (UNIQUEIDENTIFIER), or a SEQUENCE (if you're working with SQL Server 2012 or newer).
Why you should not compute the next free ID yourself:
Don't even consider running a query such as context.Customers.Max(c => c.Id) + 1 as a viable solution, because there's always the possibility that you have concurrent database accesses: another process or thread might persist a new entity to the same table after you've read the next "free" ID but before you store your entity. Computing the next free ID will be prone to collisions, unless your whole operation of getting the ID, doing something with it, and storing the entity with that ID were atomic. This would likely require a table lock in the DB, which might be inefficient.
(The same problem exists even when you use SEQUENCEs, a new feature introduced in SQL Server 2012.) (I was wrong; see end of answer.)
Possible solutions:
If you need to determine the ID of an object before you save it, then don't use the ID that goes in a IDENTITY column. Stay with a GUID, because you're extremely unlikely to get any collision with these.
There's no need to chose between one or the other: you can actually have your cake and eat it! Nothing stops you from having two ID columns, one that you determine externally (the GUID) and one that stays internal to the DB (the IDENTITY column); see the blog article "CQS vs. server generated IDs" by Mark Seemann for a more detailed look at this idea. Here's the general idea by example:
CREATE TABLE Foos
(
FooId INT IDENTITY NOT NULL PRIMARY KEY CLUSTERED,
-- ^^^^^ assigned by the DBMS upon insertion. Mostly for DB-internal use.
Id UNIQUEIDENTIFIER ROWGUIDCOL NOT NULL UNIQUE DEFAULT (NEWID()),
-- ^^ can be dictated and seen by the users of your DB. Mostly for DB-external use.
…
);
CREATE TABLE FooBars
(
FooId INT NOT NULL FOREIGN KEY REFERENCES Foos (FooId),
-- use DB-internal ID in foreign key constraints ^^^^^
…
);
CREATE VIEW PublicFoos AS
SELECT Id, … FROM Foos;
-- ^^ publish the public ID for users of your DB
(Make sure you adhere to some convention for consistently naming internal and public ID field names.)
SEQUENCEs, a feature introduced in SQL Server 2012, are a possible alternative to having an IDENTITY column. They are automatically increased and you are guaranteed a unique number when getting the next free ID using NEXT VALUE FOR SomeSequence. One of the use cases mentioned on MSDN are:
Use sequences instead of identity columns in the following scenarios: […] The application requires a number before the insert into the table is made.
Some caveats:
Getting the next sequence value will require an additional roundtrip to the database.
Like identity columns, sequences can be reset / re-seeded, so there is the theoretical possibility of ID collisions. Best to never re-seed identity columns and sequences if you can help it.
If you fetch the next free sequence value using NEXT VALUE FOR, but then decide not to use it, this will result in a "gap" in your IDs. Gaps obviously cannot happen with regular (non-sequential) GUIDs because there is no inherent ordering to them.
As far as I know you can not get the ID before saving the changes in the database. The database creates the ID after the values are inserted in the database.
To add to it when you call .SaveChanges() then only it will write the changes to the database and only then the identity value will get generated.
You can get that value by a small hack.
Create a function in SQL Server something like this
CREATE FUNCTION fn_getIdentity(#tbl_name varchar(30))
AS
BEGIN
IF #tbl_name = 'Employee_tbl'
RETURN IDENT_CURRENT('Employee_tbl')
ELSE IF #tbl_name = 'Department_tbl'
RETURN IDENT_CURRENT('Department_tbl')
ELSE
RETURN NULL
END
Create an entity in your Entity framework to support this function and use it wherever you want.
Then use
var nextValue = dbContext.fn_getIdentity("Employee_tbl")
IDENT_CURRENT returns you last incremented value for an identity column. This doesn't mean MAX + 1 as if your previous transaction generated an identity value for this column but was rolled back then you will see next value that will be generated.
Please note, I didn't check syntax properly and this syntax is just to present an idea.
However I would go with solution provided by Stakx i.e. SEQUENCE if using SQL Server 2012 or above
else creating a table to implement functionality of SEQUENCE by reserving ID once generated permanently in a table.
We can indeed if your ID is in integer, using SQL. The following example is for PostreSQL, please feel free to adapt it for other servers and to edit this answer.
Create a virtual entity model for the database to wrap our query result and to have a fake DbSet<some virtual model> to use ef core extension method FromSqlRaw.
Define a virtual model:
public class IntReturn
{
public int Value { get; set; }
}
Now fake a DbSet<IntReturn> it will not be really created on server:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
...
modelBuilder.Entity<IntReturn>().HasNoKey();
base.OnModelCreating(modelBuilder);
}
Now we can get the currently used Id for Customers table in this example. The calling method is inside a Subclassed : DbContext, you'd what to instantiate your context to use it instead of this:
public int GetNextCustomerId()
{
//gets current id, need do +1 to get the next one
var sql = "Select last_value as Value FROM \"Customers_Id_seq\";";
var i = this.Set<IntReturn>()
.FromSqlRaw(sql)
.AsEnumerable()
.First().Value + 1;
return i;
}
Credits to:
https://erikej.github.io/efcore/2020/05/26/ef-core-fromsql-scalar.html
https://stackoverflow.com/a/18233089/7149454

SqlDependency fires immediately

I want to use SqlDependency to get notifications when some datas are changed by others applications using the database.
public class DatabaseChangesNotification : IDisposable
{
private static string chaineDeConnexion = ConfigurationManager.ConnectionStrings["TransfertContext"].ConnectionString;
private static readonly Lazy<DatabaseChangesNotification> _instance = new Lazy<DatabaseChangesNotification>(() => new DatabaseChangesNotification());
private DatabaseChangesNotification()
{
System.Diagnostics.Trace.WriteLine("--- SqlDependency START ---");
SqlDependency.Start(chaineDeConnexion);
}
public void Dispose()
{
System.Diagnostics.Trace.WriteLine("--- SqlDependency STOP ---");
SqlDependency.Stop(chaineDeConnexion);
}
public static DatabaseChangesNotification Instance
{
get
{
return _instance.Value;
}
}
public void AbonnerNotification(string requete, OnChangeEventHandler eventhandler)
{
using (SqlConnection connection = new SqlConnection(chaineDeConnexion))
{
using (SqlCommand command = new SqlCommand(requete, connection) { Notification = null }) // clear existing notifications
{
connection.Open();
var sqlDependency = new SqlDependency(command);
OnChangeEventHandler delegateAutoRemove = null;
delegateAutoRemove = (sender, e) => {
var dependency = sender as SqlDependency;
dependency.OnChange -= delegateAutoRemove;
eventhandler(sender, e);
};
sqlDependency.OnChange += delegateAutoRemove;
command.ExecuteNonQuery();
}
}
}
}
So, with a single line i can register an event handler :
DatabaseChangesNotification.Instance.AbonnerNotification(#"SELECT IdUtilisateur, Code, Nom, Prenom, NomComplet, Login, Synchroniser FROM dbo.Utilisateur", OnChanges);
public void OnChanges(object sender, SqlNotificationEventArgs e){
System.Diagnostics.Trace.WriteLine("------------------------------ UPDATTEEEE -------------------------");
System.Diagnostics.Trace.WriteLine("Info: " + e.Info.ToString());
System.Diagnostics.Trace.WriteLine("Source: " + e.Source.ToString());
System.Diagnostics.Trace.WriteLine("Type: " + e.Type.ToString());
GlobalHost.ConnectionManager.GetHubContext<TransfertClientHub>().Clients.All.hello("users modified !");
//AbonnementChanges();
}
But my problem is that the notification is immediatly fired :
--- ABONNEMENT ---
------------------------------ UPDATTEEEE -------------------------
Info: Query
Source: Statement
Type: Subscribe
That's why I commented AbonnementChanges in my event handler OnChanges (or it will loop infinitely).
I don't know where the problem comes from because I reset the notifications ({ Notification = null }) and my request respect the requirements (https://msdn.microsoft.com/en-us/library/ms181122.aspx).
Edit : I want to add that select * from sys.dm_qn_subscriptions returns nothing.
Edit : It looks like it comes from database configuration, and not from my implemention, as i tried another implemention which result in the same behaviour : http://www.codeproject.com/Articles/144344/Query-Notification-using-SqlDependency-and-SqlCach
Edit : I don't see where it comes from since i use SA which is sysadmin and have all rights, isn't it ?
Edit : I tried to define another connection to the database following this tutorial : http://www.codeproject.com/Articles/12862/Minimum-Database-Permissions-Required-for-SqlDepen
So i created 2 roles :
EXEC sp_addrole 'sql_dependency_subscriber'
EXEC sp_addrole 'sql_dependency_starter'
-- Permissions needed for [sql_dependency_starter]
GRANT CREATE PROCEDURE to [sql_dependency_starter]
GRANT CREATE QUEUE to [sql_dependency_starter]
GRANT CREATE SERVICE to [sql_dependency_starter]
GRANT REFERENCES on
CONTRACT::[http://schemas.microsoft.com/SQL/Notifications/PostQueryNotification]
to [sql_dependency_starter]
GRANT VIEW DEFINITION TO [sql_dependency_starter]
-- Permissions needed for [sql_dependency_subscriber]
GRANT SELECT to [sql_dependency_subscriber]
GRANT SUBSCRIBE QUERY NOTIFICATIONS TO [sql_dependency_subscriber]
GRANT RECEIVE ON QueryNotificationErrorsQueue TO [sql_dependency_subscriber]
GRANT REFERENCES on
CONTRACT::[http://schemas.microsoft.com/SQL/Notifications/PostQueryNotification]
to [sql_dependency_subscriber]
and then i added the user (production) to this roles :
-- Making sure that my users are member of the correct role.
EXEC sp_addrolemember 'sql_dependency_starter', 'production'
EXEC sp_addrolemember 'sql_dependency_subscriber', 'production'
But with this connection i have the same behaviour than before. Notification are fired imediatly :
------------------------------ UPDATTEEEE -------------------------
Info: Query
Source: Statement
Type: Subscribe
Edit : I tried with simpler requests like : SELECT Nom, Prenom FROM dbo.Utilisateur.
Here are the details of the table which should be inspected :
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Utilisateur](
[IdUtilisateur] [uniqueidentifier] ROWGUIDCOL NOT NULL CONSTRAINT [DF_Utilisateur_IdUtilisateur] DEFAULT (newid()),
[Code] [varchar](10) NOT NULL,
[Nom] [varchar](100) NOT NULL,
[Prenom] [varchar](100) NULL,
[NomComplet] AS (([Prenom]+' ')+[Nom]),
[Login] [varchar](50) NULL,
[Synchroniser] [bit] NOT NULL CONSTRAINT [DF_Utilisateur_Synchroniser] DEFAULT ((1)),
[DATE_CREATION] [datetime] NOT NULL CONSTRAINT [DF__Utilisate__DATE___2AA1E7C7] DEFAULT (getdate()),
[DATE_DERNIERE_MODIF] [datetime] NOT NULL CONSTRAINT [DF__Utilisate__DATE___2B960C00] DEFAULT (getdate()),
[Desactive] [bit] NOT NULL CONSTRAINT [DF_Utilisateur_Desactive] DEFAULT ((0)),
CONSTRAINT [PK_Utilisateur] PRIMARY KEY CLUSTERED
(
[IdUtilisateur] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
As we can see there are some columns which can't be requested. That's why i don't use it.
Now let's check with SELECT Nom, Prenom FROM dbo.Utilisateur :
The projected columns in the SELECT statement must be explicitly
stated, and table names must be qualified with two-part names. Notice
that this means that all tables referenced in the statement must be
in the same database. OK
The statement may not use the asterisk () or table_name. syntax to
specify columns. OK
The statement may not use unnamed columns or duplicate column names. OK
The statement must reference a base table. OK
The projected columns in the SELECT statement may not contain
aggregate expressions unless the statement uses a GROUP BY
expression. When a GROUP BY expression is provided, the select list
may contain the aggregate functions COUNT_BIG() or SUM(). However,
SUM() may not be specified for a nullable column. OK
The statement may not specify HAVING, CUBE, or ROLLUP. A projected
column in the SELECT statement that is used as a simple expression
must not appear more than once. OK
The statement must not include PIVOT or UNPIVOT operators. OK
The statement must not include the INTERSECT or EXCEPT operators. OK
The statement must not reference a view. OK
The statement must not contain any of the following: DISTINCT,
COMPUTE or COMPUTE BY, or INTO. OK
The statement must not reference server global variables
(##variable_name). OK
The statement must not reference derived tables, temporary tables, or
table variables. OK
The statement must not reference tables or views from other databases
or servers. OK
The statement must not contain subqueries, outer joins, or
self-joins. OK
The statement must not reference the large object types: text, ntext,
and image. OK
The statement must not use the CONTAINS or FREETEXT full-text
predicates. OK
The statement must not use rowset functions, including OPENROWSET and
OPENQUERY. OK
The statement must not use any of the following aggregate functions:
AVG, COUNT(*), MAX, MIN, STDEV, STDEVP, VAR, or VARP. OK
The statement must not use any nondeterministic functions, including
ranking and windowing functions. OK
The statement must not contain user-defined aggregates. OK
The statement must not reference system tables or views, including
catalog views and dynamic management views. OK
The statement must not include FOR BROWSE information. OK
The statement must not reference a queue. OK
The statement must not contain conditional statements that cannot
change and cannot return results (for example, WHERE 1=0). OK
But that still doesn't works ... =(
Final edit - Solution : As Jon Tirjan said, it was caused by my computed column NomComplet which is not valid with the Service Broker (even when I don't ask to be notified on changes on this column, which is strange to me).
Service Broker doesn't work on tables with computed columns. You need to remove NomComplet from your table, or change it to an actual column which is populated another way (trigger, stored procedure, etc.)
The notification is being fired immediately because an error occurs while setting up the queue.
Thanks George Stocker for deleting my previous answer, but I had a serious issue with SqlDependency and I insist:
Be careful using SqlDependency class - it has the problems with memory leaks.
For my project I've used open source realization - SqlDependencyEx. It uses a database trigger and native Service Broker notification to receive events about the table changes. This is an usage example:
int changesReceived = 0;
using (SqlDependencyEx sqlDependency = new SqlDependencyEx(
TEST_CONNECTION_STRING, TEST_DATABASE_NAME, TEST_TABLE_NAME))
{
sqlDependency.TableChanged += (o, e) => changesReceived++;
sqlDependency.Start();
// Make table changes.
MakeTableInsertDeleteChanges(changesCount);
// Wait a little bit to receive all changes.
Thread.Sleep(1000);
}
Assert.AreEqual(changesCount, changesReceived);
With SqlDependecyEx you are able to monitor INSERT, DELETE, UPDATE separately and receive actual changed data (xml) in the event args object. Hope this help.

Why isn't the new row being added to my database table?

Here's my code:
private void button1_Click(object sender, EventArgs e)
{
var dbContext = new DataClasses1DataContext();
var record = new AutorizedActivity();
record.MbrId = (int) ddlMember.SelectedValue;
record.ActId = (int) ddlActivity.SelectedValue;
dbContext.AutorizedActivities.InsertOnSubmit(record);
dbContext.SubmitChanges();
}
Basically, the form has two dropdownlists. The user makes a selection from each list and presses the button to update a table with their selections.
The problem is, no data is ever being added to the table. No errors occur at all. I've stepped through in debug, and can't see anything wrong. Are there any common things I could check to determine why the program appears to insert a record, but nothing is actually inserted?
Here's my table definition:
CREATE TABLE [dbo].[AutorizedActivities] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[MbrId] INT NOT NULL,
[ActId] INT NOT NULL,
CONSTRAINT [PK_AutorizedActivities] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_AutorizedActivities_AvailableActivities] FOREIGN KEY ([ActId]) REFERENCES [dbo].[AvailableActivities] ([Id]),
CONSTRAINT [FK_AutorizedActivities_Members] FOREIGN KEY ([MbrId]) REFERENCES [dbo].[Members] ([Id]));
Here's the datacontext layout, for clarity:
SOLUTION: The database table was being updated... just not the one I was looking at. I was running the program in debug mode, which places a copy of the .mdf in the debug/bin directory. I wasn't looking at that table; I was looking at the table in my production directory. If I had ran the program in release mode, it would have worked like a charm and updated the live table, as opposed to the test (debug) version.
Two things might help here, but it's impossible to give a definite answer on this one - you're gonna have to try things. The first things I would try here are changing to the 'standard' structure for EF code, which will "Close/Dispose" the context, and making sure your identity property is set correctly on the Entity itself. Sometimes VS doesn't do that automatically. So, check that setting and change the code to this...
using (var dbContext = new DataClasses1DataContext()) {
var record = new AutorizedActivity();
record.MbrId = (int) ddlMember.SelectedValue;
record.ActId = (int) ddlActivity.SelectedValue;
dbContext.AutorizedActivities.Add(record);
dbContext.SubmitChanges();
}
Also... did you misspell "Authorized" - fix that now before it's too late and you propogate that through the whole app.
The database table was being updated... just not the one I was looking at. I was running the program in debug mode, which places a copy of the .mdf in the debug/bin directory. I wasn't looking at that table; I was looking at the table in my production directory. If I had ran the program in release mode, it would have worked like a charm and updated the live table, as opposed to the test (debug) version.

ORA-01400 when trying to insert into schema.table.ID

I have a table that stores rejected contract proposals.
CREATE TABLE "STATUS_CONTRATO" (
"STC_ID" NUMBER NOT NULL,
"CTB_CONTRATO" NUMBER NOT NULL,
"STC_DATA" DATE NOT NULL,
"STC_OBSERVACAO" VARCHAR2(200) NOT NULL,
CONSTRAINT "STATUS_CONTRATO_PK"
PRIMARY KEY ( "STC_ID")
ENABLE
VALIDATE,
CONSTRAINT "FK_CONTRATO"
FOREIGN KEY ( "CTB_CONTRATO")
REFERENCES "CONTRATO" ( CTB_CONTRATO)
ON DELETE SET NULL
ENABLE
VALIDATE)
;
(Script generated by Visual Studio 2010)
This table has a simple Trigger, where the value of STC_ID is set:
TRIGGER "STATUS_CONTRATO_TRIGGER1"
BEFORE
INSERT
ON "STATUS_CONTRATO"
FOR EACH ROW
when (new.STC_ID = 0)
DECLARE
BEGIN
SELECT SEQ_STATUS_ID.NEXTVAL INTO :NEW.STC_ID FROM DUAL;
END;
SEQ_STATUS_ID is a simple sequence.
Here's my problem:
I can successfuly execute this insert in the VS2010 query window:
insert into myschema.STATUS_CONTRATO s(
s.STC_ID, s.CTB_CONTRATO, s.STC_DATA, s.STC_OBSERVACAO
)values(
0, 10, SYSDATE, 'Inserting by hand works'
);
But, when I try to insert using EF, I'm getting this exception:
System.Data.UpdateException: An error occurred while updating the entries.
See the inner exception for details. ---> Oracle.DataAccess.Client.OracleException:
ORA-01400: cannot insert NULL into ("MYSCHEMA"."STATUS_CONTRATO"."STC_ID")
ORA-06512: at line 4
I'm using this code to insert
STATUS_CONTRATO statusContrato = new STATUS_CONTRATO() {
STC_ID = 0,
CTB_CONTRATO = codContrato,
STC_DATA = DateTime.Today,
STC_OBSERVACAO = observacao
};
ent.STATUS_CONTRATO.AddObject(statusContrato);
ent.SaveChanges();
I'm using VS2010, Oracle 11g (CentOS Server), ODP.NET client 11.2.0.3.0 Production, .NET Framework 4.0, EF 4.
Check this: http://www.oracle.com/technetwork/issue-archive/2011/11-sep/o51odt-453447.html
Particularly, section "Triggers and Sequences"
From the Tools menu, select Run SQL Plus Script. Browse to the
location where you extracted the code and scripts, select the
triggers.sql script, select the HR connection from the list, and click
Run. The INSERTEMPLOYEES trigger created by the script generates a new
sequence for EMPLOYEE_ID whenever NULL is passed in for that value........
Your code works, except for a problem with
ent.UNV_STATUS_CONTRATO.AddObject(statusContrato);
Is UNV_STATUS_CONTRATO another table? Why is it not
ent.STATUS_CONTRATO.AddObject(statusContrato);
That should work just fine.
However, you might find it preferable to get the sequence value from your code and apply it to your ID column in memory before saving changes. So something like this:
public static int GetSequenceNextVal()
{
using (YourEntities entities = new YourEntities ())
{
var sql = "select myschema.SEQ_STATUS_ID.NEXTVAL from dual";
var qry = entities.ExecuteStoreQuery<decimal>(sql);
return (int)qry.First();
}
}
Then you call that method before SaveChanges().
Personally, I prefer to handle it this way so that my in-memory entity then also has the correct ID, instead of just having a 0, or having to query it right back out, etc.
Also, the method described here under Triggers and Sequences can supposedly wire up an entity's PK to a sequence.
You need to specify in your EF Mapping that the DB does not generate the key for you and EF should use the Key you provided...
see my post in the following thread:
https://stackoverflow.com/a/18635325/1712367

Categories

Resources