Someone on our data team added a database constraint and, while it's perfectly valid and desirable, it creates great problems for NHibernate because there doesn't seem to be a way to override NHibernate's save order.
Given a (silly example) class like this:
public Person
{
public virtual string FirstName { get; set; }
public virtual bool IsCurrent { get; set; }
}
and a constraint that only one record in the backing table can be IsCurrent=true at the same time . . .
If I try to "deprecate" an existing record by setting IsCurrent=false, and replace it with a new record with IsCurrent=true, I get an ADO exception on Save because NHibernate tries to perform the Insert first, violating the SQL Server constraint that only one record can be IsCurrent=true at once.
I see two options:
Can SQL Server be configured to check constraints only at the end of a transaction? The following statement (the "update" of the old row to IsCurrent=false would un-break the constraint.
Can NHibernate's save order (for instances of the same type) be
modified or "hinted" in any way?
Thanks!
Jeff
Either approach is possible; I would lean toward #2. If you call:
session.saveOrUpdate(person1);
session.flush();
session.saveOrUpdate(person2);
The flush will push the SQL statement to the database. I believe this will fix your problem. (The above is java Hibernate code, your syntax may vary slightly).
The problem here is that NHibernate is not aware of all the data intergity checks in the database layer.
Your option 1 is possible if you hack SQL server and disable constraints for a (short) period when you manupulate data. But it is a dirty solution since constraints are disabled for all the transaction being processed at that time.
In this particular case I would use anonter approach:
There are no integrity checks. Data integridy is based on firing trigger on insert or update. The trigger is responsible for setting IsCurrent to false for all relevant records except the record beung currently inserted or updated. Of course, you have to deal with recursive trigger firing since withing the trigger you are modifying records in the same table where trigger was fired.
Related
I am updating a column in a SQL table and I want to check if it was updated successfully or it was updated already and my query didn't do anything
as we get ##rowcount in SQL Server.
In my case, I want to update a column named lockForProcessing, so if it is already processing, then my query would not affect any row, it means someone else is already processing it, else I would process it.
If I understand you correctly, your problem is related to a multi threading / concurrency problem, where the same table may be updated simultaneously.
You may want to have a look at the :
Chapter 11. Transactions And Concurrency
The ISession is not threadsafe!
The entity is not stored the moment the code session.SaveOrUpdate() is executed, but typically after transaction.Commit().
stored and commited are two different things.
The entity is stored after any session.Flush(). Depending on the IsolationLevel, the entity won't be seen by other transactions.
The entity is commited after a transaction.Commit(). A commit also flushes.
Maybe all you need to do is choose the right IsolationLevel when beginning transactions and then read the table row to get the current value:
using (var transaction = session.BeginTransaction(IsolationLevel.Serializable))
{
session.Get(); // Read your row
transaction.Commit();
}
Maybe it is easier to create some locking or pipeline mechanism in your application code though. Without knowing more about who is accessing the database (other transactions, sessions, processes?) it is hard to answer more precisely.
For a table that has an identity:
[AutoIncrement]
public int Id { get; set;}
When inserting a new row into the database, what is the best way to retrieve the Id of the object?
For example:
db.Insert<> (new User());
The value of the Id is 0 after the insert, but in the database this obviously is not the case. The only possibility I can see is the following:
Id = (int)db.GetLastInsertId();
However I don't believe this would be a safe call to make. If there are 100's of inserts happening at the same time, an Id for another insert may be returned. In EF when you do an insert the Id is set for you.
Does anyone know the best way to go about this?
In ServiceStack.OrmLite v4 which defaults to using parameterized queries there are a couple of options in db.Save() which automatically populates the AutoIncrement Id, e.g:
db.Save(item);
item.Id //populated with the auto-incremented id
Otherwise you can select the last insert id using:
var itemId = db.Insert(item, selectIdentity:true);
Here are more examples showcasing OrmLite's new API's.
For OrmLite v3
The correct call is db.GetLastInsertId() which for SQL Server under the hood for example calls SELECT SCOPE_IDENTITY() which returns the last inserted id for that connection.
This is safe because all the other concurrent inserts that might be happening are using different DB connections. In order for anyone else to use the same connection it needs to be disposed of and released back into the pool.
You should definitely using the Unit of Work pattern, particularly in this scenarios, you wrap the db related codes in a transaction scope.
In ormLite, you can implement this via IDbCommand and IDbTransaction (see example here http://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.OrmLite/ServiceStack.OrmLite.Tests/ShippersExample.cs)
Looking at the code, you'll notice it's going to be less magical and more manual coding, but it's one way.
Update: As seen here, if you are using ServiceStack/ORMLite v4, you need to utilize the parameterized query to get the inserted ID. For example:
var UserId = db.Insert<User>(new User(), selectIdentity: true);
so, I'm facing the challenge of having to log the data being changed for each field in a table. Now I can obviously do that with triggers (which I've never used before, but I can imagine is not that difficult), but I also need to be able to link the log who performed the change which is where the problem lies. The trigger wouldn't be aware of who is performing the change and I can't pass in a user id either.
So, how can I do what I need to do? If it helps say I have these tables:
Employees {
EmployeeId
}
Jobs {
JobId
}
Cookies {
CookieId
EmployeeId -> Employees.EmployeeId
}
So, as you can see I have a Cookies table which the application uses to verify sessions, and I can infer the user out of it, but again, I can't make the trigger be aware of it if I want to make changes to the Jobs table.
Help would be highly appreciated!
We use context_info to set the user making the calls to the DB. Then our application level security can be enforced all the way to in DB code. It might seem like an overhead, but really there is no performance issue for us.
make_db_call() {
Set context_info --some data representing the user----
do sql incantation
}
in db
select #user = dbo.ParseContextInfo()
... audit/log/security etc can determine who....
To get the previous value inside the trigger you select from the 'deleted' pseudo table, and to the get the values you are putting in you select from th 'inserted' pseudo table.
Before you issue linq2sql query issue the command like this.
context.ExecuteQuery('exec some_sp_to_set_context ' + userId')
Or more preferably I'd suggest an overloaded DataContext, where the above is executed before eqch query. See here for an example.
we don't use multiple SQL logins as we rely on the connection pooling and also locking down the db caller to a limited user.
As I am developing my database, I am working to ensure data integrity. So, for example, a Book should be deleted when its Author is deleted from the database (but not vice-versa), assuming one author.
When I setup the foreign-key, I did set up a CASCADE, so I feel like this should happen automatically if I perform a delete from LINQ. Is this true? If not, do I need to perform all the deletes on my own, or how is this accomplished?
My second question, which goes along with that, is: does the database ensure that I have all the appropriate information I need for a row when I add it to the table (e.g. I can't add a book that doesn't have an author), or do I need to ensure this myself in the business logic? What would happen if I did try to do this using LINQ to SQL? Would I get an exception?
Thanks for the help.
A cascading foreign key will cascade the delete automatically for you.
Referencial integrity will be enforced by the database; in this case, you should add the Author first and then the Book. If you violate referencial integrity, you will get an exception.
It sounds like for second question you may be interested in using a transaction. For example, you need to add several objects to the database and want to make sure all get added or none. This is what a database transaction accomplishes. And, yes you should do this in your data/business layer, you can do this by adding partial class to your datacontext classes. If your business process states that for example EVERY user MUST have ADDRESS or something to that nature. This is up to your case scenario.
LINQ automatically uses transactions provided you are within a single (using), i.e you perform everything in that one step.
If you need to perform multiple steps or combine with non LINQ database action then you can use the transaction scope. You need to enable DISTRIBUTED TRANSACTION SERVICE. This allows transactions across for example files and database.
See TransactionScope
using (TransactionScope scope = new TransactionScope())
{
do stuff here
scope.Complete
}
To eliminate the potential problems with triggers, what are some of the alternatives one may use to get the same functionality of reacting to an event fired on a INSERT action?
I have a database that needs to have some additional values added on insert. The INSERT is controlled by compiled code and cannot be changed.
EXAMPLE: The program inserts a string and from this string I need to supply an integer to a new field that points to a look-up table.
If there is an alternative to a trigger then please let me know some pros and cons to any alternative. The main reason for this is that Triggers are not allowed in our DB standards.
SQL Server 2008 Enterprise
Alternatives to plain-old inserts can be done using stored procedures, triggers, or more complicated insert statements. Since you have no control over the insert statements, you won't be able to use stored procedures, either. So your only option is triggers.
What you're describing is precisely why triggers exist. If you need to accomplish this task then it can't be done under the constraints you've listed.
Triggers are the best option. Get the DB standards changed (or at least allow this task to be an exception) because they are flawed.
How do you determine your integer, based on the string being inserted?
One alternative you might want to look into are computed columns in SQL Server. If that matching is a pretty straightforward one (e.g. extract the character 10 through 14 from the string) or something like that, you could create a computed column to do so automagically - no trigger needed.
You can even make those computed columns persisted (physically stored as part of your tables) and create indices on these fields!
Computed columns are available from SQL Server 2000 on, persisted columns from SQL Server 2005.
I know that was asked a long time ago. With SQL Server 2008 "Change Data Capture" MSDN was introduced. Another alternative, but only valid after 2008 R2 is "Change Tracking" Setting up change tracking. While you can query rows to filter (look here) what was changed, this may or may not "resolve" the issues with triggers.
Triggers are the way to perform an action after an event (insert, update, delete) occurs on a SQL table; the fact that they exist makes it unlikely that there's any tenable alternative. It's unfortunate to say, but the DB standards you say are in place effectively prevent you from doing what you want without having some process running that periodically watches your table and then performs the action you need it to, or changing all your database CrUD operations to go through stored procedures which do what you want them to. Since you say that the latter isn't possible -- you can't change the INSERT statements -- then you're left with just triggers.
SQL Server 2005 now has something called an OUTPUT clause that can do additional processing after an INSERT (or other action) occurs. This article covers more of the details. For instance, if you need to do processing after an INSERT command, you could do something like...
INSERT INTO Contact
(FirstName, MiddleName, LastName)
OUTPUT INSERTED.ContactID, INSERTED.FirstName, INSERTED.MiddleName, INSERTED.LastName
INTO Contact_Audit
VALUES
(##SCOPE_IDENTITY, 'Joe', 'D.', 'Schmoe')
And you'd have your uniquely created ID for them available.
If you want to use data history then go with system version history tables. you don't need to create trigger explicitly.
https://learn.microsoft.com/en-us/sql/relational-databases/tables/temporal-tables?view=sql-server-ver15
Your options are limited here. I think your only other alternative is to do your inserts via a stored procedure call and put the extra code in the stored procedure.
I think we can implement trigger with hibernate event system nowdays despite of the performance impact.I didn't do that before.but I think it works