I encountered an issue but I'm not sure if it's a bug or a known behaviour (can't find anything).
So we use the Orleans project and in one of our grain in the state we store an EdmModel. The issue is that Orleans raise an error during the serialization of the EdmModel (using Newtonsoft). So I was like, ok, let's find out what I've done wrong when buidling the Edm (it's constructed "by hand" because we don't have the models (created by users)).
To check what was happening I try to serialize the Edm myself and encountered the following :
Self referencing loop detected for property 'DeclaringType' with type 'Microsoft.OData.Edm.EdmEntityType'. Path 'SchemaElements[0].DeclaredKey[0]'.
So I was like, ok. I've done something bad and it's why the serialisation is not working, But after commenting my code to narrow the issue, I was left with the following :
var model = new EdmModel();
try
{
var test = JsonConvert.SerializeObject(model);
}
catch (Exception e)
{
var poney = 1;
}
try
{
var test = JsonConvert.SerializeObject(model, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore });
}
catch (Exception e)
{
var poney = 2;
}
And it's still raising an error.
Self referencing loop detected for property 'Model' with type 'Microsoft.OData.Edm.Csdl.CsdlSemantics.CsdlSemanticsModel'. Path 'ReferencedModels[1].SchemaElements[0]'
And for the second try catch an OutOfMemory.
So I'm asking if there is a way to serialize an EdmModel to Json ? Because if it is what we can do is storing the EdmModel as string in the state of the grain and making serializaion/deserialization in the Getter/Settter.
Or maybe with Xml?
Thanks a lot for the help and the inputs :)
Related
In mvc 4 (but I think in all mvc version) I need to intercept a DbEntityValidationException.
In this phase I need to show the error that is in this object, in a simple log.
In the catch, I have this code:
foreach (var eve in EventityValidationErrors)
{
foreach(var ve in eve.ValidationErros)
{
Console.Writeline(ve.PropertyName,ve.ErrorMessage)
}
}
and it's very useful.
Now, my work is try to show this Exception info (only in develop phase) in a View.
Does someone know if there is a rapid way to do it? (ideally with external nuget component)
Elmah has a nuget package and gives a rapid way to present detailed errors.
You would need to change your catch just a little. That is catch your exception, create a new exception, which includes your data, and then throw this exception and let Elmah deal with it.
catch(DbEntityValidationException dbEx)
{
var sb = new StringBuilder();
foreach (var eve in dbEx.EntityValidationErrors)
{
foreach(var ve in eve.ValidationErrors)
{
sb.Append(ve.PropertyName);
sb.Append(ve.ErrorMessage); //format to your needs
}
}
var detailedException = new Exception(sb.ToString(), dbEx)
}
There is plenty of documentation on how to setup Elmah to catch all unhandled mvc exceptions.
I am trying to find the perfect way to handle this exception and force client changes to overwrite any other changes that caused the conflict. The approach that I came up with is to wrap the call to Session.Transaction.Commit() in a loop, inside the loop I would do a try-catch block and handle each stale object individually by copying its properties, except row-version property then refreshing the object to get latest DB data then recopying original values to the refreshed object and then doing a merge. Once I loop I will commit and if any other StaleObjectStateException take place then the same applies. The loop keeps looping until all conflicts are resolved.
This method is part of a UnitOfWork class. To make it clearer I'll post my code:
// 'Client-wins' rules, any conflicts found will always cause client changes to
// overwrite anything else.
public void CommitAndRefresh() {
bool saveFailed;
do {
try {
_session.Transaction.Commit();
_session.BeginTransaction();
saveFailed = false;
} catch (StaleObjectStateException ex) {
saveFailed = true;
// Get the staled object with client changes
var staleObject = _session.Get(ex.EntityName, ex.Identifier);
// Extract the row-version property name
IClassMetadata meta = _sessionFactory.GetClassMetadata(ex.EntityName);
string rowVersionPropertyName = meta.PropertyNames[meta.VersionProperty] as string;
// Store all property values from client changes
var propertyValues = new Dictionary<string, object>();
var publicProperties = staleObject.GetType().GetProperties();
foreach (var p in publicProperties) {
if (p.Name != rowVersionPropertyName) {
propertyValues.Add(p.Name, p.GetValue(staleObject, null));
}
}
// Get latest data for staled object from the database
_session.Refresh(staleObject);
// Update the data with the original client changes except for row-version
foreach (var p in publicProperties) {
if (p.Name != rowVersionPropertyName) {
p.SetValue(staleObject, propertyValues[p.Name], null);
}
}
// Merge
_session.Merge(staleObject);
}
} while (saveFailed);
}
The above code works fine and handle concurrency with the client-wins rule. However, I was wondering if there is any built-in capabilities in NHibernate to do this for me or if there is a better way to handle this.
Thanks in advance,
What you're describing is a lack of concurrency checking. If you don't use a concurrency strategy (optimistic-lock, version or pessimistic), StaleStateObjectException will not be thrown and the update will be issued.
Okay, now I understand your use case. One important point is that the ISession should be discarded after an exception is thrown. You can use ISession.Merge to merge changes between a detached a persistent object rather than doing it yourself. Unfortunately, Merge does not cascade to child objects so you still need to walk the object graph yourself. So the implementation would look something like:
catch (StaleObjectStateException ex)
{
if (isPowerUser)
{
var newSession = GetSession();
// Merge will automatically get first
newSession.Merge(staleObject);
newSession.Flush();
}
}
Since I felt adventurous the other day I decided compiling ActiveRecord 3 RC 1 with NHibernate 3.2 and see what happens.
Besides the breaking changes which I fixed I encountered a very strange behavior regarding SessionScopes and Linq queries.
Usually I don't have to use a session scope when using a Linq query but after I compiled ActiveRecord 3 RC 1 with NHibernate 3.2 I got the following error:
Could not found a registered Scope. Linq queries needs a underlying a scope to be functional.
Stack Trace: at Castle.ActiveRecord.Framework.ActiveRecordLinqBase`1.get_Queryable()
at Castle.ActiveRecord.Framework.ActiveRecordLinq.AsQueryable[T]()
at Danel.Nursing.Scheduling.Actions.DataServices.BranchDataService.GetBranches() in D:\Work\Default\Scheduling\Danel.Nursing.Scheduling.Actions\DataServices\BranchDataService.cs:line 21
at Danel.Nursing.Scheduling.Controllers.SmallHoursAmountController.<>c__DisplayClassb.<SetBranches>b__a() in D:\Work\Default\Scheduling\Danel.Nursing.Scheduling\Controllers\SmallHoursAmountController.cs:line 275
at Danel.Nursing.Scheduling.Viewlets.WaitForAction.Worker_DoWork(Object sender
DoWorkEventArgs e) in D:\Work\Default\Scheduling\Danel.Nursing.Scheduling\Viewlets\WaitForAction.cs:line 40
It seems that the error comes from here:
public class ActiveRecordLinqBase<T> : ActiveRecordBase<T>
{
public static IOrderedQueryable<T> Queryable
{
get
{
var activeScope = holder.ThreadScopeInfo.GetRegisteredScope(); // The registered scope is null...
if (activeScope == null)
throw new ActiveRecordException("Could not found a registered Scope. Linq queries needs a underlying a scope to be functional.");
var key = holder.GetSessionFactory(typeof(T));
var session = activeScope.IsKeyKnown(key) ? activeScope.GetSession(key) : SessionFactoryHolder.OpenSessionWithScope(activeScope, key);
return session.AsQueryable<T>();
}
}
}
What has changed that now I have to open a new SessionScope?
I had some trouble too with the Queryable function. Although I did not have the sessions scope problem, I had trouble saving update to objects retrieved by IQueryable. It seems that the new session was never registered with the active scope. I also changed the scope retrieval so maybe this also helps for you:
public new IOrderedQueryable<T> Queryable
{
get
{
ISessionFactory key = ActiveRecordMediator<T>.GetSessionFactoryHolder().GetSessionFactory(typeof(T));
ISessionScope activeScope = SessionScope.Current;
if (activeScope == null)
throw new ActiveRecordException("Could not found a registered Scope. Linq queries needs a underlying a scope to be functional.");
var session = activeScope.IsKeyKnown(key) ? activeScope.GetSession(key) : OpenSessionWithScope(activeScope, key);
if (!activeScope.IsKeyKnown(key))
{
activeScope.RegisterSession(key,session);
}
return session.AsQueryable<T>();
}
}
I've been struggling with the following issue for hours now. I tried this with different NHibernate/NHibernate.Search assemblies (3.0.0.4 / 2.1.2), all of them result in the same error. Used Lucene version is 2.9.2.2
All of them compiled from source.
NHibernate is setup to use NHibernate Search, configuration goes through Fluent NHibernate.
FluentConfiguration fc = Fluently.Configure()
. (mappings, db config, etc.)
.ExposeConfiguration
(
cfg =>
{
cfg.SetProperty("hibernate.search.default.directory_provider", typeof(FSDirectoryProvider).AssemblyQualifiedName);
cfg.SetProperty("hibernate.search.default.indexBase", "~/Index");
cfg.SetProperty("hibernate.search.default.indexBase.create", "true");
cfg.SetListener(NHibernate.Event.ListenerType.PostUpdate, new FullTextIndexEventListener());
cfg.SetListener(NHibernate.Event.ListenerType.PostInsert, new FullTextIndexEventListener());
cfg.SetListener(NHibernate.Event.ListenerType.PostDelete, new FullTextIndexEventListener());
}
);
So far so good, an index is created in the Index directory in the bin folder (segments.gen & segments_1 files).
After the configuration has been created I fetch the NHibernate Session and try to index something:
var _session = _factory.OpenSession();
using (ITransaction tx = _session.BeginTransaction())
{
var fts = Search.CreateFullTextSession(_session);
fts.PurgeAll(typeof(User));
var coll = fts.CreateCriteria<User>().List<User>();
foreach (var item in coll)
{
fts.Index(item);
}
tx.Commit();
}
This goes fine until the fts.PurgeAll or fts.Index is hit, which gives this error:
Object reference not set to an instance of an object.
Line 602: // TODO: Cache that at the FTSession level
Line 603: // not strictly necesary but a small optmization
Line 604: DocumentBuilder builder = searchFactoryImplementor.DocumentBuilders[clazz];
Line 605: if (builder != null)
Line 606: {
This error is thrown from the NHiberate.Search.dll, it looks like the SearchFactory was not initialized. The code that should create the SearchFactory returns null:
if (searchFactory == null)
{
searchFactory = ContextHelper.GetSearchFactory(session);
}
Came across several possible solutions where I need to initialize the SearchFactory with SearchFactory.Initialize, but no such method exists in my (2.0 / 3.0) NHibernate.Search assemblies.
NHibernate.Search.Search.CreateFullTextSession(_session)
.CreateFullTextQuery<User>("Firstname:Cees").List<User>();
Also throws a 'null exception' (of course), above is calling:
IDictionary<System.Type, DocumentBuilder> builders = searchFactory.DocumentBuilders;
Where searchFactory == null
There is a SearchFactoryImpl
SearchFactoryImpl searchFactory = NHibernate.Search.Impl.SearchFactoryImpl.GetSearchFactory(config);
Which return a SearchFactoryImpl instance, no idea what to do with it though...
Maybe I'm missing something? Any help is much appreciated.
Hmm, seems like Ninject had something to do with it. Not sure why/how. I've got a working solution with NH 3.0.1.4000 + Search + Lucene 2.9.2.2, if interested, send me an email.
http://ceesplug.nl/LuceneNHibernateTest.rar
Complete solution, working for NHibernate with and without FluentNHibernate.
Ayende has an article about how to implement a simple audit trail for NHibernate (here) using event handlers.
Unfortunately, as can be seen in the comments, his implementation causes the following exception to be thrown: collection xxx was not processed by flush()
The problem appears to be the implicit call to ToString on the dirty properties, which can cause trouble if the dirty property is also a mapped entity.
I have tried my hardest to build a working implementation but with no luck.
Does anyone know of a working solution?
I was able to solve the same problem using following workaround: set the processed flag to true on all collections in the current persistence context within the listener
public void OnPostUpdate(PostUpdateEvent postEvent)
{
if (IsAuditable(postEvent.Entity))
{
//skip application specific code
foreach (var collection in postEvent.Session.PersistenceContext.CollectionEntries.Values)
{
var collectionEntry = collection as CollectionEntry;
collectionEntry.IsProcessed = true;
}
//var session = postEvent.Session.GetSession(EntityMode.Poco);
//session.Save(auditTrailEntry);
//session.Flush();
}
}
Hope this helps.
The fix should be the following. Create a new event listener class and derive it from NHibernate.Event.Default.DefaultFlushEventListener:
[Serializable]
public class FixedDefaultFlushEventListener: DefaultFlushEventListener
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
protected override void PerformExecutions(IEventSource session)
{
if (log.IsDebugEnabled)
{
log.Debug("executing flush");
}
try
{
session.ConnectionManager.FlushBeginning();
session.PersistenceContext.Flushing = true;
session.ActionQueue.PrepareActions();
session.ActionQueue.ExecuteActions();
}
catch (HibernateException exception)
{
if (log.IsErrorEnabled)
{
log.Error("Could not synchronize database state with session", exception);
}
throw;
}
finally
{
session.PersistenceContext.Flushing = false;
session.ConnectionManager.FlushEnding();
}
}
}
Register it during NHibernate configuraiton:
cfg.EventListeners.FlushEventListeners = new IFlushEventListener[] { new FixedDefaultFlushEventListener() };
You can read more about this bug in Hibernate JIRA:
https://hibernate.onjira.com/browse/HHH-2763
The next release of NHibernate should include that fix either.
This is not easy at all. I wrote something like this, but it is very specific to our needs and not trivial.
Some additional hints:
You can test if references are loaded using
NHibernateUtil.IsInitialized(entity)
or
NHibernateUtil.IsPropertyInitialized(entity, propertyName)
You can cast collections to the IPersistentCollection. I implemented an IInterceptor where I get the NHibernate Type of each property, I don't know where you can get this when using events:
if (nhtype.IsCollectionType)
{
var collection = previousValue as NHibernate.Collection.IPersistentCollection;
if (collection != null)
{
// just skip uninitialized collections
if (!collection.WasInitialized)
{
// skip
}
else
{
// read collections previous values
previousValue = collection.StoredSnapshot;
}
}
}
When you get the update event from NHibernate, the instance is initialized. You can safely access properties of primitive types. When you want to use ToString, make sure that your ToString implementation doesn't access any referenced entities nor any collections.
You may use NHibernate meta-data to find out if a type is mapped as an entity or not. This could be useful to navigate in your object model. When you reference another entity, you will get additional update events on this when it changed.
I was able to determine that this error is thrown when application code loads a Lazy Propery where the Entity has a collection.
My first attempt involed watching for new CollectionEntries (which I've never want to process as there shouldn't actually be any changes). Then mark them as IsProcessed = true so they wouldn't cause problems.
var collections = args.Session.PersistenceContext.CollectionEntries;
var collectionKeys = args.Session.PersistenceContext.CollectionEntries.Keys;
var roundCollectionKeys = collectionKeys.Cast<object>().ToList();
var collectionValuesClount = collectionKeys.Count;
// Application code that that loads a Lazy propery where the Entity has a collection
var postCollectionKeys = collectionKeys.Cast<object>().ToList();
var newLength = postCollectionKeys.Count;
if (newLength != collectionValuesClount) {
foreach (var newKey in postCollectionKeys.Except(roundCollectionKeys)) {
var collectionEntry = (CollectionEntry)collections[newKey];
collectionEntry.IsProcessed = true;
}
}
However this didn't entirly solve the issue. In some cases I'd still get the exception.
When OnPostUpdate is called the values in the CollectionEntries dictionary should all already be set to IsProcessed = true. So I decided to do an extra check to see if the collections not processed matched what I expected.
var valuesNotProcessed = collections.Values.Cast<CollectionEntry>().Where(x => !x.IsProcessed).ToList();
if (valuesNotProcessed.Any()) {
// Assert: valuesNotProcessed.Count() == (newLength - collectionValuesClount)
}
In the cases that my first attempt fixed these numbers would match exactly. However in the cases where it didn't work there were extra items alreay in the dictionary. In my I could be sure these extra items also wouldn't result in updates so I could just set IsProcessed = true for all the valuesNotProcessed.