I am trying to set up a MassTransit request/response scenario. The issue is that the message never arrives at the consumer. I get a “timeout waiting for response” error on PublishRequest. No other errors are showing up in the log file. Messages are being created in msmq.
Exception information:
Exception type: TargetInvocationException
Exception message: Exception has been thrown by the target of an invocation. at System.RuntimeTypeHandle.CreateInstance(RuntimeType
type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached,
RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck) at
System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean
skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark) at
System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly,
Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
at System.Activator.CreateInstance(Type type, Boolean nonPublic) at
System.Activator.CreateInstance(Type type) at
System.Web.Mvc.DefaultControllerFactory.DefaultControllerActivator.Create(RequestContext
requestContext, Type controllerType)
Timeout waiting for response, RequestId:
08cfa243-4a88-ba3a-20cf-307f54910000 at
MassTransit.RequestResponse.RequestImpl1.Wait() in
d:\BuildAgent-03\work\8d1373c869590c5b\src\MassTransit\RequestResponse\RequestImpl.cs:line
124 at
MassTransit.RequestResponseExtensions.PublishRequest[TRequest](IServiceBus
bus, TRequest message, Action1 configureCallback) in
d:\BuildAgent-03\work\8d1373c869590c5b\src\MassTransit\RequestResponseExtensions.cs:line
31 at Producer.Website.Controllers.AccountController..ctor() in
c:\Users\rick\Documents\Visual Studio
2012\Projects\ConsumerTest1\Producer.Website\Controllers\AccountController.cs:line
56
Producer setup:
_bus = ServiceBusFactory.New(sbc =>
{
sbc.UseMsmq();
sbc.VerifyMsmqConfiguration();
sbc.UseMulticastSubscriptionClient();
sbc.SetNetwork("Test");
sbc.ReceiveFrom("msmq://localhost/consumer_test_1");
});
Producer send message:
var message = new AccountNewMessage()
{
CorrelationId = CombGuid.Generate(),
UserName = “blah blah”,
Password = “yada yada”
};
this._bus.PublishRequest(message, r =>
{
r.SetTimeout(30.Seconds());
r.Handle<AccountNewMessageResponse>(m =>
{
var response = m;
});
});
Consumer setup:
this.bus = ServiceBusFactory.New(sbc =>
{
sbc.UseMsmq();
sbc.VerifyMsmqConfiguration();
sbc.UseMulticastSubscriptionClient();
sbc.SetNetwork("Test");
sbc.ReceiveFrom("msmq://localhost/consumer_test_2");
sbc.Subscribe(subs => subs.Instance(new AccountNewMessageConsumer()));
});
Consumer:
public class AccountNewMessageConsumer : Consumes<AccountNewMessage>.Context
{
public void Consume(IConsumeContext<AccountNewMessage> context)
{
context.Respond(new AccountNewMessageResponse()
{
CorrelationId = context.Message.CorrelationId,
ErrorCode = "1",
UserId = new Random().Next(1, 10000).ToString()
});
}
}
Messages:
[Serializable]
public class AccountNewMessage : CorrelatedBy<Guid>
{
public Guid CorrelationId { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
}
[Serializable]
public class AccountNewMessageResponse : CorrelatedBy<Guid>
{
public Guid CorrelationId { get; set; }
public string UserId { get; set; }
public string ErrorCode { get; set; }
}
What am I doing wrong? Thank you.
You will have to tell MassTransit how to store it's subscription info (for MSMQ).
There are two choices: MSMQ Multicast, which keeps subscription information in memory, and MSMQ Runtime Services which stores the subscription information in a database, so it persists between sessions.
Your choice of which to use will depend on whether you require permanent or temporary subscriptions - from the docs:
Permanent subscriptions represent a subscription that you want to have
stay around even if your process is shut down (maybe you are doing an
upgrade and don’t want to miss a message). A temporary subscription is
to be used in the case where you won’t care if you miss a message
while shut down.
There's no reason you couldn't use Multicast during development / PoC and switch to RuntimeServices later. The docs page also shows how to set up each in configuration, of course with RuntimeServices you also have to set up a database.
(Note that Multicast can take a little while to set itself up, so you might need to give your test system a pause to warm up before you start pumping messages through it.)
Edit: I've appended a couple of routines which validate the subscriptions and consumers: you would call the first while setting up subscriptions (ie during sbc.Subscribe) and the second once the bus is configured. Perhaps they will help find the problem?
private void ValidateSubscriptions(Configurator configurator)
{
var errors = configurator.Validate();
Console.WriteLine("Subscription Validation");
Console.WriteLine("-----------------------");
foreach (var err in errors.Where(e => string.IsNullOrEmpty(e.Value) == false))
{
Console.WriteLine("Type: {0} Message: {1} Key: {2} Value: {3}",
err.Disposition, err.Message, err.Key, err.Value);
}
}
private static void ValidateBus(Configurator bus)
{
var errors = bus.Validate();
Console.WriteLine("Consumer Validation");
Console.WriteLine("-------------------");
foreach (var err in errors.Where(e => string.IsNullOrEmpty(e.Value) == false))
{
Console.WriteLine("Type: {0} Message: {1} Key: {2} Value: {3}",
err.Disposition, err.Message, err.Key, err.Value);
}
}
Related
When trying to execute the following ASP.NET Core code:
public class Foo
{
public long A { get; set; }
public string B { get; set; }
}
[HttpGet]
[Route("Foos")]
public ActionResult<IEnumerable<IGrouping<long, Foo>>> Foos()
{
var foos = new List<Foo> { new Foo { A = 1, B = "Foo" }, new Foo { A = 2, B = "Bar" }, new Foo { A = 2, B = "Baz" } };
return new ActionResult<IEnumerable<IGrouping<long, Foo>>>(foos.GroupBy(f => f.A));
}
I get:
System.InvalidCastException: Unable to cast object of type 'Foo' to type 'System.Int64'.
at System.Text.Json.JsonPropertyInfoNotNullable`4.OnWriteEnumerable(WriteStackFrame& current, Utf8JsonWriter writer)
at System.Text.Json.JsonPropertyInfo.WriteEnumerable(WriteStack& state, Utf8JsonWriter writer)
at System.Text.Json.JsonSerializer.HandleEnumerable(JsonClassInfo elementClassInfo, JsonSerializerOptions options, Utf8JsonWriter writer, WriteStack& state)
at System.Text.Json.JsonSerializer.Write(Utf8JsonWriter writer, Int32 originalWriterDepth, Int32 flushThreshold, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.JsonSerializer.WriteAsyncCore(Stream utf8Json, Object value, Type inputType, JsonSerializerOptions options, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
at Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|29_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
Why does it seem to be trying to serialize an object to an int64? How can I successfully return the result of a GroupBy from a web API endpoint?
New System.Text.Json serializer cannot serialize IEnumerable<IGrouping<long, Foo>>.
Solution
I would recommend switching to old Json.Net serializer in your ASP.Net Core project. I've tested it and it works fine with Json.Net.
See this article for information about switching to Json.Net.
It will be something as easy as that:
services.AddMvc()
.AddNewtonsoftJson();
I have a custom ModelBinder, which, when I load test my app, and run Ants profiler on, identified reading the Request.Content as string as a hotspot:
public class QueryModelBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
var body = actionContext.Request.Content.ReadAsStringAsync().Result;
Is there a more efficient way of doing this?
Or am I reading the ANTS profiler incorrectly?
How big is the content? Note that you might be seeing a lot of time because you are calling this network call in sync rather than async.
You can potentially read the string earlier async and stash it in the request property.
Alternatively you can write a formatter instead, and then decorate your parameter with [FromBody].
The recommended approach here is to use a FromBody and a formatter, since it naturally fits with the WebAPI architecture:
For that you would write a media type formatter:
public class StringFormatter : MediaTypeFormatter
{
public StringFormatter()
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/mystring"));
}
public override bool CanReadType(Type type)
{
return (type == typeof (string));
}
public override bool CanWriteType(Type type)
{
return false;
}
public override async Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger,
CancellationToken cancellationToken)
{
if (!CanReadType(type))
{
throw new InvalidOperationException();
}
return await content.ReadAsStringAsync();
}
}
Register it in webapiconfig.cs
config.Formatters.Add(new StringFormatter());
And consume in an action
public string Get([FromBody]string myString)
{
return myString;
}
The other design (not as recommended because of coupling between the filter and the binder):
Implement a model binder (this is super Naive):
public class MyStringModelBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
// this is a Naive comparison of media type
if (actionContext.Request.Content.Headers.ContentType.MediaType == "application/mystring")
{
bindingContext.Model = actionContext.Request.Properties["MyString"] as string;
return true;
}
return false;
}
}
Add an authroization filter (they run ahead of modelbinding), to you can async access the action. This also works on a delegating handler:
public class MyStringFilter : AuthorizationFilterAttribute
{
public override async Task OnAuthorizationAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
if (actionContext.Request.Content.Headers.ContentType.MediaType == "application/mystring")
{
var myString = await actionContext.Request.Content.ReadAsStringAsync();
actionContext.Request.Properties.Add("MyString", myString);
}
}
}
Register it in WebApi.Config or apply it to the controller:
WebApiConfig.cs
config.Filters.Add(new MyStringFilter());
ValuesController.cs
[MyStringFilter] // this is optional, you can register it globally as well
public class ValuesController : ApiController
{
// specifying the type here is optional, but I'm using it because it avoids having to specify the prefix
public string Get([ModelBinder(typeof(MyStringModelBinder))]string myString = null)
{
return myString;
}
}
(Thanks for #Kiran Challa for looking over my shoulder, and suggesting the Authorization filter)
EDIT: One thing to always remember with relatively large strings (consuming more than 85KB so about 40K Chars) can go into the Large Object heap, which will wreak havoc on your site performance. If you thing this is common enough, break the input down into something like a string builder/array of strings or something similar without contiguous memory. See Why Large Object Heap and why do we care?
I'm new to MassTransit, and I miss something in my understanding.
Let's say I have a server farm were all nodes can do the same job. The application framework is CQRS's styled. That means I have two base kind of message to publish :
Commands : must be handled by exactly one of the server, any of them (the first with job slot free)
Events : must be handled by all servers
I've have build an extremely simple MassTransit prototype (a console application that is sending hello every X seconds).
In the API, I can see there is a "publish" method. How can I specify what kind of message it is (one versus all server)?
If I look a the "handler" configuration, I can specify the queue uri. If I specify the same queue for all hosts, all hosts will get the message, but I cannot limit the execution to only one server.
If I listen from a host dedicated queue, only one server will handle the messages, but I don't know how to broadcast the other kind of message.
Please help me to understand what I'm missing.
PS: if it cares, my messaging system is rabbitmq.
In order to test, I have create a common class library with this classes :
public static class ActualProgram
{
private static readonly CancellationTokenSource g_Shutdown = new CancellationTokenSource();
private static readonly Random g_Random = new Random();
public static void ActualMain(int delay, int instanceName)
{
Thread.Sleep(delay);
SetupBus(instanceName);
Task.Factory.StartNew(PublishRandomMessage, g_Shutdown.Token);
Console.WriteLine("Press enter at any time to exit");
Console.ReadLine();
g_Shutdown.Cancel();
Bus.Shutdown();
}
private static void PublishRandomMessage()
{
Bus.Instance.Publish(new Message
{
Id = g_Random.Next(),
Body = "Some message",
Sender = Assembly.GetEntryAssembly().GetName().Name
});
if (!g_Shutdown.IsCancellationRequested)
{
Thread.Sleep(g_Random.Next(500, 10000));
Task.Factory.StartNew(PublishRandomMessage, g_Shutdown.Token);
}
}
private static void SetupBus(int instanceName)
{
Bus.Initialize(sbc =>
{
sbc.UseRabbitMqRouting();
sbc.ReceiveFrom("rabbitmq://localhost/simple" + instanceName);
sbc.Subscribe(subs =>
{
subs.Handler<Message>(MessageHandled);
});
});
}
private static void MessageHandled(Message msg)
{
ConsoleColor color = ConsoleColor.Red;
switch (msg.Sender)
{
case "test_app1":
color = ConsoleColor.Green;
break;
case "test_app2":
color = ConsoleColor.Blue;
break;
case "test_app3":
color = ConsoleColor.Yellow;
break;
}
Console.ForegroundColor = color;
Console.WriteLine(msg.ToString());
Console.ResetColor();
}
private static void MessageConsumed(Message msg)
{
Console.WriteLine(msg.ToString());
}
}
public class Message
{
public long Id { get; set; }
public string Sender { get; set; }
public string Body { get; set; }
public override string ToString()
{
return string.Format("[{0}] {1} : {2}" + Environment.NewLine, Id, Sender, Body);
}
}
I have also 3 console applications that just run the ActualMain method :
internal class Program
{
private static void Main(string[] args)
{
ActualProgram.ActualMain(0, 1);
}
}
What you want is known as Competing Consumers (search SO for that you'll find more info)
Using RabbitMQ makes life easy, all you need to do is specify the same queue name for each consumer you start, the message will be processed by only one of them.
Instead of generating a unique queue each time as you are doing.
private static void SetupBus(int instanceName)
{
Bus.Initialize(sbc =>
{
sbc.UseRabbitMqRouting();
sbc.ReceiveFrom("rabbitmq://localhost/Commands);
sbc.Subscribe(subs =>
{
subs.Handler<Message>(MessageHandled);
});
});
}
AFAIK, you'll need to have a separate process for command handlers as opposed to event handlers. All the command handlers will ReceiveFrom the same queue, all event handlers will ReceiveFrom their own unique queue.
The other piece of the puzzle is how you get messages into the bus. You can still use publish for commands, but if you have configured consumers incorrectly you could get multiple executions as the message will go to all consumers, if you want to guarantee the message ends up on a single queue you can use Send rather than Publish.
Bus.Instance
.GetEndpoint(new Uri("rabbitmq://localhost/Commands"))
.Send(new Message
{
Id = g_Random.Next(),
Body = "Some message",
Sender = Assembly.GetEntryAssembly().GetName().Name
});
I am attempting to use bbv.common.EventBroker with Ninject and am running into a few problems getting things wired up correctly.
I currently am using Ninject factories to create an item and then, since I subscribe to events, use the eventbroker to do the subscription. However when the register method is called I get an error that I can not find any information for at all. I am not even sure what the error means.
Error:
bbv.Common.EventBroker.Exceptions.RepeatedSubscriptionException occurred
Message=Cannot add more than one subscription handler method of the same subscriber one topic: 'Method1'.
Source=bbv.Common.EventBroker
StackTrace:
at bbv.Common.EventBroker.Internals.EventTopic.ThrowIfRepeatedSubscription(Object subscriber, String handlerMethodName)
at bbv.Common.EventBroker.Internals.EventTopic.AddSubscription(Object subscriber, MethodInfo handlerMethod, IHandler handler, IList`1 subscriptionMatchers)
at bbv.Common.EventBroker.Internals.EventInspector.HandleSubscriber(Object subscriber, Boolean register, MethodInfo methodInfo, EventSubscriptionAttribute attr, IEventTopicHost eventTopicHost)
at bbv.Common.EventBroker.Internals.EventInspector.ProcessSubscriber(Object subscriber, Boolean register, IEventTopicHost eventTopicHost)
at bbv.Common.EventBroker.EventBroker.Register(Object item)
InnerException:
Code:
public const string Topic1 = "Topic1";
public const string Topic2 = "Topic2";
public const string Topic3 = "Topic3";
public const string Topic4 = "Topic4";
public ItemHelper(IItem item, IEventBroker eventBroker)
{
_item = item;
eventBroker.Register(this);
}
[EventSubscription(Topic1, typeof(bbv.Common.EventBroker.Handlers.Publisher))]
public void Method1(object sender, SomeArgs1 args)
{
...
}
[EventSubscription(Topic2, typeof(bbv.Common.EventBroker.Handlers.Publisher))]
public void Method2(object sender, SomeArgs2 args)
{
...
}
[EventSubscription(Topic3, typeof(bbv.Common.EventBroker.Handlers.Publisher))]
public void Method3(object sender, SomeArgs3 args)
{
...
}
[EventSubscription(Topic4, typeof(bbv.Common.EventBroker.Handlers.Publisher))]
public void Method4(object sender, SomeArgs4 args)
{
...
}
It turned out that the class had an interface which had the [EventSubscription] attributes on its methods. The error message does make a lot more sense now that I know this.
I'm implementing the "IsLike" extension to LINQ and Nhibernate as outlined in this post by Fabio.
I have the code like so:
public class MyLinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry
{
public MyLinqToHqlGeneratorsRegistry()
: base()
{
RegisterGenerator(ReflectionHelper.GetMethodDefinition(() =>
MyLinqExtensions.IsLike(null, null)),
new IsLikeGenerator());
}
}
public class IsLikeGenerator : BaseHqlGeneratorForMethod
{
public IsLikeGenerator()
{
SupportedMethods = new[] { ReflectionHelper.GetMethodDefinition(() =>
MyLinqExtensions.IsLike(null, null)) };
}
public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject,
ReadOnlyCollection<Expression> arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
{
return treeBuilder.Like(visitor.Visit(arguments[0]).AsExpression(),
visitor.Visit(arguments[1]).AsExpression());
}
}
public static class MyLinqExtensions
{
public static bool IsLike(this string source, string pattern)
{
pattern = Regex.Escape(pattern);
pattern = pattern.Replace("%", ".*?").Replace("_", ".");
pattern = pattern.Replace(#"\[", "[").Replace(#"\]", "]").Replace(#"\^", "^");
return Regex.IsMatch(source, pattern);
}
}
The extension is registered in the configuration (3rd line):
protected void InitializeNHibernateSession()
{
NHibernateConfiguration = NHibernateSession.Init(
new SimpleSessionStorage(),
GetMappingAssemblies(),
GetNHibernateConfig());
NHibernateConfiguration.Properties.Add(
Environment.LinqToHqlGeneratorsRegistry,
typeof(MyLinqToHqlGeneratorsRegistry).AssemblyQualifiedName);
NHibernateSession.RegisterInterceptor(new AuditInterceptor());
}
But when I try running the query I get an exception
System.NotSupportedException was unhandled by user code
Message=Boolean IsLike(System.String, System.String)
Source=NHibernate
StackTrace:
at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.VisitMethodCallExpression (MethodCallExpression expression)
at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.VisitExpression (Expression expression)
at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.Visit(Expression expression, VisitorParameters parameters)
at NHibernate.Linq.Visitors.QueryModelVisitor.VisitWhereClause(WhereClause whereClause, QueryModel queryModel, Int32 index)
at Remotion.Data.Linq.Clauses.WhereClause.Accept(IQueryModelVisitor visitor, QueryModel queryModel, Int32 index)
at Remotion.Data.Linq.QueryModelVisitorBase.VisitBodyClauses(ObservableCollection`1 bodyClauses, QueryModel queryModel)
at Remotion.Data.Linq.QueryModelVisitorBase.VisitQueryModel(QueryModel queryModel)
at NHibernate.Linq.Visitors.QueryModelVisitor.Visit()
at NHibernate.Linq.Visitors.QueryModelVisitor.GenerateHqlQuery(QueryModel queryModel, VisitorParameters parameters, Boolean root)
at NHibernate.Linq.NhLinqExpression.Translate(ISessionFactoryImplementor sessionFactory)
at NHibernate.Hql.Ast.ANTLR.ASTQueryTranslatorFactory.CreateQueryTranslators(String queryIdentifier, IQueryExpression queryExpression, String collectionRole, Boolean shallow, IDictionary`2 filters, ISessionFactoryImplementor factory)
at NHibernate.Engine.Query.HQLExpressionQueryPlan.CreateTranslators(String expressionStr, IQueryExpression queryExpression, String collectionRole, Boolean shallow, IDictionary`2 enabledFilters, ISessionFactoryImplementor factory)
at NHibernate.Engine.Query.HQLExpressionQueryPlan..ctor(String expressionStr, IQueryExpression queryExpression, String collectionRole, Boolean shallow, IDictionary`2 enabledFilters, ISessionFactoryImplementor factory)
at NHibernate.Engine.Query.HQLExpressionQueryPlan..ctor(String expressionStr, IQueryExpression queryExpression, Boolean shallow, IDictionary`2 enabledFilters, ISessionFactoryImplementor factory)
at NHibernate.Engine.Query.QueryPlanCache.GetHQLQueryPlan(IQueryExpression queryExpression, Boolean shallow, IDictionary`2 enabledFilters)
at NHibernate.Impl.AbstractSessionImpl.GetHQLQueryPlan(IQueryExpression queryExpression, Boolean shallow)
at NHibernate.Impl.AbstractSessionImpl.CreateQuery(IQueryExpression queryExpression)
at NHibernate.Linq.NhQueryProvider.PrepareQuery(Expression expression, IQuery& query, NhLinqExpression& nhQuery)
at NHibernate.Linq.NhQueryProvider.Execute(Expression expression)
at NHibernate.Linq.NhQueryProvider.Execute[TResult](Expression expression)
at System.Linq.Queryable.Count[TSource](IQueryable`1 source)
at MyProject.Data.Specification.LinqSpecRepository`1.FindAllPaged(Specification`1 specification, Int32 currentPage, Int32 noOfItemsPerPage) in c:\source\MyProject\Specification\LinqSpecRepository.cs:line 47
at MyProject.Tests.PersonRepositoryTests_UserSearch.FilteredQuery_CanPerformWildCardAtTheEndSearch() in c:\source\MyProject.Tests\PersonRepositoryTests_UserSearch.cs:line 51
It's like the extensions is not registered or not triggering. The property is is set since I tried adding it to the configuration in the test itself and got an exception that the key already existed.
NHibernate assembly version is 3.0.0.4000
Any suggestions to what I might be doing wrong?
After figuring out what Sharp Architecture does and learning a bit more about the SessionFactory (ie that it cannot be changed) the solution was to add the properties to the NHibernateSession.Init call
var configProperties = new Dictionary<string, string> {{
Environment.LinqToHqlGeneratorsRegistry,
typeof (MyLinqToHqlGeneratorsRegistry).AssemblyQualifiedName
}};
NHibernateConfiguration = NHibernateSession.Init(
new SimpleSessionStorage(),
GetMappingAssemblies(), null,
GetNHibernateConfig(), configProperties, null);
I tried adding the property to the config file but got validation errors on the file. Adding it to the .Init() call worked perfectly though.
Since I know Fabio's example works, it has to be something in your wrapper classes, for which we don't have the source.
What does NHibernateSession.Init do?
When are you building your SessionFactory?