Save all string values in lowercase - nhibernate

I would like to lower case all string values before saving them do db.
Is there any way NHibernate can do this and how ? Also are there any performance implications that I should be aware of ?

One way to achieve it would be introducing custom type for conversion. Something like:
[Serializable]
public class LowerCaseStringType : AbstractStringType, ILiteralType
{
public LowerCaseStringType() : base(new StringSqlType())
{
//To avoid NHibernate to issue update on flush when the same string is assigned with different casing
Comparer = StringComparer.OrdinalIgnoreCase;
}
public override string Name { get; } = "LowerCaseString";
public override void Set(DbCommand cmd, object value, int index, ISessionImplementor session)
{
base.Set(cmd, ((string) value)?.ToLowerInvariant(), index, session);
}
//Called when NHibernate needs to inline non parameterized string right into SQL. Not sure if you need it
string ILiteralType.ObjectToSQLString(object value, Dialect.Dialect dialect)
{
return "'" + ((string) value).ToLowerInvariant() + "'";
}
//If you also want to retrieve all values in lowercase than also override Get method
}
Than you can either map required properties with this type like:
<property name="Name" type="YourNamespace.LowerCaseStringType, YourAssemblyName">
Or even register it as default type for all string mappings (at least it's true for latest NHibernate 5.2):
//Somewhere before SessionFactory is created
TypeFactory.RegisterType(typeof(string), new LowerCaseStringType(), new[] {"string", "String"});

Related

Best Practice for OOP function with multiple possible control flows

In my project, I have this special function that does needs to evaluate the following:
State -- represented by an enum -- and there are about 6 different states
Left Argument
Right Argument
Left and Right arguments are represented by strings, but their values can be the following:
"_" (a wildcard)
"1" (an integer string)
"abc" (a normal string)
So as you can see, to cover all every single possibility, there's about 2 * 3 * 6 = 36 different logics to evaluate and of course, using if-else in one giant function will not be feasible at all. I have encapsulated the above 3 input into an object that I'll pass to my function.
How would one try to use OOP to solve this. Would it make sense to have 6 different subclasses of the main State class with an evaluate() method, and then in their respective methods, I have if else statements to check:
if left & right arg are wildcards, do something
if left is number, right is string, do something else
Repeat for all the valid combinations in each State subclass
This feels like the right direction, but it also feels like theres alot of duplicate logic (for example check if both args are wildcards, or both strings etc.) for all 6 subclasses. Then my thought is to abstract it abit more and make another subclass:
For each state subclass, I have stateWithTwoWildCards, statewithTwoString etc.
But I feel like this is going way overboard and over-engineering and being "too" specific (I get that this technically adheres tightly to SOLID, especially SRP and OCP concepts). Any thoughts on this?
Possibly something like template method pattern can be useful in this case. I.e. you will encapsulate all the checking logic in the base State.evaluate method and create several methods which subclasses will override. Something along this lines:
class StateBase
def evaluate():
if(bothWildcards)
evalBothWildcards()
else if(bothStrings)
evalBothStrings()
else if ...
def evalBothWildcards():
...
def evalBothStrings():
...
Where evalBothWildcards, evalBothStrings, etc. will be overloaded in inheritors.
there's about 2 * 3 * 6 = 36 different logics to evaluate
We can apply divide and conquer technique.
you have 6 states. It is possible to use Chain of Responibility pattern here to choose appropriate state handler
when desired state handler is found, then we can apply desired function. The appropriate function can be considered as strategy. So it is a place where Strategy pattern can be applied.
we can separate strategies by appropriate states and put them in simple factory to get desired strategy by key.
This is what we will do. So let's see it more thoroughly.
Chain of responsibility pattern
If you have a lot if else statements, it is possible to use Chain of Responsibility pattern. As wiki says about Chain of Responsibility:
The chain-of-responsibility pattern is a behavioral design pattern
consisting of a source of command objects and a series of processing
objects. Each processing object contains logic that defines the
types of command objects that it can handle; the rest are passed to
the next processing object in the chain. A mechanism also exists for
adding new processing objects to the end of this chain
So let's dive in code. Let me show an example via C#.
So this is our Argument class which has Left and Right operands:
public class Arguments
{
public string Left { get; private set; }
public string Right { get; private set; }
public MyState MyState { get; private set; }
public MyKey MyKey => new MyKey(MyState, Left);
public Arguments(string left, string right, MyState myState)
{
Left = left;
Right = right;
MyState = myState;
}
}
And this is your 6 states:
public enum MyState
{
One, Two, Three, Four, Five, Six
}
This is start of Decorator pattern. This is an abstraction of StateHandler which defines behaviour to to set next handler:
public abstract class StateHandler
{
public abstract MyState State { get; }
private StateHandler _nextStateHandler;
public void SetSuccessor(StateHandler nextStateHandler)
{
_nextStateHandler = nextStateHandler;
}
public virtual IDifferentLogicStrategy Execute(Arguments arguments)
{
if (_nextStateHandler != null)
return _nextStateHandler.Execute(arguments);
return null;
}
}
and its concrete implementations of StateHandler:
public class OneStateHandler : StateHandler
{
public override MyState State => MyState.One;
public override IDifferentLogicStrategy Execute(Arguments arguments)
{
if (arguments.MyState == State)
return new StrategyStateFactory().GetInstanceByMyKey(arguments.MyKey);
return base.Execute(arguments);
}
}
public class TwoStateHandler : StateHandler
{
public override MyState State => MyState.Two;
public override IDifferentLogicStrategy Execute(Arguments arguments)
{
if (arguments.MyState == State)
return new StrategyStateFactory().GetInstanceByMyKey(arguments.MyKey);
return base.Execute(arguments);
}
}
and the third state handler looks like this:
public class ThreeStateHandler : StateHandler
{
public override MyState State => MyState.Three;
public override IDifferentLogicStrategy Execute(Arguments arguments)
{
if (arguments.MyState == State)
return new StrategyStateFactory().GetInstanceByMyKey(arguments.MyKey);
return base.Execute(arguments);
}
}
Strategy pattern
Let's pay attention to the following row of code:
return new StrategyStateFactory().GetInstanceByMyKey(arguments.MyKey);
The above code is an example of using Strategy pattern. We have different ways or strategies to handle
your cases. Let me show a code of strategies of evaluation of your expressions.
This is an abstraction of strategy:
public interface IDifferentLogicStrategy
{
string Evaluate(Arguments arguments);
}
And its concrete implementations:
public class StrategyWildCardStateOne : IDifferentLogicStrategy
{
public string Evaluate(Arguments arguments)
{
// your logic here to evaluate "_" (a wildcard)
return "StrategyWildCardStateOne";
}
}
public class StrategyIntegerStringStateOne : IDifferentLogicStrategy
{
public string Evaluate(Arguments arguments)
{
// your logic here to evaluate "1" (an integer string)
return "StrategyIntegerStringStateOne";
}
}
And the third strategy:
public class StrategyNormalStringStateOne : IDifferentLogicStrategy
{
public string Evaluate(Arguments arguments)
{
// your logic here to evaluate "abc" (a normal string)
return "StrategyNormalStringStateOne";
}
}
Simple factory
There is no pattern like simple factory. However, it is a place where we can get instances of strategies by key. So by doing this we avoided to use multiple if else statements to choose correct strategy.
So, we need a place where we can store strategies by state and argument value. At first, let's create MyKey struct. It will have help us to differentiate State and arguments:
public struct MyKey
{
public readonly MyState MyState { get; }
public readonly string ArgumentValue { get; } // your three cases: "_",
// an integer string, a normal string
public MyKey(MyState myState, string argumentValue)
{
MyState = myState;
ArgumentValue = argumentValue;
}
public override bool Equals([NotNullWhen(true)] object? obj)
{
return obj is MyKey mys
&& mys.MyState == MyState
&& mys.ArgumentValue == ArgumentValue;
}
public override int GetHashCode()
{
unchecked // Overflow is fine, just wrap
{
int hash = 17;
hash = hash * 23 + MyState.GetHashCode();
hash = hash * 23 + ArgumentValue.GetHashCode();
return hash;
}
}
}
and then we can create a simple factory:
public class StrategyStateFactory
{
private Dictionary<MyKey, IDifferentLogicStrategy>
_differentLogicStrategyByStateAndValue =
new Dictionary<MyKey, IDifferentLogicStrategy>()
{
{ new MyKey(MyState.One, "_"), new StrategyWildCardStateOne() },
{ new MyKey(MyState.One, "intString"),
new StrategyIntegerStringStateOne() },
{ new MyKey(MyState.One, "normalString"),
new StrategyNormalStringStateOne() }
};
public IDifferentLogicStrategy GetInstanceByMyKey(MyKey myKey)
{
return _differentLogicStrategyByStateAndValue[myKey];
}
}
So we've written our strategies and we've stored these strategies in simple factory StrategyStateFactory.
Then we need to check the above implementation:
StateHandler chain = new OneStateHandler();
StateHandler secondStateHandler = new TwoStateHandler();
StateHandler thirdStateHandler = new ThreeStateHandler();
chain.SetSuccessor(secondStateHandler);
secondStateHandler.SetSuccessor(thirdStateHandler);
Arguments arguments = new Arguments("_", "_", MyState.One);
IDifferentLogicStrategy differentLogicStrategy = chain.Execute(arguments);
string evaluatedResult =
differentLogicStrategy.Evaluate(arguments); // output: "StrategyWildCardStateOne"
I believe I gave basic idea how it can be done.

Custom linq provider to search into an XML field for an xml attribute with a certain value

Some of my database tables, which I interact with through NHibernate, contain an XML field with the following structure:
<L xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<I>
<C>
<N>Attribute1</N>
<V>a_value</V>
</C>
<C>
<N>Attribute2</N>
<V>123</V>
</C>
</I>
</L>
Basically, each "C" tag contains an attribute, where it's name is contained in tag "N" and it's value in tag "V".
What I want to achieve is being able to write this kind of LINQ syntax in my queries:
..
.Where(m=>m.XMLField(attribute_name, attribute_value))
..
so that I'm able to get the entities of a specific table whose XML field contains the attribute named "attribute_name" with the string value specified by "attribute_value".
It's as simple as that, the XML structure is always like that and I only need to query for a single attribute with a specific value.
Doing my searches I've found that there's a specific technique to implement a custom LINQ provider:
http://www.primordialcode.com/blog/post/nhibernate-3-extending-linq-provider-fix-notsupportedexception
http://fabiomaulo.blogspot.it/2010/07/nhibernate-linq-provider-extension.html
How would I alter the SQL that Linq-to-Nhibernate generates for specific columns?
Unfortunately, I wasn't able to find some structured documentation on how to use the treebuilder, so, at the moment this is what I have:
I have figured out the correct HQL to perform such a task:
where [some other statements] and XML_COLUMN_NAME.exist('/L/I/C[N=\"{0}\" and V=\"{1}\"]') = 1","attribute_name", "attribute_value");
the method which I'm going to call inside the LINQ query:
public static bool AttributeExists(this string xmlColumnName, string attributeName, string attributeValue)
{
throw new NotSupportedException();
}
the integration part with HQL:
public class XMLAttributeGenerator : BaseHqlGeneratorForMethod
{
public XMLAttributeGenerator()
{
SupportedMethods = new[] { ReflectionHelper.GetMethodDefinition(() => TestClass.AttributeExists(null, null, null)) };
}
public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject,
ReadOnlyCollection<Expression> arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
{
return treeBuilder.Exists(???);
}
}
As you can see, I still haven't figure out how to properly use the treebuilder with the visitor object to replicate the HQL syntax expressed above. May somebody help me out with this or at least point me to some basic documentation about the usage of the treebuilder? Thanks
This is how I achieved the desired result:
MOCK METHOD
public static class MockLINQMethods
{
public static bool XMLContains(this MyCustomNHType input, string element, string value)
{
throw new NotImplementedException();
}
}
CUSTOM GENERATOR
public class CustomLinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry
{
public CustomLinqToHqlGeneratorsRegistry()
: base()
{
RegisterGenerator(ReflectionHelper.GetMethod(() => MockLINQMethods.XMLContains((MyCustomNHType) null, null, null)),
new LINQtoHQLGenerators.MyCustomNHTypeXMLContainsGenerator());
}
}
public class MyCustomNHTypeXMLContainsGenerator : BaseHqlGeneratorForMethod
{
public MyCustomNHTypeXMLContainsGenerator()
{
SupportedMethods = new[] { ReflectionHelper.GetMethod(() => MockLINQMethods.XMLContains((MyCustomNHType) null, null, null)) };
}
public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject,
ReadOnlyCollection<Expression> arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
{
var column_name = visitor.Visit(arguments[0]).AsExpression();
var element_name = visitor.Visit(arguments[1]).AsExpression();
var value = visitor.Visit(arguments[2]).AsExpression();
return treeBuilder.BooleanMethodCall("_ExistInMyCustomNHType", new [] { column_name, element_name, value});
}
}
CUSTOM FUNCTION
public class CustomLinqToHqlMsSql2008Dialect : MsSql2008Dialect
{
public CustomLinqToHqlMsSql2008Dialect()
{
RegisterFunction("_ExistInMyCustomNHType",
new SQLFunctionTemplate(NHibernateUtil.Boolean,
"?1.exist('/L/I/C[N=sql:variable(\"?2\") and V=sql:variable(\"?3\")]') = 1"));
}
}
FINALLY, LINK THE CUSTOM GENERATOR AND THE CUSTOM FUNCTION IN THE SESSION HELPER
factory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(connectionString)
.Dialect<CustomLinqToHqlMsSql2008Dialect>())
..
.ExposeConfiguration(c =>
{
..
c.SetProperty("linqtohql.generatorsregistry", "APP.MyNAMESPACE.CustomLinqToHqlGeneratorsRegistry, APP.MyNAMESPACE");
..
})
.BuildSessionFactory();

