Is it possible to order a query based on a NHibernate multi-column user type, preferably via QueryOver? - nhibernate

I have a fluent mapped a database table that uses two columns to represent an IP Address. One column, a string discriminator, address_family represents the address family (IPv4 vs IPv6) and a second column address a varbinary(16) that represents the IP address in Network Byte Order (big-endian with most significant bytes at the start).
Mapping is configured via a custom IUserType as follows
Map(x => x.Address)
.CustomType<IpAddressBinaryUserType>()
.Columns.Clear()
.Columns.Add("address_family", "address")
.Not.Nullable();
The custom IpAddressBinaryUserType(implementation shown below) implements IUserType as expected mapping the columns to an IPAddress as desired. However, there doesn't appear to be any obvious functionality to apply a OrderBy clause that would allow me, in this instance for example, to order by the address_family column descending, then by the address column ascending. I can of course do this manually after the query is run, but obviously this greatly limits my ability to page my data from large data sets.
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using NHibernate;
using NHibernate.Engine;
using NHibernate.SqlTypes;
using NHibernate.UserTypes;
namespace NHibernate.UserTypes
{
/// <summary>
/// <para>A User Type for persisting an IP Addresses as 2 columns in the DB (string AddressFamily, binary Address)</para>
/// <code>
/// Map(x => x.Address)
/// .CustomType&lt;IPAddressBinaryUserType&gt;()
/// .Columns.Clear()
/// .Columns.Add("address_family", "address")
/// .Not.Nullable();
/// </code>
/// </summary>
public class IpAddressBinaryUserType : IUserType
{
private const int AddressFamilyColumn = 0;
private const int BytesColumn = 1;
/// <summary>
/// Sql Types for custom mapping
/// </summary>
public SqlType[] SqlTypes => new[]
{
NHibernateUtil.String.SqlType, // Address Family
NHibernateUtil.Binary.SqlType // Address
};
/// <inheritdoc />
public bool IsMutable => false;
/// <inheritdoc />
public Type ReturnedType => typeof(IPAddress);
#region From Interface IUserType
/// <inheritdoc />
public object Assemble(object cached,
object owner)
{
return this.DeepCopy(cached);
}
/// <inheritdoc />
public void NullSafeSet(DbCommand cmd,
object value,
int index,
ISessionImplementor session)
{
if (value is IPAddress address)
{
NHibernateUtil.String.NullSafeSet(cmd, address.AddressFamily.ToString(), index, session);
NHibernateUtil.Binary.NullSafeSet(cmd, address.GetAddressBytes(), index + 1, session);
}
else
{
NHibernateUtil.String.NullSafeSet(cmd, null, index, session);
NHibernateUtil.Binary.NullSafeSet(cmd, null, index + 1, session);
}
}
/// <inheritdoc />
public object DeepCopy(object value)
{
return value;
}
/// <inheritdoc />
public object Disassemble(object value)
{
return this.DeepCopy(value);
}
/// <inheritdoc />
public new bool Equals(object x,
object y)
{
if (ReferenceEquals(x, y))
{
return true;
}
if (x == null
|| y == null)
{
return false;
}
return x.Equals(y);
}
/// <inheritdoc />
public int GetHashCode(object x)
{
return x.GetHashCode();
}
/// <inheritdoc />
/// <exception cref="HibernateException">on inability to do get based on parsing or column mapping</exception>
public object NullSafeGet(DbDataReader rs,
string[] names,
ISessionImplementor session,
object owner)
{
var addressFamilyString = NHibernateUtil.String.NullSafeGet(rs, names[AddressFamilyColumn], session) as string;
var bytes = NHibernateUtil.Binary.NullSafeGet(rs, names[BytesColumn], session) as byte[];
// no value
if (string.IsNullOrWhiteSpace(addressFamilyString)
&& bytes == null)
{
return null;
}
// get the address family
if (!Enum.TryParse((addressFamilyString ?? string.Empty).Trim(),
true,
out AddressFamily addressFamily))
{
throw new HibernateException($"Address family of \"{addressFamilyString}\" named by mapping as column \"{names[AddressFamilyColumn]}\" is not recognized");
}
if (bytes == null)
{
throw new HibernateException($"Bytes named by mapping as column \"{names[BytesColumn]}\" is not recognized");
}
var address = new IPAddress(GetBytes(addressFamily, bytes));
if (address.AddressFamily != addressFamily)
{
throw new HibernateException($"Expecting IP Addresses to be of address family {addressFamily}");
}
return address;
}
/// <inheritdoc />
public object Replace(object original,
object target,
object owner)
{
return original;
}
#endregion
/// <summary>
/// Get the bytes of an IP Address accounting for length based on address family
/// </summary>
/// <param name="addressFamily">the desired address family</param>
/// <param name="input">the possibly truncated bytes that may required 0 bytes affixed</param>
private static byte[] GetBytes(AddressFamily addressFamily,
IEnumerable<byte> input)
{
switch (addressFamily)
{
case AddressFamily.InterNetwork:
return AffixByteLength(input, 4);
case AddressFamily.InterNetworkV6:
return AffixByteLength(input, 16);
default:
throw new HibernateException($"Address family \"{addressFamily}\" is unsupported");
}
}
/// <summary>
/// Transform an <see cref="Enumerable" /> of <see langword="byte" /> input to a given length, trimming LSB / padding
/// with LSB 0x00\"s as necessary
/// </summary>
/// <param name="input">the bytes to transform</param>
/// <param name="desiredLength">the length of the bytes</param>
/// <returns>the transformed bytes</returns>
private static byte[] AffixByteLength(IEnumerable<byte> input,
int desiredLength)
{
var inputArray = (input ?? Enumerable.Empty<byte>()).ToArray();
if (inputArray.Length > desiredLength)
{
return inputArray.Take(desiredLength)
.ToArray();
}
if (inputArray.Length < desiredLength)
{
return inputArray.Concat(Enumerable.Repeat((byte) 0x00, desiredLength - inputArray.Length))
.ToArray();
}
return inputArray;
}
}
}
Is it possible to order a query based on a NHibernate multi-column user type? If so, how can it be done via QueryOver?

Related

Ninject Conventions and Interception

