NHibernate Update Not working - c#

I can't get my update to work. The test fails and I do not see any update statements being sent to the database. Can someone tell me what I'm doing wrong?
This is my repository update procedure:
public void UpdateProject(Project proj)
{
Session.Update(proj);
}
This is the unit test I am trying:
[Test]
public void Can_Update_A_Project()
{
var project = _projects[0];
project.Name = "test project";
repository.UpdateProject(project);
var fromDb = repository.GetAProject(_projects[0].ID);
Assert.AreEqual(project.Name, fromDb.Name);
}
The test always fails. I see the test data being inserted and I see the select for the test.I don't see the update being performed. What am I missing?
Thanks!

There are a couple of things that may be happening.
1) The update is failing and NHibernate is raising an exception that is being swallowed somewhere - that can happen depending on how you've configured things. So in VS make sure all exceptions will cause a break.
2) The update is being cached and not written directly to the DB - you can force data to be written using Repository.Flush();
3) Are you sure _projects[0] has been read from the DB - I'm assuming that's happening in a TestSetup? If not NHibernate won't be aware of that as an object which is under its 'control'.
BTW - It's good practise to read the data you are going to change within the test itself, and then undo that change, resetting the DB to it's original state. In that way your test DB won't be altered by your testing.
BTW2 - In the above test, if project.Name has already been updated once, ie the test has run succesfully. Then next time around the test will suceed even if the update itself fails. A way to avoid this - append a DateTime to the project.Name, don't set it to a fixed value.

Another thing is this: when you save an entity using a session and you load the same entity from the same session using the entity's ID, you will get the same instance that you saved - regardless of whether any inserts or updates have been issued to the database.
That's because of NHibernate's 1st level cache, which is an identity map that belongs to the session.
If you want your test to check what was actually written to the database, you may do it like so:
session.Save(someEntity);
session.Flush(); // forces the entity to be inserted
session.Clear(); // clears the session's identity map, thus
// detaching someEntity from the session
var loadedEntity = session.Get<EntityType>(someEntity.Id);
// now you may compare the fields of someEntity and loadedEntity
// to verify that they were actually persisted

ISession.Update in NHibernate does not commit changes to the database. It is used to update transient instances in a different session from the one that was used to retrieve the instance (see here for details). Changes are sent to the database when a session is flushed. By default, sessions operate in FlushOnCommit mode, which means the changes to the objects will be sent to the database when the NHibernate transaction is committed (see here for details on the different flush modes).

try this may be i am wrong but its works
public void UpdateProject(Project proj)
{
ISessionFactory SessionFactory;
ISession session = SessionFactory.OpenSession();
using (ITransaction transaction = session.BeginTransaction())
{
session.Update(proj);
transaction.Commit();
}
}

FlushMode!
I'm maintaining an app I did not write and found out the hard way that even if you use a transaction and call txn.Commit() you still may not see the changes if your NHibernate Session has session.FlushMode = FlushMode.Never.
Very easy to detect. Set a breakpoint and look at session.FlushMode. Or just search all *.cs files for FlushMode.

I recently hit this same issue.
Inserts Worked
Updates Did Not
The issue was a flaw in the coding. Data was checked if the row was changed, then a new entity was mapped, and an update was sent, but nothing happened.
Solution: The actual row had to be queried from the database, then changes in C# applied to that row that was pulled back. Now NHibernate knows the row changed, run save and all worked.
public void SaveChanges()
{
_session.Flush();
_session.Transaction.Commit();
_session.BeginTransaction();
}

Related

How to deal with a stale cache in Entity Framework?