Lambda string as VARCHAR

One of my Join key-selectors looks like this:
x => x.A + "-" + x.B
NHibernate makes "-" an extra parameter. This parameter gets the SQL type nvarchar and so the whole statement gets converted on the SQL Server from varchar to nvarchar.
The problem with this is, that SQL Server has a huge problem if the queried column is of type varchar instead of nvarchar. This is because the column is of another type than the parameter and so the index can't be used.
I cannot change the type of the column so I need to define somehow that NHibernate should use varchar for string literals when converting lambdas.
Any way to do this?
UPDATE
With help from Oskar Berggren I setup this classes:
public static class VarcharFix
{
/// This method returns its argument and is a no-op in C#.
/// It's presence in a Linq expression sends a message to the NHibernate Linq Provider.
public static string AsVarchar(string s)
{
return s;
}
}
public class MyHqlIdent : HqlExpression
{
internal MyHqlIdent(IASTFactory factory, string ident)
: base(HqlSqlWalker.IDENT, ident, factory)
{
}
internal MyHqlIdent(IASTFactory factory, System.Type type)
: base(HqlSqlWalker.IDENT, "", factory)
{
if (IsNullableType(type))
{
type = ExtractUnderlyingTypeFromNullable(type);
}
switch (System.Type.GetTypeCode(type))
{
case TypeCode.Boolean:
SetText("bool");
break;
case TypeCode.Int16:
SetText("short");
break;
case TypeCode.Int32:
SetText("integer");
break;
case TypeCode.Int64:
SetText("long");
break;
case TypeCode.Decimal:
SetText("decimal");
break;
case TypeCode.Single:
SetText("single");
break;
case TypeCode.DateTime:
SetText("datetime");
break;
case TypeCode.String:
SetText("string");
break;
case TypeCode.Double:
SetText("double");
break;
default:
if (type == typeof(Guid))
{
SetText("guid");
break;
}
if (type == typeof(DateTimeOffset))
{
SetText("datetimeoffset");
break;
}
throw new NotSupportedException(string.Format("Don't currently support idents of type {0}", type.Name));
}
}
private static System.Type ExtractUnderlyingTypeFromNullable(System.Type type)
{
return type.GetGenericArguments()[0];
}
// TODO - code duplicated in LinqExtensionMethods
private static bool IsNullableType(System.Type type)
{
return (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>));
}
}
public class MyHqlCast : HqlExpression
{
public MyHqlCast(IASTFactory factory, IEnumerable<HqlTreeNode> children)
: base(HqlSqlWalker.METHOD_CALL, "method", factory, children)
{
}
public static MyHqlCast Create(IASTFactory factory, HqlExpression expression, string targetType)
{
return new MyHqlCast(factory,
new HqlTreeNode[]
{
new MyHqlIdent(factory, "cast"),
new HqlExpressionList(factory, expression,
new MyHqlIdent(factory, targetType))
});
}
}
public class MyBaseHqlGeneratorForMethod : BaseHqlGeneratorForMethod
{
public MyBaseHqlGeneratorForMethod()
: base()
{
SupportedMethods = new MethodInfo[] { typeof(VarcharFix).GetMethod("AsVarchar") };
}
public override HqlTreeNode BuildHql(MethodInfo method, System.Linq.Expressions.Expression targetObject, System.Collections.ObjectModel.ReadOnlyCollection<System.Linq.Expressions.Expression> arguments, HqlTreeBuilder treeBuilder, global::NHibernate.Linq.Visitors.IHqlExpressionVisitor visitor)
{
return MyHqlCast.Create(new ASTFactory(new ASTTreeAdaptor()),
visitor.Visit(targetObject).AsExpression(),
"varchar");
}
}
public class ExtendedLinqtoHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry
{
public ExtendedLinqtoHqlGeneratorsRegistry()
{
this.Merge(new MyBaseHqlGeneratorForMethod());
}
}
For now it's still not working but I see light ;)
UPDATE 2: The Query
var query = aQueryable
.Join(bQueryable,
x => x.AB, x => x.A + VarcharFix.AsVarchar("-") + x.B,
(head, middle) => new ...)
UPDATE 3:
As "-".AsVarchar() gets optimized to "-" we need a dummy parameter, which cannot be optimized like "-".AsVarchar(x.A) - that way the Linq-extension kicks in!
var query = aQueryable
.Join(bQueryable,
x => x.AB, x => x.A + "-".AsVarchar(x.A) + x.B,
(head, middle) => new ...)
There may be multiple ways to do this but here is one:
Invent your own method such as:
/// This method returns its argument and is a no-op in C#.
/// It's presence in a Linq expression sends a message to the NHibernate Linq Provider.
public static string AsVarchar(string s)
{
return s;
}
Also create a class to represent the HQL expression fragment:
public class MyHqlCast : HqlExpression
{
private MyHqlCast(IASTFactory factory, IEnumerable<HqlTreeNode> children)
: base(HqlSqlWalker.METHOD_CALL, "method", factory, children)
{
}
public static MyHqlCast Create(IASTFactory factory, HqlExpression expression,
string targetType)
{
return new MyHqlCast(factory,
new [] {
new HqlIdent(factory, "cast")),
new HqlExpressionList(factory, expression,
new HqlIdent(factory, targetType)),
});
}
}
Then derive a class from BaseHqlGeneratorForMethod. In its constructor, set the SupportedMethods property to the AsVarchar() method. Override the BuildHql() method. It should output the HQL cast constructs equivalent to cast(#param as varchar). Normally you would use the Cast() method on the treeBuilder parameter, but unfortunately this accepts just a System.Type, which isn't good enough for this case. Instead create and return an instance of your MyHqlCast:
return MyHqlCast.Create(new ASTFactory(new ASTTreeAdaptor()),
visitor.Visit(arguments[0]).AsExpression(),
"varchar");
Your implementation of BaseHqlGeneratorForMethod then needs to be registered by deriving from DefaultLinqToHqlGeneratorsRegistry. Call this.Merge(new MyGenerator()); in the constructor. Then register your registry type by
nhibernateConfiguration.LinqToHqlGeneratorsRegistry<MyRegistry>();

IQuery NHibernate - do I have to Encrypt a parameter that is an encrypted IUserType?

Situation: suppose I have a column on an entity which is encrypted in the database using IUserType:
public class EncryptedStringUserType : IUserType
{
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
object r = rs[names[0]];
if (r == DBNull.Value)
return null;
return CryptoProvider.Instance.Decrypt((string) r);
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
object paramVal = DBNull.Value;
if (value != null)
paramVal = CryptoProvider.Instance.Encrypt((string) value);
IDataParameter parameter = (IDataParameter)cmd.Parameters[index];
parameter.Value = paramVal;
}
// Other IUserType members as usual..
}
As explained by Ayende himself in:
http://ayende.com/Blog/archive/2008/07/31/Entities-dependencies-best-practices.aspx
Now when querying, using the NHibernate IQuery interface, I need to encrypt the parameter I am passing into the Query:
query.SetString("DbEncryptedParameter",
CryptoProvider.Instance.Encrypt(UnencryptedValueObject.ToString()));
Question: Is there a better way of performing this query, leveraging the knowledge NHibernate has of this Encrypted Type, so the encryption doesn't have to be performed while setting the parameter?
Am I missing something? Why do you need to encrypt the value when sending it in? The point of IUserType is that you encapsulate the difference between the native form (plaintext) and persisted form (encrypted). When you provide the plaintext version to the query, NHibernate should be invoking the IUserType.NullSafeSet to encrypt the value and set the query parameter to the encrypted value.