I want to decorate my services with attributes for interception, and then have conventions based binding set the interceptors up for me. I don't want my attributes to inherit from the interception attributes... if I can avoid it.
For example, I have the following class:
[Log]
public class SomeClassToLog
{
public void DoSomething() { ... }
}
I understand I can bind this as follows:
var kernel = new StandardKernel();
kernel.Bind(x => x.FromAssembliesMatching("SomeProject.*")
.SelectAllClasses()
.WithAttribute(typeof(LogAttribute))
.BindToSelf().Configure(syntax => syntax.Intercept().With(LogInterceptor)));
How can I do this with different combinations of attributes and interceptors? For example:
If I have Log and Authorize attributes I would have to configure 3 sets of bindings? (1 for log without authorize, 1 for authorize without log and one for both log and authorize).
Updated: While I couldn't find a solution based on my original question parameters, I did stumble upon a similar question which lead me to the solution I ended up going with. Here is the source code:
Notes: Common.Interception.Interceptors namespace is in an assembly which has a reference to Ninject.Extensions.Interception (and all of its required dependencies). My attributes are defined in a separate assembly with no dependencies of their own.
MiscExtensions.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Common.Interception.Interceptors
{
// TODO: Remove the dependence on these eventually
/// <summary>
/// A bundle of extension methods which I needed to borrow from ninject source since they were internal:
/// Ninject.Extensions.Interception.Infrastructure.Language
/// ExtensionsForIEnumerable
/// ExtensionsForMethodInfo
/// ExtensionsForICustomAttributeProvider
/// </summary>
internal static class MiscExtensions
{
/// <summary>
/// Converts all of the items in the specified series using the specified converter.
/// </summary>
/// <typeparam name="TInput">The type of items contained in the input list.</typeparam>
/// <typeparam name="TOutput">The type of items to return.</typeparam>
/// <param name="items">The series of items to convert.</param>
/// <param name="converter">The converter to use to convert the items.</param>
/// <returns>A list of the converted items.</returns>
public static IEnumerable<TOutput> Convert<TInput, TOutput>(this IEnumerable<TInput> items,
Func<TInput, TOutput> converter)
{
return items.Select(converter);
}
/// <summary>
/// Skips the last items where the count of skipped items is given by count.
/// </summary>
/// <typeparam name="T">The type of the enumerable.</typeparam>
/// <param name="source">The source.</param>
/// <param name="count">The count of skipped items.</param>
/// <returns>An enumerable that skippes the last items from the source enumerable.</returns>
public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source, int count)
{
var enumerator = source.GetEnumerator();
var items = new Queue<T>();
while (enumerator.MoveNext())
{
if (count-- <= 0)
{
yield return items.Dequeue();
}
items.Enqueue(enumerator.Current);
}
}
private const BindingFlags DefaultBindingFlags =
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance;
public static PropertyInfo GetPropertyFromMethod(this MethodInfo method, Type implementingType)
{
if (!method.IsSpecialName)
{
return null;
}
var isGetMethod = method.Name.Substring(0, 3) == "get";
var returnType = isGetMethod ? method.ReturnType : method.GetParameterTypes().Last();
var indexerTypes = isGetMethod ? method.GetParameterTypes() : method.GetParameterTypes().SkipLast(1);
return implementingType.GetProperty(method.Name.Substring(4), DefaultBindingFlags, null, returnType, indexerTypes.ToArray(), null);
}
public static PropertyInfo GetPropertyFromMethod(this MethodInfo method)
{
if (!method.IsSpecialName)
{
return null;
}
return method.DeclaringType.GetProperty(method.Name.Substring(4), DefaultBindingFlags);
}
/// <summary>
/// Gets the types of the parameters of the method.
/// </summary>
/// <param name="method">The method in question.</param>
/// <returns>An array containing the types of the method's parameters.</returns>
public static IEnumerable<Type> GetParameterTypes(this MethodBase method)
{
return method.GetParameters().Convert(p => p.ParameterType);
}
/// <summary>
/// Gets the method handle of either the method or its generic type definition, if it is
/// a generic method.
/// </summary>
/// <param name="method">The method in question.</param>
/// <returns>The runtime method handle for the method or its generic type definition.</returns>
public static RuntimeMethodHandle GetMethodHandle(this MethodBase method)
{
var mi = method as MethodInfo;
if (mi != null &&
mi.IsGenericMethod)
{
return mi.GetGenericMethodDefinition().MethodHandle;
}
return method.MethodHandle;
}
/// <summary>
/// Gets the first attribute of a specified type that decorates the member.
/// </summary>
/// <typeparam name="T">The type of attribute to search for.</typeparam>
/// <param name="member">The member to examine.</param>
/// <returns>The first attribute matching the specified type.</returns>
public static T GetOneAttribute<T>(this ICustomAttributeProvider member)
where T : Attribute
{
var attributes = member.GetCustomAttributes(typeof(T), true) as T[];
return (attributes == null) ||
(attributes.Length == 0)
? null
: attributes[0];
}
/// <summary>
/// Gets the first attribute of a specified type that decorates the member.
/// </summary>
/// <param name="member">The member to examine.</param>
/// <param name="type">The type of attribute to search for.</param>
/// <returns>The first attribute matching the specified type.</returns>
public static object GetOneAttribute(this ICustomAttributeProvider member, Type type)
{
object[] attributes = member.GetCustomAttributes(type, true);
return (attributes == null) ||
(attributes.Length == 0)
? null
: attributes[0];
}
/// <summary>
/// Gets an array of attributes matching the specified type that decorate the member.
/// </summary>
/// <typeparam name="T">The type of attribute to search for.</typeparam>
/// <param name="member">The member to examine.</param>
/// <returns>An array of attributes matching the specified type.</returns>
public static T[] GetAllAttributes<T>(this ICustomAttributeProvider member)
where T : Attribute
{
return member.GetCustomAttributes(typeof(T), true) as T[];
}
/// <summary>
/// Gets an array of attributes matching the specified type that decorate the member.
/// </summary>
/// <param name="member">The member to examine.</param>
/// <param name="type">The type of attribute to search for.</param>
/// <returns>An array of attributes matching the specified type.</returns>
public static object[] GetAllAttributes(this ICustomAttributeProvider member, Type type)
{
return member.GetCustomAttributes(type, true);
}
/// <summary>
/// Determines whether the member is decorated with one or more attributes of the specified type.
/// </summary>
/// <typeparam name="T">The type of attribute to search for.</typeparam>
/// <param name="member">The member to examine.</param>
/// <returns><see langword="True"/> if the member is decorated with one or more attributes of the type, otherwise <see langword="false"/>.</returns>
public static bool HasAttribute<T>(this ICustomAttributeProvider member)
where T : Attribute
{
return member.IsDefined(typeof(T), true);
}
/// <summary>
/// Determines whether the member is decorated with one or more attributes of the specified type.
/// </summary>
/// <param name="member">The member to examine.</param>
/// <param name="type">The type of attribute to search for.</param>
/// <returns><see langword="True"/> if the member is decorated with one or more attributes of the type, otherwise <see langword="false"/>.</returns>
public static bool HasAttribute(this ICustomAttributeProvider member, Type type)
{
return member.IsDefined(type, true);
}
/// <summary>
/// Determines whether the member is decorated with an attribute that matches the one provided.
/// </summary>
/// <typeparam name="T">The type of attribute to search for.</typeparam>
/// <param name="member">The member to examine.</param>
/// <param name="attributeToMatch">The attribute to match against.</param>
/// <returns><see langword="True"/> if the member is decorated with a matching attribute, otherwise <see langword="false"/>.</returns>
public static bool HasMatchingAttribute<T>(this ICustomAttributeProvider member, T attributeToMatch)
where T : Attribute
{
T[] attributes = member.GetAllAttributes<T>();
if ((attributes == null) ||
(attributes.Length == 0))
{
return false;
}
return attributes.Any(attribute => attribute.Match(attributeToMatch));
}
}
}
AlternateInterceptorRegistrationStrategy.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Ninject;
using Ninject.Components;
using Ninject.Extensions.Interception;
using Ninject.Extensions.Interception.Advice;
using Ninject.Extensions.Interception.Planning.Directives;
using Ninject.Extensions.Interception.Registry;
using Ninject.Planning;
using Ninject.Planning.Strategies;
namespace Common.Interception.Interceptors
{
/// <summary>
/// This is a derivation of InterceptorRegistrationStrategy from Ninject.Extensions.Interception.Planning.Strategies, merged with
/// http://stackoverflow.com/questions/6386461/ninject-intercept-any-method-with-certain-attribute
/// </summary>
public class AlternateInterceptorRegistrationStrategy<TAttribute, TInterceptor> : NinjectComponent, IPlanningStrategy
where TAttribute : Attribute
where TInterceptor : IInterceptor
{
protected const BindingFlags DefaultBindingFlags =
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance;
public AlternateInterceptorRegistrationStrategy(IAdviceFactory adviceFactory, IAdviceRegistry adviceRegistry, IKernel kernel)
{
AdviceFactory = adviceFactory;
AdviceRegistry = adviceRegistry;
Kernel = kernel;
}
public IKernel Kernel { get; set; }
public IAdviceFactory AdviceFactory { get; set; }
public IAdviceRegistry AdviceRegistry { get; set; }
public virtual void Execute(IPlan plan)
{
IEnumerable<MethodInfo> candidates = GetCandidateMethods(plan.Type);
RegisterClassInterceptors(plan.Type, plan, candidates);
foreach (MethodInfo method in candidates)
{
PropertyInfo property = method.GetPropertyFromMethod(plan.Type);
ICustomAttributeProvider provider = (ICustomAttributeProvider)property ?? method;
TAttribute[] attributes = provider.GetAllAttributes<TAttribute>();
if (attributes.Length == 0)
{
continue;
}
RegisterMethodInterceptor(plan.Type, method);
// Indicate that instances of the type should be proxied.
if (!plan.Has<ProxyDirective>())
{
plan.Add(new ProxyDirective());
}
}
}
protected virtual void RegisterClassInterceptors(Type type, IPlan plan, IEnumerable<MethodInfo> candidates)
{
var attributes = type.GetAllAttributes<TAttribute>();
if (attributes.Length == 0)
{
return;
}
foreach (MethodInfo method in candidates)
{
PropertyInfo property = method.GetPropertyFromMethod(type);
ICustomAttributeProvider provider = (ICustomAttributeProvider) property ?? method;
var config = Kernel.Get<IInterceptorConfig>();
if (config.DoNotInterceptAttribute == null)
{
// A "do not intercept" attribute wasn't defined in the config, so go ahead and register
RegisterMethodInterceptor(type, method);
}
else if (!provider.IsDefined(config.DoNotInterceptAttribute, true))
{
// The method wasn't decorated with the "do not intercept" attribute, so go ahead and register
RegisterMethodInterceptor(type, method);
}
}
if (!plan.Has<ProxyDirective>())
{
plan.Add(new ProxyDirective());
}
}
protected virtual void RegisterMethodInterceptor(Type type, MethodInfo method)
{
IAdvice advice = AdviceFactory.Create(method);
advice.Callback = request => request.Context.Kernel.Get<TInterceptor>();
var config = Kernel.TryGet<IInterceptorConfig>();
if (config != null)
{
advice.Order = config.GetOrder<TInterceptor>();
}
AdviceRegistry.Register(advice);
}
protected virtual IEnumerable<MethodInfo> GetCandidateMethods(Type type)
{
MethodInfo[] methods = type.GetMethods(DefaultBindingFlags);
return methods.Where(ShouldIntercept);
}
protected virtual bool ShouldIntercept(MethodInfo methodInfo)
{
return methodInfo.DeclaringType != typeof(object) &&
!methodInfo.IsPrivate;// &&
//!methodInfo.IsFinal;
}
}
}
IInterceptorConfig.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Ninject.Extensions.Interception;
namespace Common.Interception.Interceptors
{
public interface IInterceptorConfig
{
IInterceptorConfig SpecifyOrder<TInterceptor>(int order) where TInterceptor : IInterceptor;
IInterceptorConfig SpecifyDoNotInterceptAttribute<TAttribute>() where TAttribute : Attribute;
int GetOrder<TInterceptor>() where TInterceptor : IInterceptor;
Type DoNotInterceptAttribute { get; }
}
}
InterceptorConfig.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Ninject.Extensions.Interception;
namespace Common.Interception.Interceptors
{
public class InterceptorConfig : IInterceptorConfig
{
private readonly Dictionary<Type, int> _orderDictionary = new Dictionary<Type, int>();
public IInterceptorConfig SpecifyOrder<TInterceptor>(int order) where TInterceptor : IInterceptor
{
_orderDictionary.Add(typeof(TInterceptor), order);
return this;
}
public IInterceptorConfig SpecifyDoNotInterceptAttribute<TAttribute>() where TAttribute : Attribute
{
DoNotInterceptAttribute = typeof(TAttribute);
return this;
}
public int GetOrder<TInterceptor>() where TInterceptor : IInterceptor
{
return _orderDictionary[typeof(TInterceptor)];
}
public Type DoNotInterceptAttribute { get; private set; }
}
}
TraceInterceptor.cs - just a sample interceptor
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Ninject.Extensions.Interception;
namespace Common.Interception.Interceptors
{
public class TraceInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine("Enter Method");
invocation.Proceed();
Console.WriteLine("Exit Method");
}
}
}
Notes: Here is a simple console app that shows how to wire up the attributes/interceptors. This has dependencies on both Ninject.Extensions.Interception.DynamicProxy and Ninject.Extensions.Conventions (and all their required dependencies)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Common.Interception.Attributes;
using Common.Interception.Interceptors;
using Ninject;
using Ninject.Extensions.Conventions;
using Ninject.Planning.Strategies;
using SomeProject.Infrastructure;
namespace SomeProject.ConsoleApp
{
class Program
{
static void Main(string[] args)
{
var kernel = new StandardKernel();
kernel.Components.Add<IPlanningStrategy, AlternateInterceptorRegistrationStrategy<TraceAttribute, TraceInterceptor>>();
kernel.Components.Add<IPlanningStrategy, AlternateInterceptorRegistrationStrategy<AuthorizeAttribute, AuthorizeInterceptor>>();
// I needed a way to specify execution order and "DoNotIntercept" without deriving from attributes that would force ninject references all over my domain
// not 100% confident this is the best way - but it works
kernel.Bind<IInterceptorConfig>().ToConstant(new InterceptorConfig()
.SpecifyOrder<TraceInterceptor>(1)
.SpecifyOrder<AuthorizeInterceptor>(0)
.SpecifyDoNotInterceptAttribute<DoNotInterceptAttribute>());
// SomeProject.Infrastructure contains my service classes decorated with my custom attributes
kernel.Bind(x => x.FromAssembliesMatching("SomeProject.Infrastructure")
.SelectAllClasses()
.BindToSelf());
var a = kernel.Get<SomeServiceA>();
var b = kernel.Get<SomeServiceB>();
Console.WriteLine("Calling a.DoSomeStuff()...");
a.DoSomeStuff();
Console.WriteLine("Calling b.DoMyThing()...");
b.DoMyThing();
Console.WriteLine("Calling b.NowTraceThis()...");
b.NowTraceThis();
Console.ReadLine();
}
}
}
After scouring and scouring, I found that a similar question had been asked and answered here: Ninject Intercept any method with certain attribute?
I used this in conjunction with the source code of the InterceptorRegistrationStrategy class within Ninject.Extensions.Interception.Planning.Strategies to create a derivation which I am now using.
crickets