I had been getting very strange behavior form entity framework. I am coding a WebApi application so the objects I get from the browser are disconnected/detached. The data I get back is transactional such that it does not match any given table in the database. I have to do a number of lookups and data manipulation to get the actual updates to be done on the database.
The problem I seem to have is that in querying the data I am filling up the Tracked Changes cache. That wouldn't seem to be a problem to me since the true source of data should be the database. When I finally make the data changes and I call SaveChanges I get constraint errors. Here are my steps.
Query data.
Create rows to be inserted.
compare rows to db and make db changes.
After reviewing the data in Ctx.ChangeTracker.Entries() I found that an entry to be deleted was marked as Modified when it was supposed to be deleted. The way I worked around it was by Creating a new context for step 3. And it magically started working. I thought that was it, but in my test case I do a last read from the database to verify that my transaction was writing correctly. And I was getting an extra row that should already be deleted. And in fact was, when checking the db directly. Again a new context to do that last read fixed the problem.
I just assumed the default cache setting would just be used to track changes and not to speed up queries.
If I try to use AsNoTracking in my queries I also get into trouble because if I try to delete a row queried like that I get an error. And in my code I don't know if I am going to delete or modify until later on. Is there a way to clear the cache so I don't need to create a new context?
Is there a better way to deal with these issues?
EDIT:
AsNoTracking will do the trick, to some extent. I still found myself instantiating more copies of DbContext in order to prevent errors. Many to one entities have to be deleted in order or null foreign key errors are triggered.
var details = oldInvoice.details.ToList();
Context.Entry(oldInvoice).State = EntityState.Unchanged;
Context.Entry(oldInvoice).State = EntityState.Deleted;
details.ForEach(a => Context.Entry(a).State = EntityState.Deleted);
Entity Framework offers an exception DbUpdateConcurrencyException that you can catch on your calls to SaveChanges(). you could loop through the errors something like this:
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
// Get the current entity values and the values in the database
var entry = ex.Entries.Single();
var currentValues = entry.CurrentValues;
var databaseValues = entry.GetDatabaseValues();
// Choose an initial set of resolved values. In this case we
// make the default be the values currently in the database.
var resolvedValues = databaseValues.Clone();
// Have the user choose what the resolved values should be
HaveUserResolveConcurrency(currentValues, databaseValues,
resolvedValues);
// Update the original values with the database values and
// the current values with whatever the user choose.
entry.OriginalValues.SetValues(databaseValues);
entry.CurrentValues.SetValues(resolvedValues);
}
} while (saveFailed);
also, your update code sounds suspicious as well. Usually when you pass data out to a client through WebApi or other mechanisms, the data that is returned doesn't have the tracking data, so you should be checking to see if it exists and re-attaching it to the context and changing it's state to EntityState.Modified if so before calling SaveChanges().

Context.SubmitChanges() not updating despite having a PK

I am having an issue with the SubmitChanges function provided by the linq to DB implementation in C#. When I run the command, nothing throws an error but the record never gets updated. I have looked up the issue almost everyone says that it is in issue with the table nothing a primary key. However my table has a primary key assigned to it and yet SubmitChanges does not happen. To give you an overview of what I am executing, I here is a sample:
public void setApproval(string approvalCode, int ID)
{
using (DatabaseDataContext context = new DatabaseDataContext(DBConnection().getConnectionString()))
{
myRecord con = getRecord(ID); //Gets the record succesfully, PK field in tact
con.ApprovalStatus = approvalCode;
context.SubmitChanges();
}
}
As commented above, the record is successfully obtained with all the data in tact, including the PK field used to identify it. The database connection user is given the rights to update the table, though here I would expect it to break and complain.
Any ideas? Please let me know if I have not provided enough information. Any help is greatly appreciated!
You should get the object through context
public void setApproval(string approvalCode, int ID)
{
using (DatabaseDataContext context = new DatabaseDataContext(DBConnection().getConnectionString()))
{
myRecord con = context.TableName.First(item => item.ID == ID); //Gets the record succesfully, PK field in tact
con.ApprovalStatus = approvalCode;
context.SubmitChanges();
}
}
When you get the object via Context, it keep track of changes you make and then it save those changes on SubmitChanges
Where does getRecord(ID) get its context to return a record? It is not getting passed to the method, so I assume it is using a different context. SubmitChanges() would only see changes for the current context, not the context that getRecord(ID) used.
Are you checking to see if the data was updated with code or with an independent DB tool?
If in code, your read code is as suspect as the write code:
I was having similar issues when two applications with no common API were communicating through a database. The context is not a reflection of what is in the DB right now, and no amount of telling it to refresh is going to entirely fix the problem. If you need to inspect the database for something entered by another program or thread, you have to create a new database context object to inspect the database. The old database context object may still have the old data from before your most recent update.
Your getRecord function needs to create a new context or take the current context that you just edited as a parameter. If it uses a static or class level context it will not have the latest data.

