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

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

Related

Insert 2 rows in tables with foreign key relation without knowing the primary key of the main table

I have the following databasescheme in SQL Server Manager 2014.
I'm making a C#-windows application in Visual Studio and I want to insert a new orderline and a new order. The problem is that the primary keys of both tables, auto-generate in server manager, so I haven't yet the value of the primary key of the order-table, but I need that value to fill into the foreign key of the orderLine column. How can I insert these two rows.
Kind regards
SCOPE_IDENTITY returns the last identity value inserted into an identity column in the same scope. A scope is a module: a stored procedure, trigger, function, or batch. Therefore, two statements are in the same scope if they are in the same stored procedure, function, or batch.
You can use SqlCommand.ExecuteScalar to execute the insert command and retrieve the new ID in one query.
using (var con = new SqlConnection(ConnectionString)) {
int newOrderID;
var cmd = "INSERT INTO Order (column_name) VALUES (#Value) ;SELECT CAST(scope_identity() AS int)";
using (var insertCommand = new SqlCommand(cmd, con)) {
insertCommand.Parameters.AddWithValue("#Value", "bar");
con.Open();
newOrderID = (int)insertCommand.ExecuteScalar();
}
}
This will allow you to catch the last generated OrderId and use it in the Insert Statement for the OrderLine table.
Another option is to use the following SQL code:
string command = "INSERT INTO Order(totalPrice) OUTPUT INSERTED.ID VALUES(#totalPrice)" // this will be a parameter from your code
Then the OrderId can be taken from :
Int32 orderId = (Int32) command.ExecuteScalar();
While scope_id() works fine for single rows, you really should learn to use the output clause as scope_id() is useless for multiple rows inserted with a single sql statement.
See this prior question for a simple example of using the output clause.
Obviously this allows you to retrieve more than just the identity value too.
ADDED
Also useful is the new sequence feature (added for 2012) instead of using identity. If your are coming from other databases this may seem a more natural solution.
Sequence is very useful if you would like to share a single sequence among several tables -- although this is an uncommon design I have used it a few times.
If you're using any form of direct SQL, you need to receive the SCOPE_IDENTITY() value immediately after inserting your order, then use that value to insert your lines.
INSERT INTO Order
SELECT SCOPE_IDENTITY() AS NewId; OR RETURN SCOPE_IDENTITY(); OR DECLARE #OrderId INT = SCOPE_IDENTITY();
INSERT INTO OrderLine
Otherwise, use Entity Framework and it will automatically retrieve your new IDs and assign to dependencies.

Violation of PRIMARY KEY constraint error occurred in the insert sql right after the update sql returns 0 rows

