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?
Related
I have a set of entities that implement an interface:
public interface ILocalised
{
Culture Culture { get; }
}
For lots of complicated reasons I need to filter entities which do not have the correct culture after they are returned back from the DB (i.e. I can't use a Filter). My immediate thought was to create an interceptor that would filter any entities that did not have the correct culture, e.g.
public class LocalisationInterceptor : EmptyInterceptor
{
public override object Instantiate(string clazz, NHibernate.EntityMode entityMode, object id)
{
var entity = base.Instantiate(clazz, entityMode, id); //Returns null already
if ((entity is ILocalised) && false == IsValidCulture((ILocalised)entity))
{
return null;
}
return base.Instantiate(clazz, entityMode, id);
}
public override bool OnLoad(object entity, object id, object[] state, string[] propertyNames, NHibernate.Type.IType[] types)
{
if((entity is ILocalised) && false == IsValidCulture((ILocalised)entity))
{
entity = null;
return false;
}
return base.OnLoad(entity, id, state, propertyNames, types);
}
private bool IsValidCulture(ILocalised localisedEntity)
{
return localisedEntity.Culture == Culture.En;
}
}
However so far, what ever method I try to override it will always return the entity.
Does anyone have any ideas how to prevent certain entities from being loaded in an interceptor or any other solutions?
One way is to wrap up the Session using say a Repository. In the Repository.Search method, when the results are returned, do the filtering. So basically you are doing the filtering in your own code after nHibernate is done.
I would like to formulate a contrived scenario, which nevertheless has firm actual basis. Imagine a collection type COuter, which is a wrapper around an instance of another collection type CInner. Both implement IList (never mind the T).
Furthermore, a COuter instance is buried inside some object graph, the root of which (let us refer to it as R) is returned from a WCF service method.
My question is how can I customize the WCF serialization process, so that when R is returned, the request to serialize the COuter instance will be routed through my code, which will extract CInner and pass it to the serializer instead. Thus the receiving end still gets R, only no COuter instance is found in the object graph.
I hoped that How does WCF serialize the method call? will contain the answer, unfortunately the article mentioned there (http://msdn.microsoft.com/en-us/magazine/cc163569.aspx) only barely mentions that advanced serialization scenarios are possible using IDataContractSurrogate interface, but no details are given. I am, on the other hand, would really like to see a working example.
Thank you very much in advance.
EDIT
I have created a trivial WCF sample, which demonstrates the issue. The archive is located here - https://docs.google.com/leaf?id=0B2pbsdBJxJI3NzFiNjcxMmEtMTM5Yy00MWY2LWFiMTUtNjJiNjdkYTU1ZTk4&sort=name&layout=list&num=50
It contains three small projects:
HelloServiceAPI - contains the service interface and the argument types
Host - the HelloService host
Client - a simple console client.
The service defines one method, which returns an instance of the HelloServiceResult type, which contains a reference to COuterList type, which wraps CInnerList type. The reference is specified as IMyListInterface, where both COuterList and CInnerList implement this interface. What I need is that when the result is serialized before being transmitted to the client, the COuterList reference be replaced with the wrapped CInnerList reference. I know this can be done by utilizing the existing abilities of WCF, I just do not know how.
Here is how you implement your own Surrogate:
class YourCustomTypeSurrogate : IDataContractSurrogate
{
public Type GetDataContractType(Type type)
{
// Just for reference
//if (typeof(OldType).IsAssignableFrom(type))
//{
// return typeof(NewType);
//}
return type;
}
public object GetObjectToSerialize(object obj, Type targetType)
{
// This method is called on serialization.
//if (obj is OldType)
//{
// // ... use the XmlSerializer to perform the actual serialization.
// NewType newObj = new NewType();
// return newObj;
//}
return obj;
}
public object GetDeserializedObject(object obj, Type targetType)
{
// This method is called on deserialization.
// If PersonSurrogated is being deserialized...
//if (obj is NewType)
//{
// OldType newObj = new OldType();
// return newObj;
//}
return obj;
}
public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
{
// This method is called on schema import.
//if (typeNamespace.Equals("Your Type Namespace"))
//{
// if (typeName.Equals("NewType"))
// {
// return typeof(OldType);
// }
//}
return null;
}
public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
{
// Not used in this sample.
// You could use this method to construct an entirely
// new CLR type when a certain type is imported, or modify a generated
// type in some way.
return typeDeclaration;
}
public object GetCustomDataToExport(Type clrType, Type dataContractType)
{
// Not used in this sample
return null;
}
public object GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType)
{
// Not used in this sample
return null;
}
public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
{
// Not used in this sample
}
}
Then you create a custom Serializer Operation Behavior :
public class CustomDataContractSerializerOperationBehavior : DataContractSerializerOperationBehavior
{
public CustomDataContractSerializerOperationBehavior(OperationDescription operationDescription) : base(operationDescription) { }
public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns, IList<Type> knownTypes)
{
return new DataContractSerializer(
type /*typeof OldType*/,
knownTypes,
int.MaxValue /*maxItemsInObjectGraph */,
false /*ignoreExtensionDataObject*/,
true /*preserveObjectReferences*/,
new YourCustomTypeSurrogate());
}
public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name, XmlDictionaryString ns, IList<Type> knownTypes)
{
return new DataContractSerializer(
type /*typeof OldType*/,
knownTypes,
int.MaxValue /*maxItemsInObjectGraph */,
false /*ignoreExtensionDataObject*/,
true /*preserveObjectReferences*/,
new YourCustomTypeSurrogate());
}
}
After that, you create an attribute to apply the above operation behavior to an operation contract :
public class CustomDataContractFormatAttribute : Attribute, IOperationBehavior
{
public void AddBindingParameters(OperationDescription description, BindingParameterCollection parameters)
{
}
public void ApplyClientBehavior(OperationDescription description, ClientOperation proxy)
{
ReplaceDataContractSerializerOperationBehavior(description);
}
public void ApplyDispatchBehavior(OperationDescription description, DispatchOperation dispatch)
{
ReplaceDataContractSerializerOperationBehavior(description);
}
public void Validate(OperationDescription description)
{
}
private static void ReplaceDataContractSerializerOperationBehavior(OperationDescription description)
{
DataContractSerializerOperationBehavior dcs = description.Behaviors.Find<DataContractSerializerOperationBehavior>();
if (dcs != null)
description.Behaviors.Remove(dcs);
description.Behaviors.Add(new CustomDataContractSerializerOperationBehavior(description));
}
}
And finally, you apply this Attribute to an operation :
[OperationContract]
[CustomDataContractFormat]
void DoWork();
If you want to apply this to whole service, then you customize Service Behavior instead of Operation Behavior.
Here are the references that were used to create this example :
http://msdn.microsoft.com/en-us/library/system.runtime.serialization.idatacontractsurrogate.aspx
http://www.danrigsby.com/blog/index.php/2008/03/07/xmlserializer-vs-datacontractserializer-serialization-in-wcf/
http://www.danrigsby.com/blog/index.php/2008/04/10/specifying-a-different-serializer-per-endpoint-in-wcf/
http://social.msdn.microsoft.com/forums/en-US/wcf/thread/e4d55f3f-86d1-441d-9187-64fbd8ab2b3d/
Have you tried the good old OnSerializingAttribute?
[Serializable]
[KnownType(typeof(COuterList))]
public class HelloServiceResult
{
public IMyListInterface List;
[OnSerialized]
void OnSerializing(StreamingContext context)
{
if (List is COuterList)
{
List = ((List as COuterList).InnerList as CInnerList);
}
}
}
I'm trying to use a convention to map UInt32 properties to a SQL Server 2008 database. I don't seem to be able to create a solution based on existing web sources, due to updates in the way Fluent NHibernate works - i.e. examples are out of date.
I'm trying to have NHibernate generate the schema (via ExposeConfiguration). I'm happy to have NHibernate map it to anything sensible (e.g. bigint).
Here's my code as it currently stands (which, when I try to expose the schema, fails due to SQL Server not supporting UInt32). Apologies for the code being a little long, but I'm not 100% sure what is relevant to the problem, so I'm erring on the side of caution. Most of it is based on this post.
The error reported is:
System.ArgumentException : Dialect does not support DbType.UInt32
I think I'll need a relatively comprehensive example, as I don't seem to be able to pull the pieces together into a working solution, at present.
FluentConfiguration configuration =
Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(connectionString))
.Mappings(mapping =>
mapping.AutoMappings.Add(
AutoMap.AssemblyOf<Product>()
.Conventions.Add<UInt32UserTypeConvention>()));
configuration.ExposeConfiguration(x => new SchemaExport(x).Create(false, true));
namespace NHibernateTest
{
public class UInt32UserTypeConvention : UserTypeConvention<UInt32UserType>
{
// Empty.
}
}
namespace NHibernateTest
{
public class UInt32UserType : IUserType
{
// Public properties.
public bool IsMutable
{
get
{
return false;
}
}
public Type ReturnedType
{
get
{
return typeof(UInt32);
}
}
public SqlType[] SqlTypes
{
get
{
return
new SqlType[]
{
SqlTypeFactory.Int32
};
}
}
// Public methods.
public object Assemble(object cached, object owner)
{
return cached;
}
public object DeepCopy(object value)
{
return value;
}
public object Disassemble(object value)
{
return value;
}
public new bool Equals(object x, object y)
{
return (x != null && x.Equals(y));
}
public int GetHashCode(object x)
{
return x.GetHashCode();
}
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
int? i = (int?)NHibernateUtil.Int32.NullSafeGet(rs, names[0]);
return (UInt32?)i;
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
UInt32? u = (UInt32?)value;
int? i = (Int32?)u;
NHibernateUtil.Int32.NullSafeSet(cmd, i, index);
}
public object Replace(object original, object target, object owner)
{
return original;
}
}
}
You'd need to map to an existing SQL Server datatype, of course.
Based on this question, "Best way to store UInt32 in Sql Server", your choices:
CLR datatype
bigint
decimal
a workaround using int.MinValue to map to int
this was posted on hibernate.org forums and nhusers list without much luck, so I thought I would try here.
Put simply, suppose I have a class:
class A
{
public virtual object SomeValue { get; set; }
}
the type of SomeValue is basically in the set of .NET IConvertible types (primitives like bool, byte, char, int16, double, float etc.), plus byte[] and string.
I am trying to create a nhibernate mapping for A to reflect this - so that I can basically set SomeValue to an arbitrary object (of one of the types above) and retrieve it later on on. My applogic will then reflect on it to find the type and behave accordingly.
So far I have tried Creating an implementation of IUserType to try and handle this. However I don't know what to return for the SqlType[] SqlTypes. I considered new SqlType(DbType.Object) but when I try to generate a schema from this I get a System.ArgumentException: Dialect does not support DbType.Object
If I try another data type then I get various cast exceptions when trying to convert the type. For instance if i use a DbType.Binary, and set someValue to an int32, upon attempting to commit I get System.InvalidCastException: Unable to cast object of type 'System.Int32' to type 'System.Byte[]'.
Is there a way to achieve this?
Attached code below for a non-working implementation of IUserType (based on http://intellect.dk/post/Implementing-custom-types-in-nHibernate.aspx )
public class DataElementType : IUserType
{
SqlType baseType = new SqlType(DbType.Binary);
public SqlType[] SqlTypes
{
get
{
return new[] { baseType };
}
}
public System.Type ReturnedType
{
get { return typeof(object); }
}
public new bool Equals(object x, object y)
{
if (x == null)
return false;
else
return x.Equals(y);
}
public int GetHashCode(object x)
{
return x.GetHashCode();
}
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
return rs[names[0]];
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
var param = new SQLiteParameter(baseType.DbType, value);
cmd.Parameters.Insert(index, param);
}
public object DeepCopy(object value)
{
if (value == null) return null;
return value;
}
public bool IsMutable
{
get { return false; }
}
public object Replace(object original, object target, object owner)
{
return original;
}
public object Assemble(object cached, object owner)
{
return cached;
}
public object Disassemble(object value)
{
return value;
}
}
It turns out to get around the problem with SqlType(DbType.Object) being unsupported by the Dialect, we make it supported by subclassing the SQLiteDialect with explicit support:
public class SQLiteDialectWithManifestTyping : SQLiteDialect
{
public SQLiteDialectWithManifestTyping() : base()
{
base.RegisterColumnType(DbType.Object, "NONE");
}
}
To use this dialect in Fluent, call Dialect() on your SQLiteConfiguration object. In NHibernate, set the configuration property dialect appropriatelly (see section 3.5.1 of the ref manual).
Then we can apply the above DataElementType implementation for our mappings (need to change the SqlTypes definition to this:
public SqlType[] SqlTypes
{
get
{
return new[] { new SqlType(DbType.Object) };
}
}
Notes:
It is not perfect. There is a tendency to upcast all discrete numbers to Int64 and floats to double.
There is no implicit way to store large unsigned values (e.g. values of ulong >= long.MaxValue) but this is a general sqlite problem (and possible a general ado.net problem?).
Due to the loss of compile time checking it is probably desireable to put some runtime checks in the NullSafeSet method to ensure the value is a primitive type. Attempting to store general objects seems to just cause the objects ToString() method to be called.
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;
}
}