Using the method described in NHibernate & INotifyPropertyChanged, the repository will return a collection of proxies that implement INotifyPropertyChanged, but on some objects when saving or deleting it will throw an error:
at NHibernate.Impl.SessionFactoryImpl.GetEntityPersister(String entityName)
at NHibernate.Impl.SessionImpl.GetEntityPersister(String entityName, Object obj)
at NHibernate.Engine.ForeignKeys.IsTransient(String entityName, Object entity, Nullable`1 assumed, ISessionImplementor session)
at NHibernate.Event.Default.AbstractSaveEventListener.GetEntityState(Object entity, String entityName, EntityEntry entry, ISessionImplementor source)
at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.PerformSaveOrUpdate(SaveOrUpdateEvent event)
at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.OnSaveOrUpdate(SaveOrUpdateEvent event)
at NHibernate.Impl.SessionImpl.FireSaveOrUpdate(SaveOrUpdateEvent event)
at NHibernate.Impl.SessionImpl.SaveOrUpdate(Object obj)
at MyCode ...
I figured out that if I create the session without the interceptor the SaveOrUpdate works fine, but with the interceptor it errors.
with the interceptor:
public ISession GetSession(ISessionFactory factory)
{
IInterceptor dataBinding = new DataBindingInterceptor {SessionFactory = factory};
return factory.OpenSession(dataBinding);
}
without
public ISession GetSession(ISessionFactory factory)
{
return factory.OpenSession();
}
I'm at a loss for how to go about even figuring out why the interceptor would break the save.
The only change I made to the code was changing the line
Type type = Type.GetType(clazz);
to
Type type = FindType(clazz);
public Type FindType(string typeName)
{
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
Type foundType = assembly.GetType(typeName);
if (foundType != null)
return foundType;
}
return null;
}
Solution was to always use the session with the interceptor. I was creating the IList with the interceptor, but saving with a generic session. This bypassed the GetEntityName override which redirected the proxy to the correct persister.
Related
I am able to serialize proxy objects using below code:
public class NHibernateContractResolver : DefaultContractResolver
{
protected override JsonContract CreateContract(Type objectType)
{
if (typeof(NHibernate.Proxy.INHibernateProxy).IsAssignableFrom(objectType))
return base.CreateContract(objectType.BaseType);
return base.CreateContract(objectType);
}
}
But how can I make JSON.NET ignore the NHibernate Proxy objects during serialization.
The problem I am facing is that, the parent object is fetching 1000's of child object, where as I want to send JSON only for the parent object, so I want to ignore proxy object and fetch only eager loaded relationships.
And if I comment above code, then I get the error for JSON.NET failing to serialize the proxy objects.
Please help!
write a dummy class like this.
public class NhProxyJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteNull();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return typeof(INHibernateProxy).IsAssignableFrom(objectType);
}
}
My session uses an NHInterceptor to add INotifyPropertyChanged support to models.
// I use the session generated here to fetch Data
public class SessionServiceImpl : ISessionService
{
[Inject]
public ISessionFactory SessionFactory { get; set; }
[Inject]
public NhChangeNotificationInterceptorImpl ChangeNotificationInterceptor { get; set; }
public ISession GetSession() // reduced code here
{
return SessionFactory.OpenSession(ChangeNotificationInterceptor);
}
}
// This is the interceptor implementation
public class NhChangeNotificationInterceptorImpl : EmptyInterceptor, IInterceptor
{
[Inject]
public ISessionFactory SessionFactory { get; set; }
[Inject]
public ViewModelProxyFactory ProxyFactory { get; set; }
public override object Instantiate(string entityTypeName, EntityMode entityMode, object id)
{
Type type = Type.GetType(entityTypeName);
if (type == null) { /* Throw Exception*/ }
bool isViewModel = false;
while (type != typeof(object))
{
Type tempType = type.BaseType;
if (tempType == typeof(ViewModelBase))
{
isViewModel = true;
break;
}
}
if (entityMode == EntityMode.Poco && isViewModel)
{
var instance = ProxyFactory.CreateProxy(type);
SessionFactory.GetClassMetadata(entityTypeName).SetIdentifier(instance, id, entityMode);
return instance;
}
return base.Instantiate(entityTypeName, entityMode, id);
}
}
The ProxyFactory uses Castle to create proxies that add change notification functionality. That means all my objects come from the DB as Castle Proxies, which are AFAIK transparent.
Whenever I pass one of those NH Generated MVVM-proxies into Session.Save(), all's fine.
Now, as data driven Applications go, I also need to create new instances and save them. I can create instances of the model type and save them via the session all right. Creating a MVVM proxy instance (using Ninject to ensure that the same SessionFactory and ProxyFactory instances are used all over) and throwing this into Session.Save() results in the following:
"NHibernate.MappingException".
Message=No persister for: Castle.Proxies.FieldDescriptionProxy
Source=NHibernate
StackTrace:
at NHibernate.Impl.SessionFactoryImpl.GetEntityPersister(String entityName)
at NHibernate.Impl.SessionImpl.GetEntityPersister(String entityName, Object obj)
at NHibernate.Event.Default.AbstractSaveEventListener.SaveWithGeneratedId(Object entity, String entityName, Object anything, IEventSource source, Boolean requiresImmediateIdAccess)
at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.SaveWithGeneratedOrRequestedId(SaveOrUpdateEvent event)
at NHibernate.Event.Default.DefaultSaveEventListener.SaveWithGeneratedOrRequestedId(SaveOrUpdateEvent event)
at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.EntityIsTransient(SaveOrUpdateEvent event)
at NHibernate.Event.Default.DefaultSaveEventListener.PerformSaveOrUpdate(SaveOrUpdateEvent event)
at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.OnSaveOrUpdate(SaveOrUpdateEvent event)
at NHibernate.Impl.SessionImpl.FireSave(SaveOrUpdateEvent event)
at NHibernate.Impl.SessionImpl.Save(Object obj)
at Interpretation.UI.Service.Impl.Dao.FieldDao.SaveFields(IList`1 fields, ISession session) in C:\...\FieldDao.cs:Zeile 51.
InnerException:
Any ideas what goes wrong here (or what I might have forgotten)?
EDIT : Now got it working, but why do I have to add recognition logic (see code below) to the interceptor for instances created outside, while instances created inside can be persisted as the are?
public override string GetEntityName(object entity)
{
Type type = entity.GetType();
if (type.FullName.StartsWith("Castle.Proxies") &&
type.FullName.EndsWith("Proxy"))
{
return type.BaseType.FullName;
}
return base.GetEntityName(entity);
}
Implementing the GetEntityName method did the trick.
public override string GetEntityName(object entity)
{
Type type = entity.GetType();
if (type.FullName.StartsWith("Castle.Proxies") &&
type.FullName.EndsWith("Proxy"))
{
return type.BaseType.FullName;
}
return base.GetEntityName(entity);
}
article and sample code(Intercepting Entity Creation) demonstrating the use of dynamic proxies to implement (WPF) change notification. As seen there you'll have to use the same proxy generator in NHibernate and outside to implement the recognition of the proxies for NHibernate (see class DataBindingIntercepter method GetEntityName) .
why do I have to add recognition logic (see code below) to the interceptor for instances created outside, while instances created inside can be persisted as the are?
Methods on ISession only add instances to dictionary to search for dirty ones on Flush. Since all instances loaded are automaticly part of this dictionary the session immediatly "knows" the entity and does nothing in SaveOrUpdate. For other instances coming from outside it first has to get the appropriate mapping (using the entitiyname which defaults to the class fullname) to know which properties form the primary key.
We have a project that uses an Interceptor object to tell NHibernate to do some general works before saving an entity.This interceptor has a single task to do.Now there's another task that should be added to this interceptor (NHibernate doesn't support multiple interceptors) but I don't want to make this interceptor complicated instead I would like to use composition pattern that will manage all registered interceptors.something like this :
public bool Onload(object entity,object id,object[] state,string propertyNames,IType[] types_
{
var result=false;
foreach(var interceptor in _registeredInterceptors)
result=result || interceptor.OnLoad(entity,id,state,propertyNames,types);
return result;
}
public bool OnFlushDirty(object entity,object id,object[] state,string propertyNames,IType[] types_
{
var result=false;
foreach(var interceptor in _registeredInterceptors)
result=result || interceptor.OnFlushDirty(entity,id,state,propertyNames,types);
return result;
}
by looking at this code I realized that there might be a better way preventing me from repeating myself.
the question is can I make this code more simpler and abstract using Lambda expressions and yield keyword?
One way of doing it could look like this:
public bool Execute(IList<IInterceptor> interceptors, Func<IInterceptor, bool> func)
{
bool result = false;
foreach (IInterceptor interceptor in interceptors)
{
result = result || func(interceptor);
}
return result;
}
And in the parent interceptor:
public bool Onload(object entity, object id, object[] state, string propertyNames, IType[] types_
{
return Execute(_registeredInterceptors, x => x.OnLoad(entity, id, state, propertyNames, types);
}
public bool OnFlushDirty(object entity, object id, object[] state, string propertyNames, IType[] types_
{
return Execute(_registeredInterceptors, x => x.OnFlushDirty(entity, id, state, propertyNames, types);
}
Update
If you want the result type to be generic you could do it like this:
public static T Execute<T>(IList<IInterceptor> interceptors, Func<IInterceptor, T> func)
{
T result = default(T);
foreach (IInterceptor interceptor in interceptors)
{
// your logic based on type T
}
return T;
}
Execution of the generic version would look exactly the same as of the bool one because of Type inference.
Is this what you had in mind?
Instantiating an unregistered service with known services (injecting them via ctr).
I want to avoid container pollution.
Here is another way to resolve unregistered concrete types from container. Note that all autofac constructor finding and selecting logic, all registration event handlers remain in force.
First, you define this method:
public static object ResolveUnregistered(this IComponentContext context, Type serviceType, IEnumerable<Parameter> parameters)
{
var scope = context.Resolve<ILifetimeScope>();
using (var innerScope = scope.BeginLifetimeScope(b => b.RegisterType(serviceType)))
{
IComponentRegistration reg;
innerScope.ComponentRegistry.TryGetRegistration(new TypedService(serviceType), out reg);
return context.ResolveComponent(reg, parameters);
}
}
The idea is that you get component registration from derived context and resolve it in the current context.
Then you can create some handy overloads:
public static object ResolveUnregistered(this IComponentContext context, Type serviceType)
{
return ResolveUnregistered(context, serviceType, Enumerable.Empty<Parameter>());
}
public static object ResolveUnregistered(this IComponentContext context, Type serviceType, params Parameter[] parameters)
{
return ResolveUnregistered(context, serviceType, (IEnumerable<Parameter>)parameters);
}
public static TService ResolveUnregistered<TService>(this IComponentContext context)
{
return (TService)ResolveUnregistered(context, typeof(TService), Enumerable.Empty<Parameter>());
}
public static TService ResolveUnregistered<TService>(this IComponentContext context, params Parameter[] parameters)
{
return (TService)ResolveUnregistered(context, typeof(TService), (IEnumerable<Parameter>)parameters);
}
I found a solution that required some custom code. Somethings are specific to my app, but I think you can get the picture.
Resolve(parameter.ParameterType) would be a call to your container.
public object ResolveUnregistered(Type type)
{
var constructors = type.GetConstructors();
foreach (var constructor in constructors)
{
try
{
var parameters = constructor.GetParameters();
var parameterInstances = new List<object>();
foreach (var parameter in parameters)
{
var service = Resolve(parameter.ParameterType);
if (service == null) throw new NopException("Unkown dependency");
parameterInstances.Add(service);
}
return Activator.CreateInstance(type, parameterInstances.ToArray());
}
catch (NopException)
{
}
}
throw new NopException("No contructor was found that had all the dependencies satisfied.");
}
Here is a way to resolve an unregistered type with know constructor (ctor) properties. This is based on a previous great post: https://stackoverflow.com/a/6994144/2641447.
In this solution, it is absolutely great that constructor finding and selecting logic is handled by Autofac.
The comment is referred to dispose issues what I have solved with the 'ExternallyOwned()' wich configure the component so that instances are never disposed by the container.
I think that an improvement of the solution may be the following:
public static object ResolveUnregistered(this IComponentContext context, Type serviceType, IEnumerable<Parameter> parameters)
{
var scope = context.Resolve<ILifetimeScope>();
using (var innerScope = scope.BeginLifetimeScope(b => b.RegisterType(serviceType).ExternallyOwned()))
return innerScope.Resolve(serviceType, parameters);
}
The usings:
using Autofac;
using Autofac.Core;
using System;
using System.Collections.Generic;
Does anyone know if it is possible to tell if a specific property on an object is dirty (i.e. the property is different to the one stored on the DB) using NHibernate?
The background to this question is that I will have an object with a (relatively) large number of properties on it. I need to be able to pass a parameter (string) to a function that will determine if that specific property has changed during the lifetime of the page.
If I need to I can create a copy of the object and use reflection at the end of the page lifecycle to check the value, but I am reluctant to do this. It would be great if NHibernate could simply tell me if a property was dirty.
Thanks
That is not currently available through the nHibernate (or Hibnernate, AFAIK) API. You could write something like this yourself by storing the state of the objects when they're loaded into session, then compare them.
NOTE: I haven't seen this for myself, but, if you haven't locked into an ORM choice, you should look at Subsonic. A lot of the dirty status is tracked within the objects themselves, and you'd be able to determine if a particular property is dirty.
the session can tell if the entity is dirty
public static class SessionExtensions
{
/// <exception cref="NHibernate.PersistentObjectException">object is an uninitialized proxy</exception>
public static bool IsDirty(this ISession session, object entity)
{
var sessionImpl = session.GetSessionImplementation();
entity = sessionImpl.PersistenceContext.Unproxy(entity);
var oldEntry = sessionImpl.PersistenceContext.GetEntry(entity);
var persister = sessionImpl.Factory.GetEntityPersister(oldEntry.EntityName);
return persister.FindDirty(persister.GetPropertyValues(entity), oldEntry.LoadedState, entity, sessionImpl) != null;
}
/// <exception cref="NHibernate.PersistentObjectException">object is an uninitialized proxy</exception>
public static bool IsDirtyProperty(this ISession session, object entity, string propertyName)
{
var sessionImpl = session.GetSessionImplementation();
entity = sessionImpl.PersistenceContext.Unproxy(entity);
var oldEntry = sessionImpl.PersistenceContext.GetEntry(entity);
var persister = sessionImpl.Factory.GetEntityPersister(oldEntry.EntityName);
return !Equals(persister.GetPropertyValue(entity, propertyName), oldEntry.GetLoadedValue(propertyName));
}
}
Checking whether property is dirty or not may be possible by manually checking it against IPersistenceContext. I never tried it so I cannot provide an exact code. You may refer the GetEntityEntryFromPersistenceContext method in below code sample and write your own code to do that.
You can easily check if entire entity is dirty or not. You can use following utility class to know the various states of entity including the dirty state.
public static class NHibernateUtil
{
public static bool IsDirtyEntity<TEntity>(ISession session, TEntity instance) where TEntity : class
{
if(IsPersistentEntity(session, instance, true) == false)
{
throw new NhBoilerplateException("This is not a persistent entity.");
}
ISessionImplementor sessionImpl = session.GetSessionImplementation();
IPersistenceContext persistenceContext = sessionImpl.PersistenceContext;
EntityEntry oldEntry = sessionImpl.PersistenceContext.GetEntry(instance);
if((oldEntry == null) && (instance is INHibernateProxy))
{
INHibernateProxy proxy = instance as INHibernateProxy;
object obj = sessionImpl.PersistenceContext.Unproxy(proxy);
oldEntry = sessionImpl.PersistenceContext.GetEntry(obj);
}
string className = NHibernateProxyHelper.GuessClass(instance).FullName;
IEntityPersister persister = sessionImpl.Factory.GetEntityPersister(className);
object[] oldState = oldEntry.LoadedState;
object[] currentState = persister.GetPropertyValues(instance);
int[] dirtyProps = persister.FindDirty(currentState, oldState, instance, sessionImpl);
return (dirtyProps != null);
}
public static bool IsProxyEntity<TEntity>(TEntity instance) where TEntity : class
{
if((instance is INHibernateProxy) == false)
return false;
else
return true;
}
public static bool IsPersistentEntity<TEntity>(ISession session, TEntity instance, bool validateProxy) where TEntity : class
{
EntityEntry oldEntry = GetEntityEntryFromPersistenceContext(session, instance);
if(oldEntry != null)
return true;
if(validateProxy == false)
return false;
if(IsProxyEntity(instance) == true)
return true;
else
return false;
}
public static EntityEntry GetEntityEntryFromPersistenceContext<TEntity>(ISession session, TEntity instance) where TEntity : class
{
ISessionImplementor sessionImpl = session.GetSessionImplementation();
IPersistenceContext persistenceContext = sessionImpl.PersistenceContext;
EntityEntry oldEntry = persistenceContext.GetEntry(instance);
return oldEntry;
}
}