Lambda string as VARCHAR - nhibernate

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>();

Related

HTTP end point property string starts with "is" will get omit [duplicate]

This might be a duplicate. But I cannot find a solution to my Problem.
I have a class
public class MyResponse implements Serializable {
private boolean isSuccess;
public boolean isSuccess() {
return isSuccess;
}
public void setSuccess(boolean isSuccess) {
this.isSuccess = isSuccess;
}
}
Getters and setters are generated by Eclipse.
In another class, I set the value to true, and write it as a JSON string.
System.out.println(new ObjectMapper().writeValueAsString(myResponse));
In JSON, the key is coming as {"success": true}.
I want the key as isSuccess itself. Is Jackson using the setter method while serializing? How do I make the key the field name itself?
This is a slightly late answer, but may be useful for anyone else coming to this page.
A simple solution to changing the name that Jackson will use for when serializing to JSON is to use the #JsonProperty annotation, so your example would become:
public class MyResponse implements Serializable {
private boolean isSuccess;
#JsonProperty(value="isSuccess")
public boolean isSuccess() {
return isSuccess;
}
public void setSuccess(boolean isSuccess) {
this.isSuccess = isSuccess;
}
}
This would then be serialised to JSON as {"isSuccess":true}, but has the advantage of not having to modify your getter method name.
Note that in this case you could also write the annotation as #JsonProperty("isSuccess") as it only has the single value element
I recently ran into this issue and this is what I found. Jackson will inspect any class that you pass to it for getters and setters, and use those methods for serialization and deserialization. What follows "get", "is" and "set" in those methods will be used as the key for the JSON field ("isValid" for getIsValid and setIsValid).
public class JacksonExample {
private boolean isValid = false;
public boolean getIsValid() {
return isValid;
}
public void setIsValid(boolean isValid) {
this.isValid = isValid;
}
}
Similarly "isSuccess" will become "success", unless renamed to "isIsSuccess" or "getIsSuccess"
Read more here: http://www.citrine.io/blog/2015/5/20/jackson-json-processor
Using both annotations below, forces the output JSON to include is_xxx:
#get:JsonProperty("is_something")
#param:JsonProperty("is_something")
When you are using Kotlin and data classes:
data class Dto(
#get:JsonProperty("isSuccess") val isSuccess: Boolean
)
You might need to add #param:JsonProperty("isSuccess") if you are going to deserialize JSON as well.
EDIT: If you are using swagger-annotations to generate documentation, the property will be marked as readOnly when using #get:JsonProperty. In order to solve this, you can do:
#JsonAutoDetect(isGetterVisibility = JsonAutoDetect.Visibility.NONE)
data class Dto(
#field:JsonProperty(value = "isSuccess") val isSuccess: Boolean
)
You can configure your ObjectMapper as follows:
mapper.setPropertyNamingStrategy(new PropertyNamingStrategy() {
#Override
public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
{
if(method.hasReturnType() && (method.getRawReturnType() == Boolean.class || method.getRawReturnType() == boolean.class)
&& method.getName().startsWith("is")) {
return method.getName();
}
return super.nameForGetterMethod(config, method, defaultName);
}
});
I didn't want to mess with some custom naming strategies, nor re-creating some accessors.
The less code, the happier I am.
This did the trick for us :
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
#JsonIgnoreProperties({"success", "deleted"}) // <- Prevents serialization duplicates
public class MyResponse {
private String id;
private #JsonProperty("isSuccess") boolean isSuccess; // <- Forces field name
private #JsonProperty("isDeleted") boolean isDeleted;
}
Building upon Utkarsh's answer..
Getter names minus get/is is used as the JSON name.
public class Example{
private String radcliffe;
public getHarryPotter(){
return radcliffe;
}
}
is stored as { "harryPotter" : "whateverYouGaveHere" }
For Deserialization, Jackson checks against both the setter and the field name.
For the Json String { "word1" : "example" }, both the below are valid.
public class Example{
private String word1;
public setword2( String pqr){
this.word1 = pqr;
}
}
public class Example2{
private String word2;
public setWord1(String pqr){
this.word2 = pqr ;
}
}
A more interesting question is which order Jackson considers for deserialization. If i try to deserialize { "word1" : "myName" } with
public class Example3{
private String word1;
private String word2;
public setWord1( String parameter){
this.word2 = parameter ;
}
}
I did not test the above case, but it would be interesting to see the values of word1 & word2 ...
Note: I used drastically different names to emphasize which fields are required to be same.
You can change primitive boolean to java.lang.Boolean (+ use #JsonPropery)
#JsonProperty("isA")
private Boolean isA = false;
public Boolean getA() {
return this.isA;
}
public void setA(Boolean a) {
this.isA = a;
}
Worked excellent for me.
If you are interested in handling 3rd party classes not under your control (like #edmundpie mentioned in a comment) then you add Mixin classes to your ObjectMapper where the property/field names should match the ones from your 3rd party class:
public class MyStack32270422 {
public static void main(String[] args) {
ObjectMapper om3rdParty = new ObjectMapper();
om3rdParty .addMixIn(My3rdPartyResponse.class, MixinMyResponse.class);
// add further mixins if required
String jsonString = om3rdParty.writeValueAsString(new My3rdPartyResponse());
System.out.println(jsonString);
}
}
class MixinMyResponse {
// add all jackson annotations here you want to be used when handling My3rdPartyResponse classes
#JsonProperty("isSuccess")
private boolean isSuccess;
}
class My3rdPartyResponse{
private boolean isSuccess = true;
// getter and setter here if desired
}
Basically you add all your Jackson annotations to your Mixin classes as if you would own the class. In my opinion quite a nice solution as you don't have to mess around with checking method names starting with "is.." and so on.
there is another method for this problem.
just define a new sub-class extends PropertyNamingStrategy and pass it to ObjectMapper instance.
here is a code snippet may be help more:
mapper.setPropertyNamingStrategy(new PropertyNamingStrategy() {
#Override
public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {
String input = defaultName;
if(method.getName().startsWith("is")){
input = method.getName();
}
//copy from LowerCaseWithUnderscoresStrategy
if (input == null) return input; // garbage in, garbage out
int length = input.length();
StringBuilder result = new StringBuilder(length * 2);
int resultLength = 0;
boolean wasPrevTranslated = false;
for (int i = 0; i < length; i++)
{
char c = input.charAt(i);
if (i > 0 || c != '_') // skip first starting underscore
{
if (Character.isUpperCase(c))
{
if (!wasPrevTranslated && resultLength > 0 && result.charAt(resultLength - 1) != '_')
{
result.append('_');
resultLength++;
}
c = Character.toLowerCase(c);
wasPrevTranslated = true;
}
else
{
wasPrevTranslated = false;
}
result.append(c);
resultLength++;
}
}
return resultLength > 0 ? result.toString() : input;
}
});
The accepted answer won't work for my case.
In my case, the class is not owned by me. The problematic class comes from 3rd party dependencies, so I can't just add #JsonProperty annotation in it.
To solve it, inspired by #burak answer above, I created a custom PropertyNamingStrategy as follow:
mapper.setPropertyNamingStrategy(new PropertyNamingStrategy() {
#Override
public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
{
if (method.getParameterCount() == 1 &&
(method.getRawParameterType(0) == Boolean.class || method.getRawParameterType(0) == boolean.class) &&
method.getName().startsWith("set")) {
Class<?> containingClass = method.getDeclaringClass();
String potentialFieldName = "is" + method.getName().substring(3);
try {
containingClass.getDeclaredField(potentialFieldName);
return potentialFieldName;
} catch (NoSuchFieldException e) {
// do nothing and fall through
}
}
return super.nameForSetterMethod(config, method, defaultName);
}
#Override
public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
{
if(method.hasReturnType() && (method.getRawReturnType() == Boolean.class || method.getRawReturnType() == boolean.class)
&& method.getName().startsWith("is")) {
Class<?> containingClass = method.getDeclaringClass();
String potentialFieldName = method.getName();
try {
containingClass.getDeclaredField(potentialFieldName);
return potentialFieldName;
} catch (NoSuchFieldException e) {
// do nothing and fall through
}
}
return super.nameForGetterMethod(config, method, defaultName);
}
});
Basically what this does is, before serializing and deserializing, it checks in the target/source class which property name is present in the class, whether it is isEnabled or enabled property.
Based on that, the mapper will serialize and deserialize to the property name that is exist.

How can I bind a comma separated list in a URL to an array of objects?

I have a class named VerseRangeReference that has the properties Chapter, FirstVerse and LastVerse.
I have decorated it with a TypeConverterAttribute [TypeConverter(typeof(VerseRangeReferenceConverter))]
I have an action on a controller like this
public Task<ViewResult> Verses(VerseRangeReference[] verses)
But the value of verses is always a single element with the value null. Here is my type converter
public class VerseRangeReferenceConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value == null)
throw new ArgumentNullException(nameof(value));
if (value.GetType() == typeof(string))
{
string source = (string)value;
return VerseRangeReference.ParseMultiple(source);
}
return null;
}
}
The result of VerseRangeReference.ParseMultiple(source) is a valid array of instances of VerseRange.
I had to implement a custom model binder. If someone can think of a way to do this with a TypeConverter then I will accept that answer instead because model binders are more complicated.
public class VerseRangeReferenceArrayModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
string modelName = bindingContext.ModelName;
ValueProviderResult valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);
if (valueProviderResult != ValueProviderResult.None)
{
VerseRangeReference[] verseRangeReferences = VerseRangeReference.ParseMultiple(valueProviderResult.FirstValue);
bindingContext.Result = ModelBindingResult.Success(verseRangeReferences);
}
return Task.CompletedTask;
}
}
public class VerseRangerReferenceArrayModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context.Metadata.ModelType == typeof(VerseRangeReference[]))
return new BinderTypeModelBinder(typeof(VerseRangeReferenceArrayModelBinder));
return null;
}
}
This must be registered.
services.AddMvc(options =>
{
options.ModelBinderProviders.Insert(0, new VerseRangerReferenceArrayModelBinderProvider());
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
You can use a type converter to bind a comma separated string to a sequence of values. However, the type converter should convert from the string to the sequence directly. This means that the type converter should be configured for something like IEnumerable<T> or T[]. To simplify matters I will continue my explanation for IEnumerable<int> but if you want to use arrays instead you should just make sure that the type converter converts to an array instead of something that implements IEnumerable<T>.
You can configure a type converter for IEnumerable<int> using TypeDescriptor.AddAttributes:
TypeDescriptor.AddAttributes(
typeof(IEnumerable<int>),
new TypeConverterAttribute(typeof(EnumerableIntTypeConverter)));
This configures EnumerableIntTypeConverter as a type converter that can convert IEnumerable<int>.
This call has to be made when the process starts and in the case of ASP.NET Core this can conveniently be done in the Startup.Configure method.
Here is the EnumerableIntTypeConverter that converts the comma separated string of numbers to a list of ints:
internal class EnumerableIntTypeConverter : TypeConverter
{
private const char Separator = ',';
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
=> sourceType == typeof(string);
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (!(value is string #string))
throw new NotSupportedException($"{GetType().Name} cannot convert from {(value != null ? value.GetType().FullName : "(null)")}.");
if (#string.Length == 0)
return Enumerable.Empty<int>();
var numbers = new List<int>();
var start = 0;
var end = GetEnd(#string, start);
while (true)
{
if (!int.TryParse(
#string.AsSpan(start, end - start),
NumberStyles.AllowLeadingSign,
culture,
out var number))
throw new FormatException($"{GetType().Name} cannot parse string with invalid format.");
numbers.Add(number);
if (end == #string.Length)
break;
start = end + 1;
end = GetEnd(#string, start);
}
return numbers;
}
private static int GetEnd(string #string, int start)
{
var end = #string.IndexOf(Separator, start);
return end >= 0 ? end : #string.Length;
}
}
The parsing uses System.Memory to avoid allocating a new string for each number in the list. If your framework doesn't have the int.TryParse overload that accepts a Span<char> you can use string.Substring instead.

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();

Cast route parameter in Nancy is always null

I have a Nancy module which uses a function which expects as parameters a string (a captured pattern from a route) and a method group. When trying to pass the parameter directly it will not compile as I "cannot use a method group as an argument to a dynamically dispatched operation".
I have created a second route which attempts to cast the dynamic to a string, but this always returns null.
using System;
using Nancy;
public class MyModule : NancyModule
{
public MyModule()
{
//Get["/path/{Name}/action"] = parameters =>
// {
// return MyMethod(parameters.Name, methodToBeCalled); // this does not compile
// };
Get["/path/{Name}/anotherAction"] = parameters =>
{
return MyMethod(parameters.Name as string, anotherMethodToBeCalled);
};
}
public Response MyMethod(string name, Func<int> doSomething)
{
doSomething();
return Response.AsText(string.Format("Hello {0}", name));
}
public int methodToBeCalled()
{
return -1;
}
public int anotherMethodToBeCalled()
{
return 1;
}
}
Tested with the following class in a separate project:
using System;
using Nancy;
using Nancy.Testing;
using NUnit.Framework;
[TestFixture]
public class MyModuleTest
{
Browser browser;
[SetUp]
public void SetUp()
{
browser = new Browser(with =>
{
with.Module<MyModule>();
with.EnableAutoRegistration();
});
}
[Test]
public void Can_Get_View()
{
// When
var result = browser.Get("/path/foobar/anotherAction", with => with.HttpRequest());
// Then
Assert.AreEqual(HttpStatusCode.OK, result.StatusCode);
Assert.AreEqual("Hello foobar", result.Body.AsString()); //fails as parameters.Name is always null when cast to a string
}
}
You can find the whole test over on github
I've had similar issues when using 'as' so I tend to use explicitly cast it:
return MyMethod((string)parameters.Name, anotherMethodToBeCalled);
Also I think there was a bug raised with the casing on parameters, but I think it's better to keep them lowercase:
Get["/path/{name}/anotherAction"]
(string)parameters.name
Your code works for me with upper case and lowercase, using the explicit cast.

How do I create an NHibernate IUserType for a Time in Sql Server 2008/2012?

I am trying to create an NHibernate IUserType for the Noda Time LocalTime type which would logically map to a time type in Sql Server 2008/2012. I am able to get values saving and loading from the database. However, I can't write queries involving comparison of local times like _session.Query<SchedulingTemplate>().Where(x => x.Start < end && x.End >= start) gives the error SqlException (0x80131904): The data types time and datetime are incompatible in the less than operator.
The relevant code from my user type is:
public Type ReturnedType
{
get { return typeof(LocalTime); }
}
public override object NullSafeGet(IDataReader rs, string[] names, object owner)
{
var dbValue = NHibernateUtil.Time.NullSafeGet(rs, names);
if(dbValue == null)
return null;
return LocalDateTime.FromDateTime((DateTime)dbValue).TimeOfDay;
}
public override void NullSafeSet(IDbCommand cmd, object value, int index)
{
if(value == null)
NHibernateUtil.Time.NullSafeSet(cmd, null, index);
else
NHibernateUtil.Time.NullSafeSet(cmd, ((LocalTime)value).LocalDateTime.ToDateTimeUnspecified(), index);
}
public override SqlType[] SqlTypes
{
get { return new[] { SqlTypeFactory.Time }; }
}
The problem is that despite the above code indicating the database type is a time, it generates the following query (per Sql Profiler):
exec sp_executesql N'select [...] from [SchedulingTemplate] scheduling0_ where scheduling0_.Start<#p0 and scheduling0_.[End]>=#p1',N'#p0 datetime,#p1 datetime',#p0='1753-01-01 20:00:00',#p1='1753-01-01 06:00:00'
(note I omitted the select list for brevity)
Notice that the type and value of the parameters is being treated as datetime.
This appears to be very similar to two NH bugs that have been closed https://nhibernate.jira.com/browse/NH-2661 and https://nhibernate.jira.com/browse/NH-2660.
I tried to use NHibernateUtil.TimeAsTimeSpan and that didn't seem to work either. It generated exactly the same query which surprised me. I am thinking maybe the issue described in NH-2661 also exists for user types and was not fixed for that?
I am using NHibernate v3.3.1.400 and Noda Time 1.0.0-beta2
Following #Firo's advice, I worked from the time SqlType and came up with this:
using NHibernate;
using NHibernate.Dialect;
using NHibernate.SqlTypes;
using NHibernate.Type;
using NodaTime;
using NodaTime.Text;
using System;
using System.Data;
using System.Data.SqlClient;
[Serializable]
public class LocalTimeType : PrimitiveType, IIdentifierType
{
private readonly LocalTimePattern _timePattern = LocalTimePattern.CreateWithInvariantCulture("h:mm:ss tt");
public LocalTimeType() : base(SqlTypeFactory.Time) { }
public override string Name
{
get { return "LocalTime"; }
}
public override object Get(IDataReader rs, int index)
{
try
{
if (rs[index] is TimeSpan) //For those dialects where DbType.Time means TimeSpan.
{
var time = (TimeSpan)rs[index];
return LocalTime.Midnight + Period.FromTicks(time.Ticks);
}
var dbValue = Convert.ToDateTime(rs[index]);
return LocalDateTime.FromDateTime(dbValue).TimeOfDay;
}
catch (Exception ex)
{
throw new FormatException(string.Format("Input string '{0}' was not in the correct format.", rs[index]), ex);
}
}
public override object Get(IDataReader rs, string name)
{
return Get(rs, rs.GetOrdinal(name));
}
public override Type ReturnedClass
{
get { return typeof(LocalTime); }
}
public override void Set(IDbCommand st, object value, int index)
{
var parameter = ((SqlParameter)st.Parameters[index]);
parameter.SqlDbType = SqlDbType.Time; // HACK work around bad behavior, M$ says not ideal, but as intended, NH says this is a bug in MS may work around eventually
parameter.Value = new TimeSpan(((LocalTime)value).TickOfDay);
}
public override bool IsEqual(object x, object y)
{
return Equals(x, y);
}
public override int GetHashCode(object x, EntityMode entityMode)
{
return x.GetHashCode();
}
public override string ToString(object val)
{
return _timePattern.Format((LocalTime)val);
}
public object StringToObject(string xml)
{
return string.IsNullOrEmpty(xml) ? null : FromStringValue(xml);
}
public override object FromStringValue(string xml)
{
return _timePattern.Parse(xml).Value;
}
public override Type PrimitiveClass
{
get { return typeof(LocalTime); }
}
public override object DefaultValue
{
get { return new LocalTime(); }
}
public override string ObjectToSQLString(object value, Dialect dialect)
{
return "'" + _timePattern.Format((LocalTime)value) + "'";
}
}
The key code is in the Set method where is says:
var parameter = ((SqlParameter)st.Parameters[index]);
parameter.SqlDbType = SqlDbType.Time;
This is needed because the MS data provider takes setting the DbType to DbType.Time to mean the underlying type should be DateTime. You must set the SqlDbType to time for it to work.