WCF multipart/form data with multiple files

I can't find any examples anywhere on how to process multipart/form data which contains multiple files in the request.
I'm trying to build a WCF service endpoint which contains a set of parameters in a text file, and then two image files, all in all including three files in one post. Using Fiddler, I am able to build the request and it looks like this:
HEADERS: Content-Type: multipart/form-data; boundary=-------------------------acebdf13572468
User-Agent: Fiddler
Host: dhiibews.brandonb.com
Content-Length: 107865
REQUESTBODY: ---------------------------acebdf13572468
Content-Disposition: form-data; name="Json"; filename="depositcheckrequest.txt"
Content-Type: application/json
<#INCLUDE *C:\depositcheckrequest.txt*#>
---------------------------acebdf13572468
Content-Disposition: form-data; name="frontImage"; filename="front.jpg"
Content-Type: image/jpeg
<#INCLUDE *C:\front.jpg*#>
---------------------------acebdf13572468
Content-Disposition: form-data; name="rearImage"; filename="rear.jpg"
Content-Type: image/jpeg
<#INCLUDE *C:\rear.jpg*#>
---------------------------acebdf13572468--
The include tags basically end up spitting out the file content as raw data.
I've been searching and all I can find are people who can tell me how to get the information for one file which in my case ends up being the first one. I don't want to get just one file though; as you can see I am trying to upload THREE files.
How do I parse multiple files in a single multipart/form data POST request?
Ok I figured out a solution. It turns out that the framework Nancyfx includes a multipart data processor which separates the boundaries for you into substreams. So I stole the relevant files from Nancy's source code and copied them into my project. The relevant files are:
HttpMultipart.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MyProjectNamespace
{
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
/// <summary>
/// Retrieves <see cref="HttpMultipartBoundary"/> instances from a request stream.
/// </summary>
public class HttpMultipart
{
private const byte LF = (byte)'\n';
private readonly byte[] boundaryAsBytes;
private readonly HttpMultipartBuffer readBuffer;
private readonly MemoryStream requestStream;
private readonly byte[] closingBoundaryAsBytes;
/// <summary>
/// Initializes a new instance of the <see cref="HttpMultipart"/> class.
/// </summary>
/// <param name="requestStream">The request stream to parse.</param>
/// <param name="boundary">The boundary marker to look for.</param>
public HttpMultipart(Stream requestStream, string boundary)
{
this.requestStream = new MemoryStream(ToByteArray(requestStream));
this.boundaryAsBytes = GetBoundaryAsBytes(boundary, false);
this.closingBoundaryAsBytes = GetBoundaryAsBytes(boundary, true);
this.readBuffer = new HttpMultipartBuffer(this.boundaryAsBytes, this.closingBoundaryAsBytes);
}
/// <summary>
/// Gets the <see cref="HttpMultipartBoundary"/> instances from the request stream.
/// </summary>
/// <returns>An <see cref="IEnumerable{T}"/> instance, containing the found <see cref="HttpMultipartBoundary"/> instances.</returns>
public IEnumerable<HttpMultipartBoundary> GetBoundaries()
{
return
(from boundaryStream in this.GetBoundarySubStreams()
select new HttpMultipartBoundary(boundaryStream)).ToList();
}
private static byte[] GetBoundaryAsBytes(string boundary, bool closing)
{
var boundaryBuilder = new StringBuilder();
boundaryBuilder.Append("--");
boundaryBuilder.Append(boundary);
if (closing)
{
boundaryBuilder.Append("--");
}
else
{
boundaryBuilder.Append('\r');
boundaryBuilder.Append('\n');
}
var bytes =
Encoding.ASCII.GetBytes(boundaryBuilder.ToString());
return bytes;
}
private IEnumerable<HttpMultipartSubStream> GetBoundarySubStreams()
{
var boundarySubStreams = new List<HttpMultipartSubStream>();
var boundaryStart = this.GetNextBoundaryPosition();
while (MultipartIsNotCompleted(boundaryStart))
{
var boundaryEnd = this.GetNextBoundaryPosition();
boundarySubStreams.Add(new HttpMultipartSubStream(
this.requestStream,
boundaryStart,
this.GetActualEndOfBoundary(boundaryEnd)));
boundaryStart = boundaryEnd;
}
return boundarySubStreams;
}
private bool MultipartIsNotCompleted(long boundaryPosition)
{
return boundaryPosition > -1 && !this.readBuffer.IsClosingBoundary;
}
//we add two because or the \r\n before the boundary
private long GetActualEndOfBoundary(long boundaryEnd)
{
if (this.CheckIfFoundEndOfStream())
{
return this.requestStream.Position - (this.readBuffer.Length + 2);
}
return boundaryEnd - (this.readBuffer.Length + 2);
}
private bool CheckIfFoundEndOfStream()
{
return this.requestStream.Position.Equals(this.requestStream.Length);
}
private long GetNextBoundaryPosition()
{
this.readBuffer.Reset();
while (true)
{
var byteReadFromStream = this.requestStream.ReadByte();
if (byteReadFromStream == -1)
{
return -1;
}
this.readBuffer.Insert((byte)byteReadFromStream);
if (this.readBuffer.IsFull && (this.readBuffer.IsBoundary || this.readBuffer.IsClosingBoundary))
{
return this.requestStream.Position;
}
if (byteReadFromStream.Equals(LF) || this.readBuffer.IsFull)
{
this.readBuffer.Reset();
}
}
}
private byte[] ToByteArray(Stream stream)
{
byte[] buffer = new byte[32768];
using (MemoryStream ms = new MemoryStream())
{
while (true)
{
int read = stream.Read(buffer, 0, buffer.Length);
if (read <= 0)
{
return ms.ToArray();
}
ms.Write(buffer, 0, read);
}
}
}
}
}
HttpMultipartBuffer.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MyProjectNamespace
{
public class HttpMultipartBuffer
{
private readonly byte[] boundaryAsBytes;
private readonly byte[] closingBoundaryAsBytes;
private readonly byte[] buffer;
private int position;
/// <summary>
/// Initializes a new instance of the <see cref="HttpMultipartBuffer"/> class.
/// </summary>
/// <param name="boundaryAsBytes">The boundary as a byte-array.</param>
/// <param name="closingBoundaryAsBytes">The closing boundary as byte-array</param>
public HttpMultipartBuffer(byte[] boundaryAsBytes, byte[] closingBoundaryAsBytes)
{
this.boundaryAsBytes = boundaryAsBytes;
this.closingBoundaryAsBytes = closingBoundaryAsBytes;
this.buffer = new byte[this.boundaryAsBytes.Length];
}
/// <summary>
/// Gets a value indicating whether the buffer contains the same values as the boundary.
/// </summary>
/// <value><see langword="true"/> if buffer contains the same values as the boundary; otherwise, <see langword="false"/>.</value>
public bool IsBoundary
{
get { return this.buffer.SequenceEqual(this.boundaryAsBytes); }
}
public bool IsClosingBoundary
{
get { return this.buffer.SequenceEqual(this.closingBoundaryAsBytes); }
}
/// <summary>
/// Gets a value indicating whether this buffer is full.
/// </summary>
/// <value><see langword="true"/> if buffer is full; otherwise, <see langword="false"/>.</value>
public bool IsFull
{
get { return this.position.Equals(this.buffer.Length); }
}
/// <summary>
/// Gets the the number of bytes that can be stored in the buffer.
/// </summary>
/// <value>The number of butes that can be stored in the buffer.</value>
public int Length
{
get { return this.buffer.Length; }
}
/// <summary>
/// Resets the buffer so that inserts happens from the start again.
/// </summary>
/// <remarks>This does not clear any previously written data, just resets the buffer position to the start. Data that is inserted after Reset has been called will overwrite old data.</remarks>
public void Reset()
{
this.position = 0;
}
/// <summary>
/// Inserts the specified value into the buffer and advances the internal position.
/// </summary>
/// <param name="value">The value to insert into the buffer.</param>
/// <remarks>This will throw an <see cref="ArgumentOutOfRangeException"/> is you attempt to call insert more times then the <see cref="Length"/> of the buffer and <see cref="Reset"/> was not invoked.</remarks>
public void Insert(byte value)
{
this.buffer[this.position++] = value;
}
}
}
HttpMultipartBoundary.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
namespace DHIWebSvc.Models
{
public class HttpMultipartBoundary
{
private const byte LF = (byte)'\n';
private const byte CR = (byte)'\r';
/// <summary>
/// Initializes a new instance of the <see cref="HttpMultipartBoundary"/> class.
/// </summary>
/// <param name="boundaryStream">The stream that contains the boundary information.</param>
public HttpMultipartBoundary(HttpMultipartSubStream boundaryStream)
{
this.Value = boundaryStream;
this.ExtractHeaders();
}
/// <summary>
/// Gets the contents type of the boundary value.
/// </summary>
/// <value>A <see cref="string"/> containing the name of the value if it is available; otherwise <see cref="string.Empty"/>.</value>
public string ContentType { get; private set; }
/// <summary>
/// Gets or the filename for the boundary value.
/// </summary>
/// <value>A <see cref="string"/> containing the filename value if it is available; otherwise <see cref="string.Empty"/>.</value>
/// <remarks>This is the RFC2047 decoded value of the filename attribute of the Content-Disposition header.</remarks>
public string Filename { get; private set; }
/// <summary>
/// Gets name of the boundary value.
/// </summary>
/// <remarks>This is the RFC2047 decoded value of the name attribute of the Content-Disposition header.</remarks>
public string Name { get; private set; }
/// <summary>
/// A stream containig the value of the boundary.
/// </summary>
/// <remarks>This is the RFC2047 decoded value of the Content-Type header.</remarks>
public HttpMultipartSubStream Value { get; private set; }
private void ExtractHeaders()
{
while (true)
{
var header =
this.ReadLineFromStream();
if (string.IsNullOrEmpty(header))
{
break;
}
if (header.StartsWith("Content-Disposition", StringComparison.CurrentCultureIgnoreCase))
{
this.Name = Regex.Match(header, #"name=""(?<name>[^\""]*)", RegexOptions.IgnoreCase).Groups["name"].Value;
this.Filename = Regex.Match(header, #"filename=""(?<filename>[^\""]*)", RegexOptions.IgnoreCase).Groups["filename"].Value;
}
if (header.StartsWith("Content-Type", StringComparison.InvariantCultureIgnoreCase))
{
this.ContentType = header.Split(new[] { ' ' }).Last().Trim();
}
}
this.Value.PositionStartAtCurrentLocation();
}
private string ReadLineFromStream()
{
var readBuffer = new StringBuilder();
while (true)
{
var byteReadFromStream = this.Value.ReadByte();
if (byteReadFromStream == -1)
{
return null;
}
if (byteReadFromStream.Equals(LF))
{
break;
}
readBuffer.Append((char)byteReadFromStream);
}
var lineReadFromStream =
readBuffer.ToString().Trim(new[] { (char)CR });
return lineReadFromStream;
}
}
}
and finally...
HttpMultipartSubStream.cs:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
namespace DHIWebSvc.Models
{
public class HttpMultipartSubStream : Stream
{
private readonly Stream stream;
private readonly long end;
private long start;
private long position;
/// <summary>
/// Initializes a new instance of the <see cref="HttpMultipartSubStream"/> class.
/// </summary>
/// <param name="stream">The stream to create the sub-stream ontop of.</param>
/// <param name="start">The start offset on the parent stream where the sub-stream should begin.</param>
/// <param name="end">The end offset on the parent stream where the sub-stream should end.</param>
public HttpMultipartSubStream(Stream stream, long start, long end)
{
this.stream = stream;
this.start = start;
this.position = start;
this.end = end;
}
/// <summary>
/// When overridden in a derived class, gets a value indicating whether the current stream supports reading.
/// </summary>
/// <returns><see langword="true"/> if the stream supports reading; otherwise, <see langword="false"/>.</returns>
public override bool CanRead
{
get { return true; }
}
/// <summary>
/// When overridden in a derived class, gets a value indicating whether the current stream supports seeking.
/// </summary>
/// <returns><see langword="true"/> if the stream supports seeking; otherwise, <see langword="false"/>.</returns>
public override bool CanSeek
{
get { return true; }
}
/// <summary>
/// When overridden in a derived class, gets a value indicating whether the current stream supports writing.
/// </summary>
/// <returns><see langword="true"/> if the stream supports writing; otherwise, <see langword="false"/>.</returns>
public override bool CanWrite
{
get { return false; }
}
/// <summary>
/// When overridden in a derived class, gets the length in bytes of the stream.
/// </summary>
/// <returns>A long value representing the length of the stream in bytes.</returns>
/// <exception cref="NotSupportedException">A class derived from Stream does not support seeking. </exception><exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed.</exception>
public override long Length
{
get
{
return this.end - this.start;
}
}
/// <summary>
/// When overridden in a derived class, gets or sets the position within the current stream.
/// </summary>
/// <returns>
/// The current position within the stream.
/// </returns>
/// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception><exception cref="T:System.NotSupportedException">The stream does not support seeking. </exception><exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exception><filterpriority>1</filterpriority>
public override long Position
{
get { return this.position - this.start; }
set { this.position = this.Seek(value, SeekOrigin.Begin); }
}
public void PositionStartAtCurrentLocation()
{
this.start = this.stream.Position;
}
/// <summary>
/// When overridden in a derived class, clears all buffers for this stream and causes any buffered data to be written to the underlying device.
/// </summary>
/// <remarks>In the <see cref="HttpMultipartSubStream"/> type this method is implemented as no-op.</remarks>
public override void Flush()
{
}
/// <summary>
/// When overridden in a derived class, reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
/// </summary>
/// <returns>The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. </returns>
/// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified byte array with the values between <paramref name="offset"/> and (<paramref name="offset"/> + <paramref name="count"/> - 1) replaced by the bytes read from the current source. </param>
/// <param name="offset">The zero-based byte offset in <paramref name="buffer"/> at which to begin storing the data read from the current stream.</param>
/// <param name="count">The maximum number of bytes to be read from the current stream. </param>
public override int Read(byte[] buffer, int offset, int count)
{
if (count > (this.end - this.position))
{
count = (int)(this.end - this.position);
}
if (count <= 0)
{
return 0;
}
this.stream.Position = this.position;
var bytesReadFromStream =
this.stream.Read(buffer, offset, count);
this.RepositionAfterRead(bytesReadFromStream);
return bytesReadFromStream;
}
/// <summary>
/// Reads a byte from the stream and advances the position within the stream by one byte, or returns -1 if at the end of the stream.
/// </summary>
/// <returns>The unsigned byte cast to an Int32, or -1 if at the end of the stream.</returns>
public override int ReadByte()
{
if (this.position >= this.end)
{
return -1;
}
this.stream.Position = this.position;
var byteReadFromStream = this.stream.ReadByte();
this.RepositionAfterRead(1);
return byteReadFromStream;
}
/// <summary>
/// When overridden in a derived class, sets the position within the current stream.
/// </summary>
/// <returns>The new position within the current stream.</returns>
/// <param name="offset">A byte offset relative to the <paramref name="origin"/> parameter.</param>
/// <param name="origin">A value of type <see cref="SeekOrigin"/> indicating the reference point used to obtain the new position.</param>
public override long Seek(long offset, SeekOrigin origin)
{
var subStreamRelativePosition =
this.CalculateSubStreamRelativePosition(origin, offset);
this.ThrowExceptionIsPositionIsOutOfBounds(subStreamRelativePosition);
this.position = this.stream.Seek(subStreamRelativePosition, SeekOrigin.Begin);
return this.position;
}
/// <summary>
/// When overridden in a derived class, sets the length of the current stream.
/// </summary>
/// <param name="value">The desired length of the current stream in bytes.</param>
/// <remarks>This will always throw a <see cref="InvalidOperationException"/> for the <see cref="HttpMultipartSubStream"/> type.</remarks>
public override void SetLength(long value)
{
throw new InvalidOperationException();
}
/// <summary>
/// When overridden in a derived class, writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.
/// </summary>
/// <param name="buffer">An array of bytes. This method copies <paramref name="count"/> bytes from <paramref name="buffer"/> to the current stream. </param>
/// <param name="offset">The zero-based byte offset in <paramref name="buffer"/> at which to begin copying bytes to the current stream. </param>
/// <param name="count">The number of bytes to be written to the current stream. </param>
/// <remarks>This will always throw a <see cref="InvalidOperationException"/> for the <see cref="HttpMultipartSubStream"/> type.</remarks>
public override void Write(byte[] buffer, int offset, int count)
{
throw new InvalidOperationException();
}
private void ThrowExceptionIsPositionIsOutOfBounds(long subStreamRelativePosition)
{
if (subStreamRelativePosition < 0 || subStreamRelativePosition > this.end)
{
throw new InvalidOperationException();
}
}
private long CalculateSubStreamRelativePosition(SeekOrigin origin, long offset)
{
var subStreamRelativePosition = 0L;
switch (origin)
{
case SeekOrigin.Begin:
subStreamRelativePosition = this.start + offset;
break;
case SeekOrigin.Current:
subStreamRelativePosition = this.position + offset;
break;
case SeekOrigin.End:
subStreamRelativePosition = this.end + offset;
break;
}
return subStreamRelativePosition;
}
private void RepositionAfterRead(int bytesReadFromStream)
{
if (bytesReadFromStream == -1)
{
this.position = this.end;
}
else
{
this.position += bytesReadFromStream;
}
}
}
}
The usage is relatively simple for what I was trying to do. I simply defined a boundary and only look for specific part names. So for the example in my question, I used the boundary "-------------------------acebdf13572468" with three files: Json, frontImage, and rearImage.

Error: Object references an unsaved transient instance

now I'm building a web application on asp-net using castle active record. When I was trying to save an entity with a has-many relation I got the error: "object references an unsaved transient instance - save the transient instance before flushing. Type: SupBoardModel.Entities.Output, Entity: SupBoardModel.Entities.Output#0". Searching on the web I found the causes of this error and some of its solutions but no one worked for me. The relation already have a property set to Cascade = ManyRelationCascadeEnum.All, one of a common suggestion around the web so... What is wrong here??? There is a piece of code for more information and understanding:
//In some part of my application
State state = new State();
//Set some fields
//...
state.Outputs = (List<Output>)Session["outputs"]; //Collection filled on a web form but not saved yet
//Here is the error
state.SaveAndFlush(); // Booom!!!!
//Part of a definition of Output(child entity)
[Serializable, ActiveRecord(Table = "SUPB_OUTPUTS")]
public class Output : ActiveRecordBase<Output>
{
private int _id;
/// <summary>
/// Primary key
/// </summary>
[PrimaryKey(PrimaryKeyType.SeqHiLo, "OUTPUT_ID", SequenceName = "SEQ_OUTPUT_ID")]
public int Id
{
get { return _id; }
set { _id = value; }
}
private string _label;
/// <summary>
/// Output custom label
/// </summary>
[Property("OUTPUT_LABEL")]
public string Label
{
get { return _label; }
set { _label = value; }
}
private State _state;
/// <summary>
/// StateRef owner (An output is only available for one state)
/// </summary>
[BelongsTo("OUTPUT_ID_STATE", Lazy = FetchWhen.OnInvoke)]
public State StateRef
{
get { return _state; }
set { _state = value; }
}
}
// Part of a definition of State(parent entity)
[Serializable, ActiveRecord(Table = "SUPB_STATES")]
public class State : ActiveRecordBase<State>
{
private int _id;
/// <summary>
/// Primary key
/// </summary>
[PrimaryKey(PrimaryKeyType.SeqHiLo, "STATE_ID", SequenceName = "SEQ_STATE_ID")]
public int Id
{
get { return _id; }
set { _id = value; }
}
private string _name;
/// <summary>
/// StateRef name
/// </summary>
[Property("STATE_NAME")]
public string Name
{
get { return _name; }
set { _name = value; }
}
private string _description;
/// <summary>
/// StateRef description
/// </summary>
[Property("STATE_DESC")]
public string Description
{
get { return _description; }
set { _description= value; }
}
private IList<Output> _outputs;
/// <summary>
/// State outputs (Columns to display data)
/// </summary>
[HasMany(typeof(Output), Table = "SUPB_OUTPUTS", ColumnKey = "OUTPUT_ID_STATE", Lazy = true, Cascade = ManyRelationCascadeEnum.All)]
public IList<Output> Outputs
{
get { return _outputs; }
set { _outputs = value; }
}
}
This error is make me crazy. I hope that is a way to save the State without save each Output before. The cascade attribute has no change for me, all options (All, AllDeleteOrfan, SaveUpdate) give me the same result. This case is very common and is mentioned on http://docs.castleproject.org/%28X%281%29S%28znghcs55lveeljjvqg21vni4%29%29/Active%20Record.Getting%20Started.ashx but is a mystery for me. Can any body help me??
Thanks
Menrique
Ok, I put Cascade=CascadeEnum.All in the StateRef field of the Output, something like that:
/// <summary>
/// StateRef owner (An output is only available for one state)
/// </summary>
[BelongsTo("OUTPUT_ID_STATE", Lazy = FetchWhen.OnInvoke, Cascade=CascadeEnum.All)]
public State StateRef
{
get { return _state; }
set { _state = value; }
}
And It WORK!!! So is not enough to put Cascade = ManyRelationCascadeEnum.All only in the relation hasMany of the parent entity, is necesary in the child entity too.
Thanks
Menrique

Using NInject to bind a generic interface, with a default if a binding for the generic type is not set

Imagine I have the following classes and interfaces:
public interface IService<T> { }
public class DefaultService<T> : IService<T> { }
public class FooService : IService<Foo> { }
public class BarService : IService<Bar> { }
I would then like to be able to get instances from the Kernel like this:
Kernel.Get<IService<Foo>>(); // Should return FooService
Kernel.Get<IService<Bar>>(); // Should return BarService
Kernel.Get<IService<Dog>>(); // Should return DefaultService
Kernel.Get<IService<Cat>>(); // Should return DefaultService
Kernel.Get<IService<Giraffe>>(); // Should return DefaultService
Is it possible to setup bindings using NInject (possibly using the Conventions extension), so that I don't have to manually bind every single possible implementation of IService?
I've been working on something similar recently and came up with somewhat simpler solution of your problem (although a bit weaker).
What should suffice is to bind a generic implementation (DefaultService) to the generic interface, and concrete implementations (FooService, BarService) to the concrete interfaces. When you ask for a concrete instance of the interface, Ninject resolves whether you defined the concrete binding. If you did, it gives you the appropriate instance, otherwise it falls through to the generic binding. The following code should do the trick.
var kernel = new StandardKernel();
kernel.Bind(typeof(IService<>)).To(typeof(DefaultService<>));
kernel.Bind<IService<Foo>>().To<FooService>();
kernel.Bind<IService<Bar>>().To<BarService>();
EDIT:
The concept works throughout the whole Ninject, so you can use it along with Extensions.Conventions as well.
e.g. define the following:
public class Foo{}
public class Bar{}
public class Dog{}
public interface IService<T>{}
public class DefaultService<T> : IService<T>{}
public class FooService : IService<Foo>{}
public class BarService : IService<Bar>{}
use conventions to bind the services:
kernel.Bind(x => x.FromThisAssembly()
.SelectAllClasses()
.InheritedFrom(typeof(IService<>))
.BindSingleInterface());
and create and check the appropriate services:
Assert.IsInstanceOf<BarService>(kernel.Get<IService<Bar>>());
Assert.IsInstanceOf<FooService>(kernel.Get<IService<Foo>>());
Assert.IsInstanceOf<DefaultService<Dog>>(kernel.Get<IService<Dog>>());
I took the liberty of refactoring the answer from #cbp, so that it works for the new IBindingGenerator signature in Ninject v3 conventions. It's pretty much replacing the Process() method signature with the CreateBindings() method signature, but I didn't test this, so there's a chance you'll have to tweak it a bit if you use it.
/// <summary>
/// Creates bindings on open generic types.
/// This is similar to the out-of-the-box
/// <see cref="GenericBindingGenerator" />,
/// but allows a default class to be
/// specified if no other bindings can be found.
/// See the test case for usages.
/// </summary>
public class GenericBindingGeneratorWithDefault : IBindingGenerator
{
private static readonly Type TypeOfObject = typeof(object);
private readonly Type _contractType;
private readonly Dictionary<Type, Type> _cachedBindings;
private readonly Type _defaultType;
public GenericBindingGeneratorWithDefault(Type contractType, Type defaultType)
{
if (!(contractType.IsGenericType || contractType.ContainsGenericParameters))
throw new ArgumentException("The contract must be an open generic type.",
"contractType");
_cachedBindings = new Dictionary<Type, Type>();
_contractType = contractType;
_defaultType = defaultType;
}
/// <summary>
/// Creates the bindings for a type.
/// </summary>
/// <param name="type">The type for which the bindings are created.</param>
/// <param name="bindingRoot">The binding root that is used to create the bindings.</param>
/// <returns>
/// The syntaxes for the created bindings to configure more options.
/// </returns>
public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(Type type, IBindingRoot bindingRoot)
{
if (type == null) throw new ArgumentNullException("type");
if (bindingRoot == null) throw new ArgumentNullException("bindingRoot");
if (type.IsInterface || type.IsAbstract) yield break;
if (type == _defaultType)
{
yield return bindingRoot.Bind(_contractType).ToMethod(
ctx =>
{
Type requestedType = ctx.Request.Service;
Type resolution = _cachedBindings.ContainsKey(requestedType)
? _cachedBindings[requestedType]
: _defaultType.MakeGenericType(ctx.GenericArguments);
return ctx.Kernel.Get(resolution);
});
}
else
{
Type interfaceType = ResolveClosingInterface(type);
if (interfaceType != null)
{
yield return bindingRoot.Bind(type).To(_cachedBindings[interfaceType]);
}
}
}
/// <summary>
/// Resolves the closing interface.
/// </summary>
/// <param name="targetType">Type of the target.</param>
/// <returns></returns>
private Type ResolveClosingInterface(Type targetType)
{
if (targetType.IsInterface || targetType.IsAbstract) return null;
do
{
Type[] interfaces = targetType.GetInterfaces();
foreach (Type #interface in interfaces)
{
if (!#interface.IsGenericType) continue;
if (#interface.GetGenericTypeDefinition() == _contractType)
{
return #interface;
}
}
targetType = targetType.BaseType;
} while (targetType != TypeOfObject);
return null;
}
}
I figured out how to do this after a couple of hours messing around with NInject Convention's GenericBindingGenerator.
If anyone is interested I can post it.
Update:
/// <summary>
/// Creates bindings on open generic types.
/// This is similar to the out-of-the-box <see cref="GenericBindingGenerator" />, but allows a default class to be
/// specified if no other bindings can be found. See the test case for usages.
/// </summary>
public class GenericBindingGeneratorWithDefault : IBindingGenerator
{
private static readonly Type TYPE_OF_OBJECT = typeof (object);
private readonly Type _contractType;
private Dictionary<Type, Type> _cachedBindings = new Dictionary<Type, Type>();
private readonly Type _defaultType;
public GenericBindingGeneratorWithDefault(Type contractType, Type defaultType)
{
if ( !( contractType.IsGenericType || contractType.ContainsGenericParameters ) )
{
throw new ArgumentException( "The contract must be an open generic type.", "contractType" );
}
_contractType = contractType;
_defaultType = defaultType;
}
/// <summary>
/// Processes the specified type creating kernel bindings.
/// </summary>
/// <param name="type">The type to process.</param>
/// <param name="scopeCallback">the scope callback.</param>
/// <param name="kernel">The kernel to configure.</param>
public void Process( Type type, Func<IContext, object> scopeCallback, IKernel kernel )
{
if (type == _defaultType)
{
kernel.Bind(_contractType).ToMethod(
ctx =>
{
var requestedType = ctx.Request.Service;
var resolution = _cachedBindings.ContainsKey(requestedType)
? _cachedBindings[requestedType]
: _defaultType.MakeGenericType(ctx.GenericArguments);
return ctx.Kernel.Get(resolution);
});
}
else
{
Type interfaceType = ResolveClosingInterface(type);
if (interfaceType != null)
{
_cachedBindings[interfaceType] = type;
}
}
}
/// <summary>
/// Resolves the closing interface.
/// </summary>
/// <param name="targetType">Type of the target.</param>
/// <returns></returns>
public Type ResolveClosingInterface( Type targetType )
{
if ( targetType.IsInterface || targetType.IsAbstract )
{
return null;
}
do
{
Type[] interfaces = targetType.GetInterfaces();
foreach ( Type #interface in interfaces )
{
if ( !#interface.IsGenericType )
{
continue;
}
if ( #interface.GetGenericTypeDefinition() == _contractType )
{
return #interface;
}
}
targetType = targetType.BaseType;
} while ( targetType != TYPE_OF_OBJECT );
return null;
}
}

NHibernate: Custom Property Accessor's Get and Set methods not being called

I'm attempting to map a database field ("LS_RECNUM") possible values of NULL, 'M' and 'F' to a property with a Gender enumeration type.
The mapping looks like this:
Map(x => x.Gender).Column("LS_GENDER").Access.Using<GenderPropertyAccessor>();
...and the GenderPropertyAccessor class looks like this:
using System;
using System.Collections;
using System.Reflection;
using Kctc;
using NHibernate.Engine;
using NHibernate.Properties;
public class GenderPropertyAccessor : IPropertyAccessor
{
#region Setter
private class GenderGetterSetter : IGetter, ISetter
{
PropertyInfo _property;
public GenderGetterSetter(PropertyInfo property)
{
if (property == null) throw new ArgumentNullException("property");
if (property.PropertyType != typeof(Gender)) throw new ArgumentException("property");
_property = property;
}
public void Set(object target, object value) //Convert string to enum
{
_property.SetValue(target, GetGenderFromString(value), null);
}
public object Get(object target) //Convert enum back to string
{
Gender gender = (Gender)_property.GetValue(target, null);
return SetGenderToString(gender);
}
/// <summary>
/// Interprets the supplied string as a gender.
/// </summary>
/// <param name="strGender">The gender as either 'F' or 'M'.</param>
/// <returns></returns>
private Gender GetGenderFromString(object strGender)
{
if (strGender == null) return Gender.Unknown;
switch (strGender.ToString().ToLower())
{
case "f":
return Gender.Female;
case "m":
return Gender.Male;
default:
return Gender.Unknown;
}
}
/// <summary>
/// Sets the supplied Gender to the appropriate 'M' or 'F' value.
/// </summary>
/// <param name="objGender">The gender.</param>
/// <returns></returns>
private string SetGenderToString(object objGender)
{
Gender gender = (Gender) objGender;
switch (gender)
{
case Gender.Female:
return "F";
case Gender.Male:
return "M";
default:
return null;
}
}
public MethodInfo Method
{
get { return null; }
}
public string PropertyName
{
get { return _property.Name; }
}
public object GetForInsert(object owner, IDictionary mergeMap, ISessionImplementor session)
{
return Get(owner);
}
public Type ReturnType
{
get { return typeof(byte[]); }
}
}
#endregion
public IGetter GetGetter(Type theClass, string propertyName)
{
return new GenderGetterSetter(theClass.GetProperty(propertyName));
}
public ISetter GetSetter(Type theClass, string propertyName)
{
return new GenderGetterSetter(theClass.GetProperty(propertyName));
}
public bool CanAccessThroughReflectionOptimizer
{
get { return false; }
}
}
Not being particularly familiar with reflection, I'm not at all sure that the Get and Set methods have been implemented correctly.
When I try this, I still get an error 'Can't parse F as Gender'. I've tried debugging the GenderPropertyAccessor class. The relevant line (shown above) in the mapping file is executing correctly, as is the constructor for the GenderGetterSetter class, but the Get and Set methods are never called!!!
Can anyone tell me what I might be doing wrong?
I would use an implementation of IUserType for this. Here's a good simple example. In the NullSafeGet and NullSafeSet methods you will mutate the string to an enum and back, respectively. It's also critical that your Equals implementation is correct in order for NHibernate to detect changes.
Mapping the property to use a custom user type is easy:
Map(x => x.Gender).Column("LS_GENDER").CustomType(typeof(MyUserType));