How to Check Unique Key Constraint [duplicate] - c#

This question already has answers here:
combination of two field must be unique in Entity Framework code first approach. how it would?
(2 answers)
Closed 8 years ago.
I want check unique key constrain but I do not know which method to use.
Which method is better?
I use c#, EF, SQL Server
First Method:
bool contactExists = _db.Contacts.Any(contact => contact.ContactName.Equals(ContactName));
if (contactExists)
{
return -1;
}
else
{
_db.Contacts.Add(myContact);
_db.SaveChanges();
return myContact.ContactID;
}
Second Method:
Handle in exception.
The third method:
check with T-SQL
If Exists (
Select Name
From dbo.ContentPage
Where Name = RTRIM(LTRIM(N'page 1'))
)
Begin
Select 'True'
End
Else
Begin
Select 'False'
End

I rarely check unique key constraints in code. It fact, it is really a waste of time if multiple clients can be updating data at the same time. Say you check to make sure you could add the employee 'Saeid Mirzaei' and found the key was not in use. So you add it. You can still get a dup key problem if someone else enters it in the meantime and you end up getting the exception anyway. You can handle the exception in TSQL or C# but you pretty much need to use exception handling for robust code.

It depends on your requirements, but usually handling in a Exception is the best choice. This will be most efficient and reliable. I assume you mean by this the Exception that will be thrown because of the unique constraint.

I prefer method 1, check in your code AND add UNIQUE constraint in your column.

I suggest you that Create Unique index on Name column in SQL Server, and have instead of insert trigger for save RTRIM(LTRIM(Inserted.name)) column instead of name column on your table.
Also you can have client control for reduce network connection to your database.

Related

SQL Server prevent only specific table from updating based on parameter passed from C# controller