WCF Data Services/Entity Framework : Logging client's username, Authentication, Authorization

I am using Entity Framework with WCF Data Services and I have the following table in my database :
Table Contract
Id (int)
Name (varchar)
byUser (varchar)
isDeleted (bit)
Entity Framework class
Id (int)
Name(string)
byUser(string)
isDeleted(boolean)
whenever the user is inserting/updating/deleting a contract(through a client app), I need to log who did the action.
so, I created Stored procedures for insert/update/delete that will receive the username from the client when an insertion/deletion/update is performed.
the issue is that the delete operation does not send over who is performing the operation :
var ctx = Context;
var contractToDelete = ctx.Contracts.Where(c => c.ContractId == 1).First();
contractToDelete.ByUser = username;
ctx.DeleteObject(contractToDelete);
ctx.SaveChanges();
at the server side, the byUser is always null.
Questions :
1) How do I make it so that the byUser parameter is sent to the server ?
2) Is there a better way to handle this kind of scenario ? (logging/authentication/authorization) with Entity Framework
It doesn't send null "always". It sends the old value always. That is some internal logic in entity framework. For each tracked object EF keeps both original and current values. When you are deleting object EF doesn't use current values - it uses original values (don't ask me why, simply this is how it works).
So you need to cheat EF:
var ctx = Context;
var contractToDelete = ctx.Contracts.Where(c => c.ContractId == 1).First();
contractToDelete.ByUser = username;
ctx.Contracts.ApplyOriginalValues(contractToDelete);
ctx.DeleteObject(contractToDelete);
ctx.SaveChanges();
Calling ApplyOriginalValues will force EF to override original values with values passed in parameter = you will override original values with current values.
In my opinion the better way is storing deleted records in separate table because it will avoid a lot of problems with passing isDeleted=false to every query where both eager and lazy loading will load deleted records as well. The only way to avoid problems with isDeleted is using conditional mapping but in such case you will not be able to load deleted records even if you want to unless you use stored procedures or direct SQL queries.
The way I managed this is, when my user logs in, I store basic information about them in the session. I then have a class that sits on top of my actions to context.
Whenever I commit back changes, I go through the same routine which checks what changed. I developed the ability to trigger actions based upon the entity being worked with (so I can keep an eye on something such as contracts). Then I have the user able to be logged.
[Edit]
This is tougher to clarify than I realised, but I'll try.
I'm creating a web application. Heavily using Ninject.
When the user logs in, I store their information in an IUserSession object (this is really held in Session, but a custom Ninject scope makes this neat for me and prevents me from having to expose my data layer to Web Session). This user session object contains username, user id etc.
I created a class that contains the context,and wraps all the SELECT,CREATE,DELETE and COMMIT calls. i.e. SELECT;
public IQueryable<TEntity> All<TEntity>( ) {
return Context.Set<TEntity>();
}
This class also has a Commit method, this is the call to SaveChanges.
Before calling SaveChanges, you have access to the changes in the form of Context.ChangeTracker.Entities
For each entity that has changed, you can test to see if it was added, deleted or modified.To get the type of the element being modified;
Type baseEntityType = ObjectContext.GetObjectType( entity.Entity.GetType( ) );
I do plan on writing up a tutorial soon, based upon my personal experience with doing this (not that that helps you right now).

Clean way to do an upsert (update or insert) operation in Dynamics CRM 4

Currently, I'm using the following code to perform an 'upsert' in CRM
try
{
crm.Create(c);
}
catch (SoapException)
{
crm.Update(c);
}
Am I right in assuming that this type of updating will wipe all existing information from my business entity? And if so, how can I get the entity to update from the repository? Do I need fetch, retrieve, or something like that?
Thanks.
Using exception handling for flow control is bad 99.99% of the time; in this case among other things because you never know what the actual reason for your SoapException is.
A much cleaner way would be to check whether your record's ID field has a value; if so, do an Update, if not, do a Create (and maybe add the resulting ID to your object if you use it further). (We've seen a Create on a record with an ID actually do an update years ago, but we've never been able to reproduce it.)
Other than that, #ckeller is perfectly right; an attribute that is null in your object (because it wasn't in the ColumnSet when retrieving from the database or hasn't been set otherwise) will not be touched in an Update.

How do you save a Linq object if you don't have its data context?

I have a Linq object, and I want to make changes to it and save it, like so:
public void DoSomething(MyClass obj) {
obj.MyProperty = "Changed!";
MyDataContext dc = new MyDataContext();
dc.GetTable<MyClass>().Attach(dc, true); // throws exception
dc.SubmitChanges();
}
The exception is:
System.InvalidOperationException: An entity can only be attached as modified without original state if it declares a version member or does not have an update check policy.
It looks like I have a few choices:
put a version member on every one of my Linq classes & tables (100+) that I need to use in this way.
find the data context that originally created the object and use that to submit changes.
implement OnLoaded in every class and save a copy of this object that I can pass to Attach() as the baseline object.
To hell with concurrency checking; load the DB version just before attaching and use that as the baseline object (NOT!!!)
Option (2) seems the most elegant method, particularly if I can find a way of storing a reference to the data context when the object is created. But - how?
Any other ideas?
EDIT
I tried to follow Jason Punyon's advice and create a concurrency field on on table as a test case. I set all the right properties (Time Stamp = true etc.) on the field in the dbml file, and I now have a concurrency field... and a different error:
System.NotSupportedException: An attempt has been made to Attach or Add an entity that is not new, perhaps having been loaded from another DataContext. This is not supported.
So what the heck am I supposed to attach, then, if not an existing entity? If I wanted a new record, I would do an InsertOnSubmit()! So how are you supposed to use Attach()?
Edit - FULL DISCLOSURE
OK, I can see it's time for full disclosure of why all the standard patterns aren't working for me.
I have been trying to be clever and make my interfaces much cleaner by hiding the DataContext from the "consumer" developers. This I have done by creating a base class
public class LinqedTable<T> where T : LinqedTable<T> {
...
}
... and every single one of my tables has the "other half" of its generated version declared like so:
public partial class MyClass : LinqedTable<MyClass> {
}
Now LinqedTable has a bunch of utility methods, most particularly things like:
public static T Get(long ID) {
// code to load the record with the given ID
// so you can write things like:
// MyClass obj = MyClass.Get(myID);
// instead of:
// MyClass obj = myDataContext.GetTable<MyClass>().Where(o => o.ID == myID).SingleOrDefault();
}
public static Table<T> GetTable() {
// so you can write queries like:
// var q = MyClass.GetTable();
// instead of:
// var q = myDataContext.GetTable<MyClass>();
}
Of course, as you can imagine, this means that LinqedTable must somehow be able to have access to a DataContext. Up until recently I was achieving this by caching the DataContext in a static context. Yes, "up until recently", because that "recently" is when I discovered that you're not really supposed to hang on to a DataContext for longer than a unit of work, otherwise all sorts of gremlins start coming out of the woodwork. Lesson learned.
So now I know that I can't hang on to that data context for too long... which is why I started experimenting with creating a DataContext on demand, cached only on the current LinqedTable instance. This then led to the problem where the newly created DataContext wants nothing to do with my object, because it "knows" that it's being unfaithful to the DataContext that created it.
Is there any way of pushing the DataContext info onto the LinqedTable at the time of creation or loading?
This really is a poser. I definitely do not want to compromise on all these convenience functions I've put into the LinqedTable base class, and I need to be able to let go of the DataContext when necessary and hang on to it while it's still needed.
Any other ideas?
Updating with LINQ to SQL is, um, interesting.
If the data context is gone (which in most situations, it should be), then you will need to get a new data context, and run a query to retrieve the object you want to update. It's an absolute rule in LINQ to SQL that you must retrieve an object to delete it, and it's just about as iron-clad that you should retrieve an object to update it as well. There are workarounds, but they are ugly and generally have lots more ways to get you in trouble. So just go get the record again and be done with it.
Once you have the re-fetched object, then update it with the content of your existing object that has the changes. Then do a SubmitChanges() on the new data context. That's it! LINQ to SQL will generate a fairly heavy-handed version of optimistic concurrency by comparing every value in the record to the original (in the re-fetched) record. If any value changed while you had the data, LINQ to SQL will throw a concurrency exception. (So you don't need to go altering all your tables for versioning or timestamps.)
If you have any questions about the generated update statements, you'll have to break out SQL Profiler and watch the updates go to the database. Which is actually a good idea, until you get confidence in the generated SQL.
One last note on transactions - the data context will generate a transaction for each SubmitChanges() call, if there is no ambient transaction. If you have several items to update and want to run them as one transaction, make sure you use the same data context for all of them, and wait to call SubmitChanges() until you've updated all the object contents.
If that approach to transactions isn't feasible, then look up the TransactionScope object. It will be your friend.
I think 2 is not the best option. It's sounding like you're going to create a single DataContext and keep it alive for the entire lifetime of your program which is a bad idea. DataContexts are lightweight objects meant to be spun up when you need them. Trying to keep the references around is also probably going to tightly couple areas of your program you'd rather keep separate.
Running a hundred ALTER TABLE statements one time, regenerating the context and keeping the architecture simple and decoupled is the elegant answer...
find the data context that originally created the object and use that to submit changes
Where did your datacontext go? Why is it so hard to find? You're only using one at any given time right?
So what the heck am I supposed to attach, then, if not an existing entity? If I wanted a new record, I would do an InsertOnSubmit()! So how are you supposed to use Attach()?
You're supposed to attach an instance that represents an existing record... but was not loaded by another datacontext - can't have two contexts tracking record state on the same instance. If you produce a new instance (ie. clone) you'll be good to go.
You might want to check out this article and its concurrency patterns for update and delete section.
The "An entity can only be attached as modified without original state if it declares a version member" error when attaching an entitity that has a timestamp member will (should) only occur if the entity has not travelled 'over the wire' (read: been serialized and deserialized again). If you're testing with a local test app that is not using WCF or something else that will result in the entities being serialized and deserialized then they will still keep references to the original datacontext through entitysets/entityrefs (associations/nav. properties).
If this is the case, you can work around it by serializing and deserializing it locally before calling the datacontext's .Attach method. E.g.:
internal static T CloneEntity<T>(T originalEntity)
{
Type entityType = typeof(T);
DataContractSerializer ser =
new DataContractSerializer(entityType);
using (MemoryStream ms = new MemoryStream())
{
ser.WriteObject(ms, originalEntity);
ms.Position = 0;
return (T)ser.ReadObject(ms);
}
}
Alternatively you can detach it by setting all entitysets/entityrefs to null, but that is more error prone so although a bit more expensive I just use the DataContractSerializer method above whenever I want to simulate n-tier behavior locally...
(related thread: http://social.msdn.microsoft.com/Forums/en-US/linqtosql/thread/eeeee9ae-fafb-4627-aa2e-e30570f637ba )
You can reattach to a new DataContext. The only thing that prevents you from doing so under normal circumstances is the property changed event registrations that occur within the EntitySet<T> and EntityRef<T> classes. To allow the entity to be transferred between contexts, you first have to detach the entity from the DataContext, by removing these event registrations, and then later on reattach to the new context by using the DataContext.Attach() method.
Here's a good example.
When you retrieve the data in the first place, turn off object tracking on the context that does the retrieval. This will prevent the object state from being tracked on the original context. Then, when it's time to save the values, attach to the new context, refresh to set the original values on the object from the database, and then submit changes. The following worked for me when I tested it.
MyClass obj = null;
using (DataContext context = new DataContext())
{
context.ObjectTrackingEnabled = false;
obj = (from p in context.MyClasses
where p.ID == someId
select p).FirstOrDefault();
}
obj.Name += "test";
using (DataContext context2 = new ())
{
context2.MyClasses.Attach(obj);
context2.Refresh(System.Data.Linq.RefreshMode.KeepCurrentValues, obj);
context2.SubmitChanges();
}

Categories

Resources