JSON.NET and nHibernate Lazy Loading of Collections

Is anybody using JSON.NET with nHibernate? I notice that I am getting errors when I try to load a class with child collections.
I was facing the same problem so I tried to use #Liedman's code but the GetSerializableMembers() was never get called for the proxied reference.
I found another method to override:
public class NHibernateContractResolver : DefaultContractResolver
{
protected override JsonContract CreateContract(Type objectType)
{
if (typeof(NHibernate.Proxy.INHibernateProxy).IsAssignableFrom(objectType))
return base.CreateContract(objectType.BaseType);
else
return base.CreateContract(objectType);
}
}
We had this exact problem, which was solved with inspiration from Handcraftsman's response here.
The problem arises from JSON.NET being confused about how to serialize NHibernate's proxy classes. Solution: serialize the proxy instances like their base class.
A simplified version of Handcraftsman's code goes like this:
public class NHibernateContractResolver : DefaultContractResolver {
protected override List<MemberInfo> GetSerializableMembers(Type objectType) {
if (typeof(INHibernateProxy).IsAssignableFrom(objectType)) {
return base.GetSerializableMembers(objectType.BaseType);
} else {
return base.GetSerializableMembers(objectType);
}
}
}
IMHO, this code has the advantage of still relying on JSON.NET's default behaviour regarding custom attributes, etc. (and the code is a lot shorter!).
It is used like this
var serializer = new JsonSerializer{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
ContractResolver = new NHibernateContractResolver()
};
StringWriter stringWriter = new StringWriter();
JsonWriter jsonWriter = new Newtonsoft.Json.JsonTextWriter(stringWriter);
serializer.Serialize(jsonWriter, objectToSerialize);
string serializedObject = stringWriter.ToString();
Note: This code was written for and used with NHibernate 2.1. As some commenters have pointed out, it doesn't work out of the box with later versions of NHibernate, you will have to make some adjustments. I will try to update the code if I ever have to do it with later versions of NHibernate.
I use NHibernate with Json.NET and noticed that I was getting inexplicable "__interceptors" properties in my serialized objects. A google search turned up this excellent solution by Lee Henson which I adapted to work with Json.NET 3.5 Release 5 as follows.
public class NHibernateContractResolver : DefaultContractResolver
{
private static readonly MemberInfo[] NHibernateProxyInterfaceMembers = typeof(INHibernateProxy).GetMembers();
protected override List<MemberInfo> GetSerializableMembers(Type objectType)
{
var members = base.GetSerializableMembers(objectType);
members.RemoveAll(memberInfo =>
(IsMemberPartOfNHibernateProxyInterface(memberInfo)) ||
(IsMemberDynamicProxyMixin(memberInfo)) ||
(IsMemberMarkedWithIgnoreAttribute(memberInfo, objectType)) ||
(IsMemberInheritedFromProxySuperclass(memberInfo, objectType)));
var actualMemberInfos = new List<MemberInfo>();
foreach (var memberInfo in members)
{
var infos = memberInfo.DeclaringType.BaseType.GetMember(memberInfo.Name);
actualMemberInfos.Add(infos.Length == 0 ? memberInfo : infos[0]);
}
return actualMemberInfos;
}
private static bool IsMemberDynamicProxyMixin(MemberInfo memberInfo)
{
return memberInfo.Name == "__interceptors";
}
private static bool IsMemberInheritedFromProxySuperclass(MemberInfo memberInfo, Type objectType)
{
return memberInfo.DeclaringType.Assembly == typeof(INHibernateProxy).Assembly;
}
private static bool IsMemberMarkedWithIgnoreAttribute(MemberInfo memberInfo, Type objectType)
{
var infos = typeof(INHibernateProxy).IsAssignableFrom(objectType)
? objectType.BaseType.GetMember(memberInfo.Name)
: objectType.GetMember(memberInfo.Name);
return infos[0].GetCustomAttributes(typeof(JsonIgnoreAttribute), true).Length > 0;
}
private static bool IsMemberPartOfNHibernateProxyInterface(MemberInfo memberInfo)
{
return Array.Exists(NHibernateProxyInterfaceMembers, mi => memberInfo.Name == mi.Name);
}
}
To use it just put an instance in the ContractResolver property of your JsonSerializer. The circular dependency problem noted by jishi can be resolved by setting the ReferenceLoopHandling property to ReferenceLoopHandling.Ignore . Here's an extension method that can be used to serialize objects using Json.Net
public static void SerializeToJsonFile<T>(this T itemToSerialize, string filePath)
{
using (StreamWriter streamWriter = new StreamWriter(filePath))
{
using (JsonWriter jsonWriter = new JsonTextWriter(streamWriter))
{
jsonWriter.Formatting = Formatting.Indented;
JsonSerializer serializer = new JsonSerializer
{
NullValueHandling = NullValueHandling.Ignore,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
ContractResolver = new NHibernateContractResolver(),
};
serializer.Serialize(jsonWriter, itemToSerialize);
}
}
}
Are you getting a circular dependancy-error? How do you ignore objects from serialization?
Since lazy loading generates a proxy-objects, any attributes your class-members have will be lost. I ran into the same issue with Newtonsoft JSON-serializer, since the proxy-object didn't have the [JsonIgnore] attributes anymore.
You will probably want to eager load most of the object so that it can be serialized:
ICriteria ic = _session.CreateCriteria(typeof(Person));
ic.Add(Restrictions.Eq("Id", id));
if (fetchEager)
{
ic.SetFetchMode("Person", FetchMode.Eager);
}
A nice way to do this is to add a bool to the constructor (bool isFetchEager) of your data provider method.
I'd say this is a design problem in my opinion. Because NH makes connections to the database underneath all and has proxies in the middle, it is not good for the transparency of your application to serialize them directly (and as you can see Json.NET does not like them at all).
You should not serialize the entities themselves, but you should convert them into "view" objects or POCO or DTO objects (whatever you want to call them) and then serialize these.
The difference is that while NH entity may have proxies, lazy attributes, etc. View objects are simple objects with only primitives which are serializable by default.
How to manage FKs?
My personal rule is:
Entity level: Person class and with a Gender class associated
View level: Person view with GenderId and GenderName properties.
This means that you need to expand your properties into primitives when converting to view objects. This way also your json objects are simpler and easier to handle.
When you need to push the changes to the DB, in my case I use AutoMapper and do a ValueResolver class which can convert your new Guid to the Gender object.
UPDATE: Check http://blog.andrewawhitaker.com/blog/2014/06/19/queryover-series-part-4-transforming/ for a way to get the view directly (AliasToBean) from NH. This would be a boost in the DB side.
The problem can happen when NHibernate wraps the nested collection properties in a PersistentGenericBag<> type.
The GetSerializableMembers and CreateContract overrides cannot detect that these nested collection properties are "proxied". One way to resolve this is to override the CreateProperty method. The trick is to get the value from the property using reflection and test whether the type is of PersistentGenericBag. This method also has the ability to filter any properties that generated exceptions.
public class NHibernateContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
property.ShouldSerialize = instance =>
{
try
{
PropertyInfo prop = (PropertyInfo)member;
if (prop.CanRead)
{
var value = prop.GetValue(instance, null);
if (value != null && typeof(NHibernate.Collection.Generic.PersistentGenericBag<>).IsSubclassOfRawGeneric(value.GetType()))
return false;
return true;
}
}
catch
{ }
return false;
};
return property;
}
}
The IsSubclassOfRawGeneric extension used above:
public static class TypeExtensions
{
public static bool IsSubclassOfRawGeneric(this Type generic, Type? toCheck)
{
while (toCheck != null && toCheck != typeof(object))
{
var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck;
if (generic == cur)
{
return true;
}
toCheck = toCheck?.BaseType;
}
return false;
}
}
If you serialize objects that contain NHibernate proxy classes you might end up downloading the whole database, because once the property is accessed NHibernate would trigger a request to the database.
I've just implemented a Unit of Work for NHibernate: NHUnit that fixes two of the most annoying issues from NHibernate: proxy classes and cartesian product when using fetch.
How would you use this?
var customer = await _dbContext.Customers.Get(customerId) //returns a wrapper to configure the query
.Include(c => c.Addresses.Single().Country, //include Addresses and Country
c => c.PhoneNumbers.Single().PhoneNumberType) //include all PhoneNumbers with PhoneNumberType
.Unproxy() //instructs the framework to strip all the proxy classes when the Value is returned
.Deferred() //instructs the framework to delay execution (future)
.ValueAsync(token); //this is where all deferred queries get executed
The above code is basically configuring a query: return a customer by id with multiple child objects which should be executed with other queries (futures) and the returned result should be stripped of NHibernate proxies. The query gets executed when ValueAsync is called.
NHUnit determines if it should do join with the main query, create new future queries or make use of batch fetch.
There is a simple example project on Github to show you how to use NHUnit package. If others are interested in this project I will invest more time to make it better.
This is what I use:
Have a marker interface and inherit it on your entities, e.g. in my case empty IEntity.
We will use the marker interface to detect NHibernate entity types in the contract resolver.
public class CustomerEntity : IEntity { ... }
Create a custom contract resolver for JSON.NET
public class NHibernateProxyJsonValueProvider : IValueProvider {
private readonly IValueProvider _valueProvider;
public NHibernateProxyJsonValueProvider(IValueProvider valueProvider)
{
_valueProvider = valueProvider;
}
public void SetValue(object target, object value)
{
_valueProvider.SetValue(target, value);
}
private static (bool isProxy, bool isInitialized) GetProxy(object proxy)
{
// this is pretty much what NHibernateUtil.IsInitialized() does.
switch (proxy)
{
case INHibernateProxy hibernateProxy:
return (true, !hibernateProxy.HibernateLazyInitializer.IsUninitialized);
case ILazyInitializedCollection initializedCollection:
return (true, initializedCollection.WasInitialized);
case IPersistentCollection persistentCollection:
return (true, persistentCollection.WasInitialized);
default:
return (false, false);
}
}
public object GetValue(object target)
{
object value = _valueProvider.GetValue(target);
(bool isProxy, bool isInitialized) = GetProxy(value);
if (isProxy)
{
if (isInitialized)
{
return value;
}
if (value is IEnumerable)
{
return Enumerable.Empty<object>();
}
return null;
}
return value;
}
}
public class NHibernateContractResolver : CamelCasePropertyNamesContractResolver {
protected override JsonContract CreateContract(Type objectType)
{
if (objectType.IsAssignableTo(typeof(IEntity)))
{
return base.CreateObjectContract(objectType);
}
return base.CreateContract(objectType);
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
property.ValueProvider = new NHibernateProxyJsonValueProvider(property.ValueProvider);
return property;
}
}
Normal uninitialized lazy loaded properties will result in null in the json output.
Collection uninitialized lazy loaded properties will result in an [] empty array in json.
So for a lazy loaded property to appear in the json output you need to eagerly load it in the query or in code before serialization.
Usage:
JsonConvert.SerializeObject(entityToSerialize, new JsonSerializerSettings() {
ContractResolver = new NHibernateContractResolver()
});
Or globally in in ASP.NET Core Startup class
services.AddNewtonsoftJson(options =>
{
options.SerializerSettings.ContractResolver = new NHibernateContractResolver();
});
Using:
NET 5.0
NHibernate 5.3.8
JSON.NET latest via ASP.NET Core