In our .net application, we have a tool that allows you to type SQL in a browser and submit it, for testing. In this context, though, I need to be able to prevent testers from writing to specific tables. So, based on the parameter passed from the controller (InSupportTool = true, or something), I need to know if SQL Server is allowed to make updates or inserts to, say, an accounts table.
Things I've tried so far:
I have tried looking into triggers, but there is no before trigger available, and I've heard people don't recommend using them if you can help it.
Parsing the passed SQL string to look for references to inserting or updating on that table. This is even more fragile and has countless ways, I'm sure, of getting around it if someone wanted to.
Check constraint, which is the closest I feel I've gotten but I can't quite put it together.
For check constraints, I have this:
ALTER TABLE Accounts WITH NOCHECK
ADD CONSTRAINT chk_read_only_accounts CHECK(*somehow this needs to be dynamic based on parameters passed from C# controller*)
The above works to prevent updates to that table, but only if I put a check like 1 = 0. I've seen a post where people said you could use a function as the check, and pass parameters that way, but I'm at the limit of my familiarity with SQL/.net.
Given what I'm looking to do, does anyone have experience with something like this? Thanks!
Since the application is running under a different account than the end user, you could specify your application name in the connection string (e.g. Application Name=SupportTool) and check that in an after trigger, rolling back the transaction as needed:
CREATE TABLE dbo.example(
col1 int
);
GO
CREATE TRIGGER tr_example
ON dbo.example
AFTER INSERT, UPDATE, DELETE
AS
IF APP_NAME() = N'SupportTool'
BEGIN
ROLLBACK;
THROW 50000, 'This update is not allowed using the support tool', 1;
END;
GO
INSERT INTO dbo.example VALUES(1);
GO

How to implement Identity for group of records?

I have a table that contains a non primary key RequestID. When I do a bulkInsert, all the records must have the same RequestID. But If I do another BulkInsert, the next inserted rows must have RequestID incremented :
NewRequestID = PreviousRequestID + 1
The only solution I found so far -and I don't like it by the way-, is to get the last record everytime before inserting the new records.
Why I dont like this approach ? because the database is supposed to be relationnel, which means there is "no specific order". Besides, I don't have primary keys or Dates to order with.
What is the best way to implement this?
(I've added c# tag because i am using EF. if there is an easy solution with EF)
You could take a number of different approaches:
Are you guaranteed that your RequestID's are always incremented? If so, you could query table for largest RequestID and that should represent the "last one inserted."
You could track state somewhere in your application, but this is likely dangerous in scenarios where service fails/restarts (unless state is tracked externally).
Assuming you have control over the schema, if you don't want to update the particular table schema you are speaking of, you could create another table to track the last RequestID used, and retrieve it from there (which would protect you against service restarts/failures).
Those are a few that come to mind.
UPDATE:
Assuming RequestID isn't a particular type of identifier, you could use timestamp - which will always be incremented when you do a new batch, however, I'm not sure if you needed it to always be incremented by exactly '1' which would preclude this approach.

Exception Handling better in Db or in c# code?

I have a stored procedure to insert data in SQL Server DB. I'm using WCF Service which takes data from client and inserts data in the DB. I have a unique key constraint on Name column of my DB table.
If there is any unique key constraint errors:
I handle that in Stored Procedure. e.g using if exists statement in SQL Server and if value is there then I should return -1 otherwise it should insert row in db.
I handle that in my (WCF Service)c# code. I get sql exception and return that sql exception code to the client.
In the first solution I think there is performance issue, because unique key constraint will be checked 2 times. First time I’m checking it manually within stored procedure and second time unique key constraint will check it again. So, 1 value is being checked twice.
In second solution exception is being handle by wcf service using c# code and I’ve heard exceptions in wcf is not so good.
What's the best solution?
"Better" is a little bit subjective here, and it kinds of depends on which "better" you like.
If you mean from a performance perspective, I'd be willing to bet that Option 1 is actually more performant, since the process of checking an index for an existing value in SQL Server (even twice) will probably be dwarfed by the time it takes to raise and propagate an exception back into your code. (Not to mention that you don't have to check it twice at all, you can try/catch in T-SQL itself and return -1 on a Unique key violation)
However if you mean from a design and maintenance point of view, then Option 2 is in my opinion far more desirable, since it is very clear what is going on
In other words, as a developer I would rather read (pseudo-code)
//assuming you have a connection open, a command prepared, etc.
try
{
var result=command.ExecuteNonQuery();
}
catch(SqlException ex)
{
if(ex.Number==2627)
{
//Unique Key constraint violation.
//Error 2627 is documented and well known to be a Unique Key Constraint Violation
//so it's immediately obvious what's going on here
}
}
than
var result=command.ExecuteNonQuery();
if (result==-1)
{
//this means a unique key violation
//what if that comment got removed? I'd have no clue what
//-1 meant...
}
Even though at first glance the second is shorter and more succint.
Note: as MetroSmurf pointed out, catching the exception here is not ideal, it should be handled by the caller, so this is just for illustrative purposes.
This is because the -1 here is pretty arbitrary on the part of the stored procedure; so unless you can guarantee that you can document it and that this document will never go out of date ,etc, then you could be placing the burden on the next developer to have to go look up the Stored Procedure, and figure out what exactly -1 means in this context.
Plus since it's possible for someone to change the SP without touching your C#, what are you going to do if the SP suddenly starts returning "42" for Unique Key Violations? Of course you may be in full control of the SP but will that always be the case?

How to get Indentity value back after insert

Given the following code (which is mostly irrelevant except for the last two lines), what would your method be to get the value of the identity field for the new record that was just created? Would you make a second call to the database to retrieve it based on the primary key of the object (which could be problematic if there's not one), or based on the last inserted record (which could be problematic with multithreaded apps) or is there maybe a more clever way to get the new value back at the same time you are making the insert?
Seems like there should be a way to get an Identity back based on the insert operation that was just made rather than having to query for it based on other means.
public void Insert(O obj)
{
var sqlCmd = new SqlCommand() { Connection = con.Conn };
var sqlParams = new SqlParameters(sqlCmd.Parameters, obj);
var props = obj.Properties.Where(o => !o.IsIdentity);
InsertQuery qry = new InsertQuery(this.TableAlias);
qry.FieldValuePairs = props.Select(o => new SqlValuePair(o.Alias, sqlParams.Add(o))).ToList();
sqlCmd.CommandText = qry.ToString();
sqlCmd.ExecuteNonQuery();
}
EDIT: While this question isn't a duplicate in the strictest manner, it's almost identical to this one which has some really good answers: Best way to get identity of inserted row?
It strongly depends on your database server. For example for Microsoft SQL Server you can get the value of the ##IDENTITY variable, that contains the last identity value assigned.
To prevent race conditions you must keep the insert query and the variable read inside a transaction.
Another solution could be to create a stored procedure for every type of insert you have to do and make it return the identity value and accept the insert arguments.
Otherwise, inside a transaction you can implement whatever ID assignment logic you want and be preserved from concurrency problems.
Afaik there is not finished way.
I solved by using client generated ids (guid) so that my method generated the id and returns it to the caller.
Perhaps you can analyse some SqlServer systables in order to see what has last changed. But you would get concurrency issues (What if someone else inserts a very similar record).
So I would recommend a strategy change and generate the id's on the clients
You can take a look at : this link.
I may add that to avoid the fact that multiple rows can exist, you can use "Transactions", make the Insert and the select methods in the same transaction.
Good luck.
The proper approach is to learn sql.
You can do a SQL command followed by a SELECT in one run, so you can go in and return the assigned identity.
See

LINQ to SQL Insert Sequential GUID

I have a database that is part of a Merge Replication scheme that has a GUID as it's PK. Specifically the Data Type is uniqueidentifier, Default Value (newsequentialid()), RowGUID is set to Yes. When I do a InsertOnSubmit(CaseNote) I thought I would be able to leave CaseNoteID alone and the database would input the next Sequential GUID like it does if you manually enter a new row in MSSMS. Instead it sends 00000000-0000-0000-0000-000000000000. If I add CaseNoteID = Guid.NewGuid(), the I get a GUID but not a Sequential one (I'm pretty sure).
Is there a way to let SQL create the next sequential id on a LINQ InsertOnSubmit()?
For reference below is the code I am using to insert a new record into the database.
CaseNote caseNote = new CaseNote
{
CaseNoteID = Guid.NewGuid(),
TimeSpentUnits = Convert.ToDecimal(tbxTimeSpentUnits.Text),
IsCaseLog = chkIsCaseLog.Checked,
ContactDate = Convert.ToDateTime(datContactDate.Text),
ContactDetails = memContactDetails.Text
};
caseNotesDB.CaseNotes.InsertOnSubmit(caseNote);
caseNotesDB.SubmitChanges();
Based on one of the suggestions below I enabled the Autogenerated in LINQ for that column and now I get the following error --> The target table of the DML statement cannot have any enabled triggers if the statement contains an OUTPUT clause without INTO clause.
Ideas?
In the Linq to Sql designer, set the Auto Generated Value property to true for that column.
This is equivalent to the IsDbGenerated property for a column. The only limitation is that you can't update the value using Linq.
From the top of the "Related" box on the right:
Sequential GUID in Linq-to-Sql?
If you really want the "next" value, use an int64 instead of GUID. COMB guid will ensure that the GUIDs are ordered.
In regards to your "The target table of the DML statement cannot have any enabled triggers if the statement contains an OUTPUT clause without INTO clause", check out this MS KB article, it appears to be a bug in LINQ:
http://support.microsoft.com/kb/961073
You really needed to do a couple of things.
Remove any assignment to the GUID type property
Change the column to autogenerated
Create a constraint in the database to default the column to NEWSEQUENTIALID()
Do insert on submit just like you were before.
On the insert into the table the ID will be created and will be sequential. Performance comparison of NEWSEQUENTIALID() vs. other methods
There is a bug in Linq2Sql when using an auto-generated (guid/sequential guid) primary key and having a trigger on the table.. that is what is causing your error. There is a hotfix for the problem:
http://support.microsoft.com/default.aspx?scid=kb;en-us;961073&sd=rss&spid=2855
Masstransit uses a combguid :
https://github.com/MassTransit/MassTransit/blob/master/src/MassTransit/NewId/NewId.cs
is this what you're looking for?
from wikipedia:
Sequential algorithms
GUIDs are commonly used as the primary key of database tables, and
with that, often the table has a clustered index on that attribute.
This presents a performance issue when inserting records because a
fully random GUID means the record may need to be inserted anywhere
within the table rather than merely appended near the end of it. As a
way of mitigating this issue while still providing enough randomness
to effectively prevent duplicate number collisions, several algorithms
have been used to generate sequential GUIDs. The first technique,
described by Jimmy Nilsson in August 2002[7] and referred to as a
"COMB" ("combined guid/timestamp"), replaces the last 6 bytes of Data4
with the least-significant 6 bytes of the current system date/time.
While this can result in GUIDs that are generated out of order within
the same fraction of a second, his tests showed this had little
real-world impact on insertion. One side effect of this approach is
that the date and time of insertion can be easily extracted from the
value later, if desired. Starting with Microsoft SQL Server version
2005, Microsoft added a function to the Transact-SQL language called
NEWSEQUENTIALID(),[8] which generates GUIDs that are guaranteed to
increase in value, but may start with a lower number (still guaranteed
unique) when the server restarts. This reduces the number of database
table pages where insertions can occur, but does not guarantee that
the values will always increase in value. The values returned by this
function can be easily predicted, so this algorithm is not well-suited
for generating obscure numbers for security or hashing purposes. In
2006, a programmer found that the SYS_GUID function provided by Oracle
was returning sequential GUIDs on some platforms, but this appears to
be a bug rather than a feature.[9]
You must handle OnCreated() method
Partial Class CaseNote
Sub OnCreated()
id = Guid.NewGuid()
End Sub
End Class

Categories

Resources