I have a WCF Data Service (OData) that serves as the data repository for a larger system. I'm trying to fire off specific methods based on operations on Entities in the repository.
Specifically, if someone changes a Message record, I want to hook into the pipeline. I'm using ChangeInterceptors for this.
They work for Add and Delete. However, nothing fires when an entity is updated. I am concerned that the DbContext can not resolve the fact that the entity has changed, since the request is stateless.
This does not trigger the handler:
var whatever = from m in Messages
where m.MessageKey == 3
select m;
whatever.First().UpdatedDate = DateTime.Now;
this.SaveChanges();
Has anyone else faced this problem?
So, I was trying to use AttachTo() to handle the fact that my record was detached. This flat out didn't work, and led to runtime exceptions like the following:
This operation requires the entity be of an Entity Type, and has at least one key
property.Parameter name: entity
At any rate, just use the update method and the change will be intercepted (and actually applied)
var whatever = (from m in Messages where m.MessageKey == 1
select m ).Single();
whatever.UpdatedDate = DateTime.Now;
this.UpdateObject(whatever);
this.SaveChanges();
Related
Currently have Elastic Apm setup with: app.UseAllElasticApm(Configuration); which is working correctly. I've just been trying to find a way to record exactly how many SQL Queries are run via Entity Framework for each transaction.
Ideally when viewing the Apm data in Kibana the metadata tab could just include an EntityFramework.ExecutedSqlQueriesCount.
Currently on .Net Core 2.2.3
One thing you can use is the Filter API for this.
With that you have access to all transactions and spans before they are sent to the APM Server.
You can't run through all the spans on a given transaction, so you need some tweaking - for this I use a Dictionary in my sample.
var numberOfSqlQueries = new Dictionary<string, int>();
Elastic.Apm.Agent.AddFilter((ITransaction transaction) =>
{
if (numberOfSqlQueries.ContainsKey(transaction.Id))
{
// We make an assumption here: we assume that all SQL requests on a given transaction end before the transaction ends
// this in practice means that you don't do any "fire and forget" type of query. If you do, you need to make sure
// that the numberOfSqlQueries does not leak.
transaction.Labels["NumberOfSqlQueries"] = numberOfSqlQueries[transaction.Id].ToString();
numberOfSqlQueries.Remove(transaction.Id);
}
return transaction;
});
Elastic.Apm.Agent.AddFilter((ISpan span) =>
{
// you can't relly filter whether if it's done by EF Core, or another database library
// but you have all sorts of other info like db instance, also span.subtype and span.action could be helpful to filter properly
if (span.Context.Db != null && span.Context.Db.Instance == "MyDbInstance")
{
if (numberOfSqlQueries.ContainsKey(span.TransactionId))
numberOfSqlQueries[span.TransactionId]++;
else
numberOfSqlQueries[span.TransactionId] = 1;
}
return span;
});
Couple of thing here:
I assume you don't do "fire and forget" type of queries, if you do, you need to handle those extra
The counting isn't really specific to EF Core queries, but you have info like db name, database type (mssql, etc.) - hopefully based on that you'll be able filter the queries you want.
With transaction.Labels["NumberOfSqlQueries"] we add a label to the given transction, and you'll be able to see this data on the transaction in Kibana.
In the TestFixtureTearDown-part of an NUnit test I try to delete some test-entities created in the TestFixtureSetUp-part. I use the following code
sessionFactory = NHibernateHelper.CreateSessionFactory(cssc["DefaultTestConnectionString"].ConnectionString);
uow = new NHibernateUnitOfWork(sessionFactory);
var g = reposGebruiker.GetByName(gebruiker.GebruikerNaam);
reposGebruiker.Delete(g);
var k = reposKlant.GetByName(klant.Naam);
reposKlant.Delete(k);
// Commit changes to persistant storage
uow.Commit();
However, after the commit, the two entities were still in the database. After searching on I came across this page on SO and so I added:
uow.Session.Flush();
However, still the entities remain in the DB. Does anyone have an idea as to why this is?
I've never used the UoW class you're using, but my projects are implemented using ISession.BeginTransaction and ISession.Transaction.Commit in a helper like this:
public void CreateContext(Action logic)
{
ISession.BeginTransaction();
logic();
ISession.Transaction.Commit();
}
And then:
CreateContext(() =>
Session.Delete(someObject));
This should work.
I want to mention that this is an example, and you'd want to make some abstractions.
How are the repositories created? In for the delete to succeed, the objects must be loaded in the same UoW (ISession) in which the Delete command is issued. The Delete method makes the objects non-persistent and marks them for deletion.
We are trying to use NHibernate 1.1 to as the persistence layer behind a web service API. Nothing new there. We use Automapper to translate the domain objects we get from (Fluent-)NHibernate to DTO's which we send over the wire.
The problem we are seeing is the following scenario:
We read an object from the repository
We translate that object (via Automapper) into the DTO form.
After some, perhaps trivial fiddling, the object is mapped back from DTO to the Fluent-Nhibernate object (including the primary key which cannot change).
We save the object.
After step 4 we get a Nonuniqueobjectexception from NHibernate and it creates a new row to save the object (with a new primary key). We want to update the original row but instead, new rows get modified(!)
So, in this situation, how can we convince NHibernate to do an actual update instead of an insert operation?
BTW, if we skip the translation to the DTO and back, there is no problem with the update.
== Tevya ==
1) NHibernate 1.1 or Fluent NHib 1.1?
2) I think you have a session management problem. If you load one object in session1 and try to persist it without attaching the actual object to session2 you will end up with a new row in the DB. Try to read and update the object within one session and see the results.
You could try merging the object into your session.
// Assuming you already have a session open
// and you've already mapped your DTO back
using (var tx = session.BeginTransaction())
{
var nhibernateObject = (YourNhibernateObjectType)session.Merge(mappedBackFromDTO);
tx.Commit();
}
You should attach your (new) object after mapping from DTO to the current ISession. Attaching is the operation made by ISession.Update method.
I have seen unit of work pattern implemented with something like a following code:
private HashSet<object> _newEntities = new HashSet<object>();
private HashSet<object> _updatedEntities = new HashSet<object>();
private HashSet<object> _deletedEntities = new HashSet<object>();
and then there are methods for adding entities to each of these HashSets.
On Commit UnitOfWork creates some Mapper instance for each entity and calls Insert, Update, Delete methods from some imaginary Mapper.
The problem for me with this approach is: the names of Insert, Update, Delete methods are hard-coded, so it seems such a UnitOfWork is capable only of doing simple CRUD operations. But what if I need the following usage:
UnitOfWork ouw = new UnitOfWork();
uow.Start();
ARepository arep = new ARepository();
BRepository brep = new BRepository();
arep.DoSomeNonSimpleUpdateHere();
brep.DoSomeNonSimpleDeleteHere();
uow.Commit();
Now the three-HashSet approach fails because I then I could register A and B entities only for Insert, Update, Delete operations but I need those custom operations now.
So it seems that I cannot always stack the Repository operations and then perform them all with UnitOfWork.Commit();
How to solve this problem? The first idea is - I could store addresses of methods
arep.DoSomeNonSimpleUpdateHere();
brep.DoSomeNonSimpleDeleteHere();
in UoW instance and execute them on uow.Commit() but then I have also to store all the method parameters. That sounds complicated.
The other idea is to make Repositories completely UoW-aware: In DoSomeNonSimpleUpdateHere I can detect that there is a UoW running and so I do not perform DoSomeNonSimpleUpdateHere but save the operation parameters and 'pending' status in some stack of the Repository instance (obviously I cannot save everything in UoW because UoW shouldn't depend on concrete Repository implementations). And then I register the involved Repository in the UoW instance. When UoW calls Commit, it opens a transaction, and calls some thing like Flush() for each pending Repository. Now every method of Repository needs some stuff for UoW detection and operation postponing for later Commit().
So the short question is - what is the easiest way to register all the pending changes in multiple repositories in UoW and then Commit() them all in a single transaction?
It would seem that even complicated updates can be broken down into a series of modifications to one or more DomainObjects. Calling DoSomeNonSimpleUpdateHere() may modify several different DomainObjects, which would trigger corresponding calls to UnitOfWork.registerDirty(DomainObject) for each object. In the sample code below, I have replaced the call to DoSomeNonSimpleUpdateHere with code that removes inactive users from the system.
UnitOfWork uow = GetSession().GetUnitOfWork();
uow.Start();
UserRepository repository = new UserRespository();
UserList users = repository.GetAllUsers();
foreach (User user in users)
{
if (!user.IsActive())
users.Remove( user );
}
uow.Commit();
If you are concerned about having to iterate over all users, here is an alternative approach that uses a Criteria object to limit the number of users pulled from the database.
UnitOfWork uow = GetSession().GetUnitOfWork();
uow.Start();
Repository repository = new UserRespository();
Criteria inactiveUsersCriteria = new Criteria();
inactiveUsersCriteria.equal( User.ACTIVATED, 0 );
UserList inactiveUsers = repository.GetMatching( inactiveUsersCriteria );
inactiveUsers.RemoveAll();
uow.Commit();
The UserList.Remove and UserList.RemoveAll methods will notify the UnitOfWork of each removed User. When UnitOfWork.Commit() is called, it will delete each User found in its _deletedEntities. This approach allows you to create arbitrarily complex code without having to write SQL queries for each special case. Using batched updates will be useful here, since the UnitOfWork will have to execute multiple delete statements instead of only one statement for all inactive users.
The fact that you have this problem suggests that you aren't using the Repository pattern as such, but something more like multiple table data gateways. Generally, a repository is for loading and saving an aggregate root. As such, when you save an entity, your persistence layer saves all the changes in that aggregate root entity instance's object graph.
If, in your code, you have roughly one "repository" per one table (or Entity), you're probably actually using a table data gateway or a data transfer object. In that case, you probably need to have a means of passing in a reference to the active transaction (or the Unit of Work) in each Save() method.
In Evans DDD book, he recommends leaving transaction control to the client of a repository, and I would agree that it's not a good practice, though it may be harder to avoid if you're actually using a table data gateway pattern.
I finally found this one:
http://www.goeleven.com/Blog/82
The author solves the problem using three Lists for update/insert/delete, but he does not store entities there. Instead repository delegates and their parameters are stored. So on Commit the author calls each registered delegate. With this approach I could register even some complex repository methods and so avoid using a separate TableDataGateway.
In my asp.net application, I open and close/flush the session at the beginning/ending of each request.
With this setup, I thought it would result in things like:
Entity e = EntityDao.GetById(1);
e.Property1 = "blah";
EntityDao.MakePersistant(e);
e = EntityDao.GetById(1);
e.Property1 // this won't be blah, it will be the old value since the request hasn't flushed
But I noticed that the value returned was the most recent updated value.
Someone responded that because of they way I have my identity setup?
Can someone explain this behaviour? So I don't need to call flush to ensure it is persisted to the db?
I belive (but could be mistaken) that nHibernate's caching and change-tracking is the reasons for this.
Since this Session instance is still active, nHibernate is tracking the changes you made to 'e'. When you ask for it again it includes those changes in the object returned. What happened when you called MakePersistant (which I assume calls Session.SaveOrUpdate(e)) is that it told that Session instance you are going to save those changes, thus it knows about them and will still show them when you call Session.Get(id) again.
If you instead started another Session and then called Session.Get(id) you wouldn't see those changes if you had not called Flush (or closed the Transaction, since you should be using a Transaction here) as it knows nothing of the changes made in the other Session.
To answer your other question, yes you still need to call Flush or close a Transaction to ensure changes are written to the database. One thing that is neat, is that you don't actually need to call SaveOrUpdate(e). For example this code will cause an Update to the database:
using (var session = SessionFactory.OpenSession())
using (var trans = session.BeginTransaction()){
var e = session.Get(id);
e.Name = "New Name";
trans.Commit();
}
nHibernate knows to update 'e' since it was tracking the changes that were made to during that Session. When I commit that transaction they are written. Note, this is the default behavior and I believe it can be changed if you want to require that .SaveOrUpdate() is called.