I have the code like this
if (***Adapter.UpdateRow(memberID, Type) == 0)
{
***Adapter.InsertRow(memberID, Type);
}
but i get the Violation of PRIMARY KEY constraint error in the InsertRow.
the primary key in table is memberID and Type and here are the sqls:
InsertRow
string sql = #"
INSERT INTO TBL***** (
MemberID
, Type
, DateTime
)
VALUES (
#MemberID
, #Type
, GETDATE()
)
";
var cm = new SqlCommand(sql.ToString());
cm.Parameters.Add("#MemberID", SqlDbType.Int).Value = memberID;
cm.Parameters.Add("#Type", SqlDbType.Int).Value = (int)Type;
return this.ExecuteNonQuery(cm);
UpdateRow
string sql = #"
UPDATE TBL****
SET DateTime = GETDATE()
WHERE MemberID = #MemberID
AND Type = #Type
";
var cm = new SqlCommand(sql.ToString());
cm.Parameters.Add("#MemberID", SqlDbType.Int).Value = memberID;
cm.Parameters.Add("#Type", SqlDbType.Int).Value = (int)Type;
return this.ExecuteNonQuery(cm);
i'm really want to now the reason why it happened (right after the update one).
thank you all for your comments .
I think that maybe there are two threads that conflicted with each other ,then the first one inserted successfully but the second one failed.
I have this logic in my web site's user registration and it happends when the user click the activation link in the register mail.
I think the user may double click(with a high speed) the link and the error occured.
A Violation of PRIMARY KEY constraint means that you're attempting to insert a record that will create a duplicate on the primary key fields.
Check the table definition and note the fields in the primary key.
If you can post the table definition in your question for other readers even better :)
Edit after comment added
Given your primary key contains both MemberId and Type, there is likely a logic error in the update, or there is a scenario where no results are returned in the update.
You should use an upsert (update or insert) as Allan S. Hansen has suggested.
The logic is as follows:
Check if the record exists
Either update if exists or insert a new record
You wouldn't normally perform an update to check if the record exists, as the update may perform no changes in some scenarios.
Your code is relying on the ROWCOUNT being returned after executing DML. This is not guaranteed to happen, because is subject to SET NOCOUNT setting. When SET NOCOUNT is ON
the returned rowcount is always 0, irrelevant how many rows where actually updated. The returned rowcount is what ExecuteNonQuery() returns.
You could check and ensure SET NOCOUNT is OFF but it would still be a lost cause. The code you propose is fundamentally flawed in presence of concurrency: multiple client apps can run the UPDATE simultaneously and all conclude they should INSERT and then only one would succeed. The correct way to do this is to use MERGE statement:
MERGE INTO TBL**** as t
USING (VALUES (GETDATE(), #MemberId, #Type)) AS s (date, MemberID, Type)
ON t.MemberID =s.MemberID and t.Type = s.Type
WHEN MATCHED THEN
UPDATE SET t.DateTime = s.date
WHEN NOT MATCHED BY TARGET
INSERT (MemberID, Type, DateTime) VALUES (s.MemberID, s.Type, s.DateTime);
This is a single statement that does wither the INSERT or the UPDATE without the concurrency risks.

Stored procedure returns int instead of result set

I have a stored procedure that contains dynamic select. Something like this:
ALTER PROCEDURE [dbo].[usp_GetTestRecords]
--#p1 int = 0,
--#p2 int = 0
#groupId nvarchar(10) = 0
AS
BEGIN
SET NOCOUNT ON;
DECLARE #query NVARCHAR(max)
SET #query = 'SELECT * FROM CUSTOMERS WHERE Id = ' + #groupId
/* This actually contains a dynamic pivot select statement */
EXECUTE(#query);
END
In SSMS the stored procedure runs fine and shows result set.
In C# using Entity Framework it shows returning an int instead of IEnumerable?
private void LoadTestRecords()
{
TestRecordsDBEntities dataContext = new TestRecordsDBEntities();
string id = ddlGroupId.SelectedValue;
List<TestRecord> list = dataContext.usp_GetTestRecords(id); //This part doesn't work returns int
GridView1.DataSource = list;
}
Generated function for usp_GetTestRecords
public virtual int usp_GetTestRecords(string groupId)
{
var groupIdParameter = groupId != null ?
new ObjectParameter("groupId", groupId) :
new ObjectParameter("groupId", typeof(string));
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction("usp_GetTestRecords", groupIdParameter);
}
I get this when I have a stored procedure that includes an "exec" call into a temporary table, such as:
insert into #codes (Code, ActionCodes, Description)
exec TreatmentCodes_sps 0
It appears that Entity Framework gets confused as to what should be returned by the procedure. The solution I've come across is to add this at the top of the sproc:
SET FMTONLY OFF
After this, all is well.
I got the same problem, and found solution here
Move to your .edmx
At Model Browser Window/Function Imports find your procedure then double click it
Change the return type to you want
Save .edmx and check the return type again.
It should be what you need now.
Entity Framework can't tell what your stored procedure is returning. I've had success creating a table variable that mirrors the data from your SELECT statement. Just insert into the table variable then do a select from that table variable. EF should pick it up.
See Ladislav Mrnka's answer in this Stack Overflow post
https://stackoverflow.com/a/7131344/4318324
I had the same basic problem.
Adding
SET FMTONLY OFF
To a procedure you are trying to import during the import will address this problem.
It's a good practice to remove the line afterwards unless the purpose of the database is solely to provide schema for EF (Entity Framework).
The main reason for caution is that EF uses this setting to prevent data mutations when trying to obtain metadata.
If you refresh your entity model from a database any procedures with this line in them can potentially update the data in that database just by trying to obtain the schema.
I wanted to add a further note on this so it's not needed to fully scan through the other link.
if you want to try to use FMTONLY here are a couple things to keep in mind.
when FMTONLY is on:
1) only the schema is returned (no) rows.
similar to adding a blanket false statement to your where clause (ie "where 1=0")
2) flow control statements are ignored
Example
set fmtonly on
if 1=1
begin
select 1 a
end
else
begin
select 1 a,2 b
end
while 1=1
select 1 c
The above returns NO rows whatsoever and the metadata for each of the three queries
For this reason some people suggest toggling it off in a way that takes advantage of it's non-observance of flow control
if 1=0
begin
set fmtonly off
end
In fact you could use this to introduce logic that tracks this
set fmtonly off
declare #g varchar(30)
set #g = 'fmtonly was set to off'
if 1=0
begin
set fmtonly off
set #g = 'fmtonly was set to on'
end
select #g
Think VERY CAREFULLY before trying to use this feature as it is both deprecated and potentially makes sql extremely hard to follow
the MAIN concepts that need to be understood are the following
1. EF turns FMTONLY on to prevent MUTATING data from executing stored procedures
when it executes them during a model update.
(from which it follows)
2. setting FMTONLY off in any procedure that EF will attempt to do a schema scan
(potentially ANY and EACHONE) introduces the potential to mutate database
data whenever *anyone* attempts to update their database model.
Entity Framework will automatically return a scalar value if your stored procedure doesn't have a primary key in your result set. Thus, you'd have to include a primary key column in your select statement, or create a temp table with a primary key in order for Entity Framework to return a result set for your stored procedure.
I had the same problem, I changed the name of return fields by 'AS' keyword and addressed my problem. One reason for this problem is naming column names with SQL Server reserved keywords.
The example is fallows:
ALTER PROCEDURE [dbo].[usp_GetProducts]
AS
BEGIN
SET NOCOUNT ON;
SELECT
, p.Id
, p.Title
, p.Description AS 'Description'
FROM dbo.Products AS p
END
Best solution I found is to cheat a little.
In the store procedure, comment everything, put a first line with a select [foo]='', [bar]='' etc...
Now update the model, go to the mapped function, select complex type and click on Get Column Information and then Create Complex Type.
Now comment the fake select and un-comment the real store procedure body.
When you generated your model class for your stored procedure, you chose scalar return result by mistake. you should remove your stored procedure from your entity model, then re-add the stored procedure. In the dialog for the stored procedure, you can choose the return type you are expecting. Do not just edit the generated code.. this may work now, but the generated code can be replaced if you make other changes to your model.
I have pondered this a bit and I think I have a better/simpler answer
If you have a complex stored that gives entity framework some difficultly (for current versions of Entity Framework that are using the FMTONLY tag to aquire schema)
consider doing the folowing at the beginning of your stored procedure.
--where [columnlist] matches the schema you want EF to pick up for your stored procedure
if 1=0
begin
select
[columnlist]
from [table list and joins]
where 1=0
end
if you are okay loading your result set into a table variable
you can do the following to help keep your schema in sync
declare #tablevar as table
(
blah int
,moreblah varchar(20)
)
if 1=0
begin
select * from #tablevar
end
...
-- load data into #tablevar
select * from #tablevar
If you need to do this, then you might be better off just making a partial of the dbcontext and creating the C# function yourself that will use SqlQuery to return the data you need. Advantages over some of the other options is:
Don't have to change anything when the model updates
Won't get overwritten if you do it directly in the generated class (someone above mention this as if it's an option :) )
Don't have to add anything to the proc itself that could have side effects now or later on
Example Code:
public partial class myEntities
{
public List<MyClass> usp_GetTestRecords(int _p1, int _p2, string _groupId)
{
// fill out params
SqlParameter p1 = new SqlParameter("#p1", _p1);
...
obj[] parameters = new object[] { p1, p2, groupId };
// call the proc
return this.Database.SqlQuery<MyClass>(#"EXECUTE usp_GetTestRecords #p1, #p2, #groupId", parameters).ToList();
}
}
Just change to
ALTER PROCEDURE [dbo].[usp_GetTestRecords]
--#p1 int = 0,
--#p2 int = 0
#groupId nvarchar(10) = 0
AS
BEGIN
SET NOCOUNT ON;
SELECT * FROM CUSTOMERS WHERE Id = #groupId
END
I know this is an old thread but in case someone has the same problems I'll tell my woes.
As a help to find the issue, run sql profiler when you add your stored proc. Then you can see what entity framework is passing as parameters to generate your resultset. I imagine nearly always it will pass null parameter values. If you are generating sql on the fly by concatenating string values and parameter values and some are null then the sql will break and you wont get a return set.
I haven't needed to generate temp tables or anything just an exec command.
Hope it helps
During import
SET FMTONLY ON
can be used for taking the sp schema.
If you change the sp and want to update the new one, you should delete the old defined function from edmx file (from xml), because although deleting sp from model browser, it is not deleted in edmx. For example;
<FunctionImport Name="GetInvoiceByNumber" ReturnType="Collection(Model.Invoice_Result)">
<Parameter Name="InvoiceNumber" Mode="In" Type="Int32" />
</FunctionImport>
I had the same problem, and when I delete the FuctionImport tag of corresponding sp totally, the model updated right. You can find the tag by searching the function name from visual studio.
You may have luck opening up the model browser, then going to Function Imports, double clicking the stored procedure in question and then manually clicking "Get Column Information" and then clicking "Create New Complex Type". This usually sorts out the problem.
Well I had this issue as well but after hours of online searching none of above methods helped.
Finally I got to know that It will happen if your store procedure is getting some parameters as null and which generate any error in query execution.
Entity Framework will generate method for store procedure by defining the complex entity model. Due to that null value your store procedure will return and int value.
Please check your store procedure either its providing empty result set with null values. It will fix your problem. Hopefully.
I think this is a problem of permissions on the database, I don't know what exactly could be, but, in my job we use Active Directory users to grant applications connect to databases, this accounts are specially created for the applications, each app has its own user account, well, as a developers I have permissions for read, write and other basic things, no alter, and no advanced features, and I have this same problem running Visual Studio with my normal account, then, what I did was to open Visual Studio selecting the option "as a different user" on the context menu, and I put the AD login granted for the application and voila!, now my Stored Procedures are loading with all the fields I was expected, before that, my Stored Procedures was returning as int. I hope this help someone, maybe the VIEW DEFINITION permissions on database account do the trick
If SQL Authentication is in place, verify that the user credential that is being used to connect Entity Framework to the database has the proper rights to read from CUSTOMERS table.
When Entity Framework uses SQL Authentication to map complex objects (i.e stored procedures that SELECTs more than one column), if any of the tables from within such stored procedure don't have set up the Read permission, the mapping will result in returning INT instead of the desired Result set.

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

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.

Getting ##IDENTITY from TableAdapter

I am trying to complete a seemingly simple task that has turned into a several hour adventure: Getting ##Identity from TableAdapter.Insert().
Here's my code:
protected void submitBtn_Click(object sender, EventArgs e)
{
AssetsDataSetTableAdapters.SitesTableAdapter sta = new AssetsDataSetTableAdapters.SitesTableAdapter();
int insertedID = sta.Insert(siteTxt.Text,descTxt.Text);
AssetsDataSetTableAdapters.NotesTableAdapter nta = new AssetsDataSetTableAdapters.NotesTableAdapter();
nta.Insert(notesTxt.Text, insertedID, null,null,null,null,null,null);
Response.Redirect("~/Default.aspx");
}
One answer suggests all I may have to do is change the ExecuteMode. I tried that. This makes GetData() quit working (because I'm returning a scalar now instead of rowdata) (I need to keep GetData()). It also does not solve the issue in that the insertedID variable is still set to 1.
I tried creating a second TableAdapter in the TypedDataSet.XSD and setting the property for that adapter to "scalar", but it still fails with the variable getting a value of 1.
The generated insert command is
INSERT INTO [dbo].[Sites] ([Name], [Description]) VALUES (#Name, #Description);
SELECT Id, Name, Description FROM Sites WHERE (Id = SCOPE_IDENTITY())
And the "Refresh the Data Table" (adds a select statement after Insert and Update statements to retrieve Identity" is also set.
Environment
SQL Server 2008 R2, Visual Studio 2010, .NET 4, Windows XP, all local same machine.
What's causing this?
EDIT/UPDATE
I want to clarify that I am using auto-generated code within Visual Studio. I don't know what the "tool" that generated the code is, but if you double click the *.XSD file it displays a UI of the SQL Table Schema's and associated TableAdapter's. I want to keep using the auto-generated code and somehow enable getting the Identity. I don't want to write this all by hand with stored procedures.
The real answer:
read the notes below!
Get identity from tableadapter insert function
I keep getting questions about this issue very often and never found
time to write it down.
Well, problem is following: you have table with primary key with int
type defined as Identity and after insert you need to know PK value of
newly inserted row. Steps for accomplishing to do this are following:
use wizard to add new insert query (let's call it InsertQuery) in the
body of query just add SELECT SCOPE_IDENTITY() at the bottom after
saving this query, change ExecuteMode property of this query from
NonQuery to Scalar in your code write following (ta is TableAdapter
instance) :
int id;
try
{
id = Convert.toInt32(ta.InsertQuery(firstName, lastName, description));
}
catch (SQLException ex)
{
//...
}
finally
{
//...
}
Make money with this! :) Posted 12th March 2009 by Draško Sarić
From:
http://quickdeveloperstips.blogspot.nl/2009/03/get-identity-from-tableadapter-insert.html
Notes:
Setting the ExecutMode to Scalar is possible via the Properties of your generated Insert Query. (press F4).
In my version (Visual Studio 2010 SP1 ) the select statement was generated automatically.
Here's my SQL Code that works.
CREATE PROCEDURE [dbo].[Branch_Insert]
(
#UserId uniqueidentifier,
#OrganisationId int,
#InsertedID int OUTPUT
)
AS
SET NOCOUNT OFF;
INSERT INTO [Branch] ([UserId], [OrganisationId])
VALUES (#UserId, #OrganisationId);
SELECT Id, UserId, OrganisationId FROM Branch WHERE (Id = SCOPE_IDENTITY())
SELECT #InsertedID = SCOPE_IDENTITY()
Then when I create the Table Adapter - I can instantly see the #InsertedID parameter.
Then from code, all I do is:
int? insertedId = 0;
branchTA.Insert(userId, orgId, ref insertedId);
I'm not 100% whether using ref is the best option but this works for me.
Good luck.
All of the info is here, but I didn't find any single answer complete, so here is the complete steps I use.
Add an insert query, and append SELECT SCOPE_IDENTITY() to it, like so:
INSERT INTO foo(bar) VALUES(#bar);
SELECT SCOPE_IDENTITY()
Make sure you add a ; to the end of the INSERT statement that VS creates for you.
After you Finish the add query wizard, make sure the query is selected in the design view then change Execute Mode to Scalar from the properties pane.
Make sure you use Convert.ToInt32() when you call the query from your code, like so:
id = Convert.ToInt32( dataTableAdapter.myInsertQuery("bar") )
You will not get compiler errors without the Convert.ToInt32, but you will get the wrong return value.
Also, any time you modify the query, you have to reset the Execute Mode back to Scalar, because VS will change it back to Non Query every time.
Here's how you do it (in the visual Designer)
Right Click the Table Adapter and "Add Query"
SQL Statements - Choose Update (best auto-gen parameters)
Copy and paste your SQL, it can be multi-line, just make sure that the "Query Designer" doesn't open up, as it will not be able to interpret the multiple commands - my example shows a sample "merge" set of statements (note that new SERVERS have Merge commands).
UPDATE YOURTABLE
SET YourTable_Column1 = #YourTable_Column1, YourTable_Column2 = #YourTableColumn2
WHERE (YourTable_ID = #YourTable_ID)
IF ##ROWCOUNT=0
INSERT INTO YOURTABLE ([YourTable_Column1], [YourTable_Column2])
VALUES (#YourTable_Column1, #YourTable_Column2)
#YourTable_ID = SCOPE_IDENTITY()
Change/Add the #YourTable_ID Parameters from the query properties window/sidebar. In the Parameter Collection Editor, The ID parameter needs to be have a Direction of InputOutput, so that the value gets updated when the Table Adapter function is called. (Special note: Make sure that whatever column you make InputOutput that the designer doesn't have this column as "Read Only" and that the data types match up as well, otherwise change the column in the datatable, or the parameter information accordingly)
This should save the need of writing a Stored procedure for such a simple activity.
Much Wow. You will notice that this method is a fast way of doing Data Layer functions without having to break into the SQL procedures and write up a ton of Procedures. The only problem, there is a lot of dancing you have to do...
You'll need to setup the insert to return the identity as an output value and then grab it as a parameter in your adapter.
These two links should get you going:
http://www.akadia.com/services/dotnet_autoincrement.html
http://msdn.microsoft.com/en-us/library/ks9f57t0.aspx
You have exactly two choices:
change your SQL code manually
use whatever Visual Studio generates
I'd use the following SQL and ExecuteScalar.
INSERT INTO [dbo].[Sites] ([Name], [Description])
OUTPUT INSERTED.ID
VALUES (#Name, #Description);
One way is to run a select query after the insert command. A good way is to wrap the original command like this:
public int WrapInsert(Parameters)
{
.....
int RowsAffected = this.Insert(..Parameters..);
if ( RowsAffected > 0)
{
try
{
SqlCommand cm = this.Connection.CreateCommand();
cm.CommandText = "SELECT ##IDENTITY";
identity = Convert.ToInt32(cm.ExecuteScalar());
}
finally
{
....
}
}
return RowsAffected;
}

Categories

Resources