I am writing a logging behavior like in this blog by Pieter de Rycke, but for NLog. I came up with this code:
public class NLogLogger : IParameterInspector
{
private void Log(Type instanceType, string operationName, string msg)
{
NLog.Logger logger = LogManager.GetLogger(
instanceType.FullName, instanceType);
logger.Info(msg, instanceType);
}
public object BeforeCall(string operationName, object[] inputs)
{
// Retrieve the service instance type for the logger then log the call.
OperationContext operationContext = OperationContext.Current;
Type instanceType = operationContext.InstanceContext
.GetServiceInstance().GetType();
Log(instanceType, operationName, "BeforeCall");
return instanceType;
}
public void AfterCall(
string operationName, object[] outputs,
object returnValue, object correlationState
)
{
if (correlationState is Type)
Log(correlationState as Type, operationName, "AfterCall");
}
}
The logging behavior works fine. I injected it into the service Example.MyService using an attribute as described by Pieter. I have this layout in an NLog target:
${longdate} ${callsite} ${level:uppercase=true} ${message}
However the callsite for the operation GetContacts is wrong:
2013-07-11 13:32:53.1379 Common.NLogLogger.Log INFO BeforeCall
2013-07-11 13:32:53.7121 Common.NLogLogger.Log INFO AfterCall
Correct would be this:
2013-07-11 13:32:53.1379 Example.MyService.GetContacts INFO BeforeCall
2013-07-11 13:32:53.7121 Example.MyService.GetContacts INFO AfterCall
What have I tried?
NLog offers a special handling of callsite for logging wrappers or facades, as described in this StackOverflow answer: Pass the class of the callsite to the logging methods.
In fact I did this with logger.Info(msg, instanceType) above in the Log() method. However this does not work because the callsite is not yet in the stack trace when the behavior's BeforeCall() method is running. WCF has not yet even started to run the operation. NLog does not find the callsite in the stack trace and is not capable to unwrap the stack trace.
How can I fake a callsite? Or how can I display the "right" callsite for the logging behavior?
UPDATE:
Thanks to your clarification, I better understand what you are trying to do. You would like the messages logged from the IParameterInspector implementation to reflect a call site of "Example.MyService.GetContacts" where Example.MyService is your service (as indicated by the instanceType parameter) and "GetContacts" is the operation. You could synthesize the call site information manually. You would still use NLog's Logger.Log method and you would still create a LogEventInfo object. Additionally, you can store the "class" and "method" in the LogEventInfo.Properties object. Rather than retrieving a logger (from LogManager) based on the instanceType (i.e. the service), retrieve the logger based on the type of the parameter inspector (NLogLogger in your case). Finally, you can add an additional rule to your NLog.config (and apply it to the NLogLogger type) so that rule has a different logging format. You will manually add a field to the logging format that contains the call site information (that you stored in the LogEventInfo.Properties collection) in the same position as the "real" callsite LayoutRenderer in your other logging rule configurations.
Next I will post a new version of your NLogLogger implementation that does what I described above.
public class NLogLogger : IParameterInspector
{
private static readonly NLog.Logger logger = LogManager.GetCurrentClassLogger();
private void Log(Type instanceType, string operationName, string msg)
{
NLog.Logger serviceLogger = LogManager.GetLogger(
instanceType.FullName, instanceType);
//Create LogEventInfo with the Logger.Name from the logger associated with the service
LogEventInfo le = new LogEventInfo(LogLevel.Info, serviceLogger.Name, msg);
le.Properties.Add("fakecallsite", string.Format("{0}.{1}",instanceType.ToString(),operationName);
//Log the message using the parameter inspector's logger.
logger.Log(typeof(NLogLogger), le);
}
public object BeforeCall(string operationName, object[] inputs)
{
// Retrieve the service instance type for the logger then log the call.
OperationContext operationContext = OperationContext.Current;
Type instanceType = operationContext.InstanceContext
.GetServiceInstance().GetType();
Log(instanceType, operationName, "BeforeCall");
return instanceType;
}
public void AfterCall(
string operationName, object[] outputs,
object returnValue, object correlationState
)
{
if (correlationState is Type)
Log(correlationState, operationName, "AfterCall");
}
}
Your NLog.config will have rules something like this. One rule is specifically for your NLogLogger Parameter Inspector. It logs to "f1" and is a "final" rule, meaning that logging messages from the parameter inspector won't be logged by any other rules. The other rule is for all other loggers. Each logs to a different file target, but both file targets write to the same file (which works, I think). The key is that each file has its own layout.
<logger name="Your.Full.NameSpace.NLogLogger" minlevel="*" writeTo="f1" final="true" />
<logger name="*" minlevel="*" writeTo="f2" />
Your targets and layouts would look something like this. We are defining a variable whose value is the value of the EventPropertiesLayoutRenderer, which is the fake call site that we stored in LogEventInfo.Properties["fakecallsite"].
<variable name="fakecallsite" value="${event-properties:fakecallsite}"/>
<variable name="f1layout" value="${longdate} | ${level} | ${logger} | ${fakecallsite} | ${message}"/>
<variable name="f2layout" value="${longdate} | ${level} | ${logger} | ${callsite} | ${message}"/>
<targets>
<target name="f1" xsi:type="File" layout="${f1layout}" fileName="${basedir}/${shortdate}.log" />
<target name="f2" xsi:type="File" layout="${f2layout}" fileName="${basedir}/${shortdate}.log" />
</targets>
Note that I have not tried this, but I think it should work (or should be close enough that you can get it working). One limitation is that, since we are calculating the fake call site, we cannot use the real callsite LayoutRenderer to manipulate the contents of the fakecallsite field in the output. If this important, it can probably be simulated by storing the class and method separately (in LogEventInfo.Properties) and then setting the "fakecallsite" variable in the NLog.config to contain the class, the method, or both.
END UPDATE
Your wrapper should use the Log method. Also, the type you pass to the NLog Logger.Log method should be the type of your NLog Logger wrapper, not the type of the type of the service instance. You can still use the type of your service instance to retrieve the right Logger instance. It should look something like this:
public class NLogLogger : IParameterInspector
{
private void Log(Type instanceType, string operationName, string msg)
{
NLog.Logger logger = LogManager.GetLogger(
instanceType.FullName, instanceType);
//This is the key to preserving the call site in a wrapper. Create a LogEventInfo
//then use NLog's Logger.Log method to log the message, passing the type of your
//wrapper as the first argument.
LogEventInfo le = new LogEventInfo(LogLevel.Info, logger.Name, msg);
logger.Log(typeof(NLogLogger), le);
}
public object BeforeCall(string operationName, object[] inputs)
{
// Retrieve the service instance type for the logger then log the call.
OperationContext operationContext = OperationContext.Current;
Type instanceType = operationContext.InstanceContext
.GetServiceInstance().GetType();
Log(instanceType, operationName, "BeforeCall");
return instanceType;
}
public void AfterCall(
string operationName, object[] outputs,
object returnValue, object correlationState
)
{
if (correlationState is Type)
Log(correlationState, operationName, "AfterCall");
}
}
Related
To create a SimpleMessageListenerContainer, I do something like this.
SimpleMessageConsumer simpleMessageConsumer = new SimpleMessageConsumer();
MessageListenerAdapter adapter =
new CustomMessageListenerAdapater(simpleMessageConsumer);
adapter.setDefaultListenerMethod("consume");
adapter.setMessageConverter(new SimpleMessageConverter());
SimpleMessageListenerContainer
container =
new SimpleMessageListenerContainer(connectionFactory);
container.setMessageListener(adapter);
My SimpleMessageConsumer
public void consume(String message){
log.info(message);
}
I noticed in the spring-amqp-refrence that the "annotated
listener endpoint infrastructure" allows you to conveniently pass additional Message properties into your consumer like so :
#RabbitListener(queues = "myQueue")
public void processOrder(Order order, #Header("order_type") String orderType) {
...
}
Can that be done using consumer creation approach I mention above? I want to access to some of the headers of the Message object. I know that I can make my consumer implement the MessageListener or the ChannelAwareMessageListener and have access to the entire Message object. However, I dont want the whole Message object, since I like the convenience of using the ContentTypeDelegatingMessageConverter with the Jackson2JsonMessageConverter to do conversion to my desired type(in my real app), before my consumer delegated method is triggered.
public class CustomMessageListenerAdapter extends MessageListenerAdapter {
public CustomMessageListenerAdapter(Object delegate) {
super(delegate);
}
#Override
protected Object invokeListenerMethod(String methodName, Object[] arguments,
Message originalMessage) throws Exception {
Object[] modifiedArguments = new Object[arguments.length+1];
System.arraycopy(arguments, 0, modifiedArguments,
0, arguments.length);
//add the original message with headers
modifiedArguments[arguments.length] = originalMessage;
return super.invokeListenerMethod(methodName, modifiedArguments, originalMessage);
}
}
and my Message consumer now has an additional argument - the original message
public class SimpleMessageConsumer {
private static final Logger log = LoggerFactory.getLogger(SimpleMessageConsumer.class);
public void consume(String messageConverted, Message originalMessage){
log.info(originalMessage.toString());
}
}
My Bean Configuration looks something like this:
SimpleMessageConsumer simpleMessageConsumer = new SimpleMessageConsumer();
MessageListenerAdapter adapter =
new CustomMessageListenerAdapter(simpleMessageConsumer);
adapter.setDefaultListenerMethod("consume");
adapter.setMessageConverter(messageConverter());
//container
SimpleMessageListenerContainer
container =
new SimpleMessageListenerContainer(connectionFactory);
container.setMessageListener(adapter);
You need to use a MessagingMessageConverter with the payloadConverter as the mentioned Jackson2JsonMessageConverter and then in your CustomMessageListenerAdapater you override there a:
/**
* Build an array of arguments to be passed into the target listener method. Allows for multiple method arguments to
* be built from a single message object.
* <p>
* The default implementation builds an array with the given message object as sole element. This means that the
* extracted message will always be passed into a <i>single</i> method argument, even if it is an array, with the
* target method having a corresponding single argument of the array's type declared.
* <p>
* This can be overridden to treat special message content such as arrays differently, for example passing in each
* element of the message array as distinct method argument.
* #param extractedMessage the content of the message
* #return the array of arguments to be passed into the listener method (each element of the array corresponding to
* a distinct method argument)
*/
protected Object[] buildListenerArguments(Object extractedMessage) {
casting that extractedMessage to the Message<?> and extract desired headers if that.
I have a wcf client. According to requirements, I need to record some of the metadata in the request (as well as user data which is not included in the request.) Then, if the request is successful I may have to record response metadata and depending on flags, the full soap request.
I am trying to do this the right way (using IParameterInspector to examine the metadata and IClientMessageInspector to get the Soap), but I have no way of correlating the two Interface requests. I am not sure about thread safety here. This is a stripped down version of where I am at...
public class SoapRequestInfo
{
public string UserId { get; set; }
public Guid Key { get; set; }
//would contain a lot more info
}
public class OperationProfilerParameterInspector : IParameterInspector, IClientMessageInspector
{
//before serialization
public object BeforeCall(string operationName, object[] inputs) //IParameterInspector
{
//Add the operation, record some specific inputs to db
return new SoapRequestInfo
{
UserId = "1234",
Key = new Guid()
};
}
public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState) //IParameterInspector
{
var info = correlationState as SoapRequestInfo;
//Do some additional logging - easy enough
}
public object BeforeSendRequest(ref Message request, IClientChannel channel) //IClientMessageInspector
{
//want to correlate this with IParameterInspector
return null;
}
public void AfterReceiveReply(ref Message reply, object correlationState) //IClientMessageInspector
{
//May want to log full soap message depending on after call criteria
}
}
I know I can't use a private variable to hold the Guid. I can't use session, because there can be multiple requests in close succession and can't guarantee the response is correct. So how can I uniquely identify the correlationState between the two interfaces?
You can probably use HttpContext.Items to keep your object if your service is running in ASPNET compatibility mode otherwise you can use TLS (Thread Local Storage), put the data in a slot and fetch/clear later.
May eb you can tro to do this little bit diffrent:
From this post passing correlation token to WCF service?:
If you use message version with WS-Addressing you should have it automatically because each messages will contain its autogenerated ID (guid) and each response will contain ID of the request as well. You can access these headers through OperationContext
If using messages is not fit your requirements, probably you can try to put your own id in the header when you send request, and probably update the response with same id.
If I create a class that implements IParameterInspector, and insert it into the WCF pipline using a custom ServiceBehavior, will the same instance of the class be used when invoking BeforeCall and AfterCall? In other words, can I establish state about the current invocation during BeforeCall that I can access in AfterCall, and be sure that the response will come to the same instance?
Note _stateValue in the sample code below? Can I depend on a mechanism like this?
class OperationParameterInspector : IParameterInspector
{
public int _stateValue;
public object BeforeCall(string operationName, object[] inputs)
{
_stateValue = (int) inputs[0];
return null;
}
public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
{
int originalInput = _stateValue;
return;
}
}
Passing state related to a particular call is the purpose of the return value from BeforeCall and the correlationState argument of AfterCall. The WCF infrastructure ensures that whatever object you return from BeforeCall is then passed into AfterCall via the correlationState, after the operation has completed.
As your subsequent comment suggests, the problem with using instance state in the inspector object is that instances may be shared between concurrent requests in some scenarios. However, I don't think there are any scenarios where a single operation request would be served by different parameter inspector objects in BeforeCall and AfterCall.
An advantage of an IoC container is that you can swap in a mock service at the bottom of your object graph. However this seems much harder to do in Spring.Net than in other IoC Containers. Here's some code that does it in Unity and has Spring.Net code;
namespace IocSpringDemo
{
using Microsoft.Practices.Unity;
using NUnit.Framework;
using Spring.Context;
using Spring.Context.Support;
public interface ISomeService
{
string DoSomething();
}
public class ServiceImplementationA : ISomeService
{
public string DoSomething()
{
return "Hello A";
}
}
public class ServiceImplementationB : ISomeService
{
public string DoSomething()
{
return "Hello B";
}
}
public class RootObject
{
public ISomeService SomeService { get; private set; }
public RootObject(ISomeService service)
{
SomeService = service;
}
}
[TestFixture]
public class UnityAndSpringDemo
{
[Test]
public void UnityResolveA()
{
UnityContainer container = new UnityContainer();
container.RegisterType<ISomeService, ServiceImplementationA>();
RootObject rootObject = container.Resolve<RootObject>();
Assert.AreEqual("Hello A", rootObject.SomeService.DoSomething());
}
[Test]
public void UnityResolveB()
{
UnityContainer container = new UnityContainer();
container.RegisterType<ISomeService, ServiceImplementationB>();
RootObject rootObject = container.Resolve<RootObject>();
Assert.AreEqual("Hello B", rootObject.SomeService.DoSomething());
}
[Test]
public void SpringResolveA()
{
IApplicationContext container = ContextRegistry.GetContext();
RootObject rootObject = (RootObject)container.GetObject("RootObject");
Assert.AreEqual("Hello A", rootObject.SomeService.DoSomething());
}
[Test]
public void SpringResolveB()
{
// does not work - what to do to make this pass?
IApplicationContext container = ContextRegistry.GetContext();
RootObject rootObject = (RootObject)container.GetObject("RootObject");
Assert.AreEqual("Hello B", rootObject.SomeService.DoSomething());
}
}
}
For the benefit of Spring, the following needed to be in the App.config file. Clearly this only serves the first spring test, and not the second. Can you put multiple spring configurations in the config file? If so, what is the syntax and how do you access them? Or is there another way to do this?
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
</sectionGroup>
</configSections>
<spring>
<context>
<resource uri="config://spring/objects"/>
</context>
<objects xmlns="http://www.springframework.net">
<object name="RootObject" type="IocSpringDemo.RootObject, IocDemo" autowire="constructor" />
<object name="service" type="IocSpringDemo.ServiceImplementationA, IocDemo" autowire="constructor" />
</objects>
</spring>
Update
Here is a partial answer based at code at the links that Marko Lahma gave to Mark Pollack's blog. I have the above tests passing, with the following code:
public static class SpringHelper
{
public static T Resolve<T>(this IApplicationContext context, string name)
{
return (T)context.GetObject(name);
}
public static void RegisterType<T>(this GenericApplicationContext context, string name)
{
context.RegisterType(name, typeof(T));
}
public static void RegisterType(this GenericApplicationContext context, string name, Type type)
{
IObjectDefinitionFactory objectDefinitionFactory = new DefaultObjectDefinitionFactory();
ObjectDefinitionBuilder builder = ObjectDefinitionBuilder.RootObjectDefinition(objectDefinitionFactory, type);
builder.SetAutowireMode(AutoWiringMode.AutoDetect);
context.RegisterObjectDefinition(name, builder.ObjectDefinition);
}
}
...
[Test]
public void SpringResolveA()
{
GenericApplicationContext container = new GenericApplicationContext();
container.RegisterType<RootObject>("RootObject");
container.RegisterType<ServiceImplementationA>("service");
RootObject rootObject = container.Resolve<RootObject>("RootObject");
Assert.AreEqual("Hello A", rootObject.SomeService.DoSomething());
}
[Test]
public void SpringResolveB()
{
GenericApplicationContext container = new GenericApplicationContext();
container.RegisterType<RootObject>("RootObject");
container.RegisterType<ServiceImplementationB>("service");
RootObject rootObject = container.Resolve<RootObject>("RootObject");
Assert.AreEqual("Hello B", rootObject.SomeService.DoSomething());
}
This raises a few questions to me:
I want to integrate this technique into existing code that uses the usual container. Why do I have to use a different container type, GenericApplicationContext in this case? What if I want to read data into this object from the existing spring config in app.config or web.config? Would it work as the usual context? Could I then write data over these registrations with code?
How can I specify that ISomeService is to be created as a singleton? I don't mean supply a singleton instance to the container, but the container to create the instance, resolving its constructor, and use it when that type is needed.
how can I do the equivalent of container.RegisterType<ISomeService, ServiceImplementationA>(); ? I want to register type mappings to use in all cases where that type is needed by a constructor.
What exactly does container.RegisterType<ServiceImplementationA>("service"); do? It seems to register ServiceImplementationA as the implementation of ISomeService but ISomeServiceis never mentioned, so there could be ambiguity. e.g. what if ServiceImplementationA implemented more than one interface.
What is the string name given to the registration for? It won't work with en empty string, but it doesn't seem to matter what it is.
Am I trying to use spring in a way that it just does not work? I'm trying to use it like other IoC containers, but it's not quite working.
Adding as new answer trying to address the open points...
I want to integrate this technique
into existing code that uses the usual
container. Why do I have to use a
different container type,
GenericApplicationContext in this
case? What if I want to read data into
this object from the existing spring
config in app.config or web.config?
Would it work as the usual context?
Could I then write data over these
registrations with code?
Spring has concrete application context implementations for different kind of initialization tactics. The most common ones to use are GenericApplicationContext (manual), XmlApplicationContext (XML files) and WebApplicationContext (very much like XmlApplicationContext but tailored for web use). They all implement common interface: IApplicationContext which is the preferred way to access these containers.
Unfortonately altering registrations with code usually means that you need to use the specific sub-class directly. With GenericApplicationContext and StaticApplicationContext this is quite natural but XmlApplicationContext is usually considered to be XML only and this ways "fixed" to XML definition.
How can I specify that ISomeService is
to be created as a singleton? I don't
mean supply a singleton instance to
the container, but the container to
create the instance, resolving its
constructor, and use it when that type
is needed.
Your SpringHelper does just that, by default all objects in Spring are singletons. You could alter this behavior by calling ObjectDefinitionBuilder's SetSingleton method with false.
how can I do the equivalent of
container.RegisterType(); ? I want to
register type mappings to use in all
cases where that type is needed by a
constructor.
Spring uses object names (ids) to distinct between different implementations. So if you want to get specific type to serve a specific instance in case that there are many alternatives you should refer to this specific instance by name. If you are using autowiring and your object has dependency to interface ISomeService and there's only one object registered that implements it, the autowiring can set it without ambiguity.
What exactly does
container.RegisterType("service");
do? It seems to register
ServiceImplementationA as the
implementation of ISomeService but
ISomeServiceis never mentioned, so
there could be ambiguity. e.g. what if
ServiceImplementationA implemented
more than one interface.
Continuing from previous answer, this registers singleton of type ServiceImplementationA with name "service". This object comes autowiring candidate with all it's implemented interfaces (and with it's concrete type of course).
What is the string name given to the
registration for? It won't work with
en empty string, but it doesn't seem
to matter what it is.
It matters a great deal as explained earlier. The name is unique id within that context (parent context could have object with same name) and can be used to access specific object registrations. In short where other frameworks may associate a type as key to object registration, Spring uses name.
That's a bit apples and oranges comparison as the unit test uses code configuration for Unity and XML (app.config) configuration for Spring.NET.
If you go the XML route, then you can either comment out old implementation A and define the B implementation as the one to use - that what's configuration is all about right? Other option is to have dedicated XML files for each scenario (configuration setup) and include them via context's resource definitions (you have inline resource now). Other options include file system and assembly, see the web configuration section in Spring.NET's manual for a nice example.
If you go the code configuration route I would suggest to check Spring.NET Recoil and upcoming CodeConfig.
Pulling my hair out trying to debug this one. Earlier this morning, this code was working fine, and I can't see what I've changed to break it. Now, whenever I try to open an nHibernate session, I'm getting the following error:
Test method BCMS.Tests.Repositories.BlogBlogRepositoryTests.can_get_recent_blog_posts threw exception: System.TypeInitializationException: The type initializer for 'NHibernate.Cfg.Environment' threw an exception. ---> System.Runtime.Serialization.SerializationException: Type is not resolved for member 'Castle.DynamicProxy.Serialization.ProxyObjectReference,Rhino.Mocks, Version=3.5.0.1337, Culture=neutral, PublicKeyToken=0b3305902db7183f'..
Any thoughts on how to debug what's going on here?
I hit the same issue as you - in my case it was with NLog's static method:
LogManager.GetCurrentClassLogger()
I'd replaced the current thread's principal with a Rhinomocks stub:
var identity = MockRepository.GenerateStub<IIdentity>();
identity.Stub(x => x.IsAuthenticated).Return(true);
var principal = MockRepository.GenerateStub<IPrincipal>();
principal.Stub(x => x.Identity).Return(identity);
Thread.CurrentPrincipal = principal;
Running unit tests for my code threw the same exception from the original question.
The stack trace:
at System.AppDomain.get_Evidence()
at System.AppDomain.get_EvidenceNoDemand()
at System.AppDomain.get_Evidence()
at System.Configuration.ClientConfigPaths.GetEvidenceInfo(AppDomain appDomain, String exePath, String& typeName)
at System.Configuration.ClientConfigPaths.GetTypeAndHashSuffix(AppDomain appDomain, String exePath)
at System.Configuration.ClientConfigPaths..ctor(String exePath, Boolean includeUserConfig)
at System.Configuration.ClientConfigPaths.GetPaths(String exePath, Boolean includeUserConfig)
at System.Configuration.ClientConfigurationHost.RequireCompleteInit(IInternalConfigRecord record)
at System.Configuration.BaseConfigurationRecord.GetSectionRecursive(String configKey, Boolean getLkg, Boolean checkPermission, Boolean getRuntimeObject, Boolean requestIsHere, Object& result, Object& resultRuntimeObject)
at System.Configuration.BaseConfigurationRecord.GetSection(String configKey)
at System.Configuration.ClientConfigurationSystem.System.Configuration.Internal.IInternalConfigSystem.GetSection(String sectionName)
at System.Configuration.ConfigurationManager.GetSection(String sectionName)
at NLog.Config.XmlLoggingConfiguration.get_AppConfig()
at NLog.LogFactory.get_Configuration()
at NLog.LogFactory.GetLogger(LoggerCacheKey cacheKey)
at NLog.LogFactory.GetLogger(String name)
at NLog.LogManager.GetCurrentClassLogger()
at MyClassHere...
So as you can see from the stack trace an attempt to read the config file is made, which won't work - why? Because the now mocked current principal is no longer the WindowsPrincipal that we had originally - it's now a mocked principal which won't have any sort of windows file access.
Thinking off the cuff here's a couple of ways to fix this issue.
Inject the logger into my class so that it can be stubbed (I probably should be doing this anyway I suppose..). This would allow me to use a stub for the Thread principal.
Modify the existing WindowsPrincipal (or create another based on it) on the thread to add in the roles required to call my methods.
-- UPDATE --
To fix my issue, in the end I decided to run with my first suggestion above. To avoid writing my own abstraction of the NLog Logger I just leveraged what was offered from Common.Logging. Class constructors now accept an ILog as one of their parameters, and the Unity config to inject the logger just looks like this:
container.RegisterType<ILog>(new TransientLifetimeManager(), new InjectionFactory(x => LogManager.GetCurrentClassLogger()));
Meanwhile, my unit tests now allow me to pass in a mocked logger.
var logger = MockRepository.GenerateStub<ILog>();
Some more info... it seems to be related to switching the Thread.CurrentPrincipal to a mocked IPrincipal implementation. I do all my security checks in my domain model inside the entities. The entity's methods check Thread.CurrentPrincipal.IsInRole() before modifying properties on the entity.
So, in order to test the entity's methods, I have to set different users (contributor user, moderator user, etc.) before I make my entity method calls.
I haven't figured out why this was working fine yesterday.
Here's an example of my Mocked IPrincipal:
private static IPrincipal _blogContributorUser = null;
public static IPrincipal BlogContributorUser
{
get
{
if (null == _blogContributorUser)
{
var identity = MockRepository.GenerateStub<IIdentity>();
identity.Stub(p => p.Name).Return("BlogContributor").Repeat.Any();
var principal = MockRepository.GenerateStub<IPrincipal>();
principal.Stub(p => p.Identity).Return(identity).Repeat.Any();
principal.Stub(p => p.IsInRole(UserRoles.BlogContributor)).Return(true).Repeat.Any();
principal.Stub(p => p.IsInRole(UserRoles.CommentContributor)).Return(true).Repeat.Any();
principal.Stub(p => p.IsInRole(UserRoles.TagContributor)).Return(true).Repeat.Any();
_blogContributorUser = principal;
}
return _blogContributorUser;
}
}
I have the same issue. It looks like that it has trouble reading the config file, since CurrentPrincipal is changed. I have moved all that has to be initialized from the config file, before replacing the CurrentPrincipal (for example, opened NHibernate session, initialized Unity and that kind of stuff), and everything works after that. Of course, this is not a solution, just a workaround figured out by a desperate man.
Errors like this usually indicate versioning issues.
What I suspect may be happening is that both RhinoMocks and NHibernate are making use of Castle.DynamicProxy type, but they are asking for different versions of that type.
Did you recently uprade RhinoMocks or NHibernate to a newer version?
If this isn't the issue, then more information would be helpful - do all tests fail, or just this particular one?
edit You may also wish to try adding these lines to your Properties\AssemblyInfo.cs file:
[assembly: InternalsVisibleTo("Rhino.Mocks")]
[assembly: InternalsVisibleTo("Castle.DynamicProxy")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
In case the error is related to mocking IPrincipal and/or IIdentity with RhinoMocks or Moq the solution is actually quite simple: don't use those frameworks but create simple fake types instead.
Here is an example for a simple "allow everything" implementation:
public class FakeIdentity : IIdentity
{
public string Name { get { return "IntegrationTest"; } }
public string AuthenticationType { get { return "Kerberos"; } }
public bool IsAuthenticated { get { return true; } }
}
public class FakePrincipal : IPrincipal
{
public FakePrincipal() { this.Identity = new FakeIdentity(); }
public IIdentity Identity { get; private set; }
public bool IsInRole(string role) { return true; }
}
If you need more complexity take a look at the System.Security.Principal.GenericPrincipal class.