I have the following unity construct method:
public static IUnityContainer CreateContainer()
{
UnityContainer container = new UnityContainer();
container.LoadConfiguration();
.......
}
Then an interface, and an implementation class:
namespace MyCompany.Web.Areas.MyApp.Common
{
public interface ISession
{
}
}
namespace MyCompany.Web.Areas.MyApp.Common
{
public class SessionHandler : ISession
{
}
}
Instead of doing this:
Container.RegisterType<ISession, SessionHandler>(new ContainerControlledLifetimeManager()) ;
I want to use container.LoadConfiguration() to load the above RegisterType config from the web.config. BUT it doesn't seem to work
web.config:
<unity>
<containers>
<container>
<types>
<type type="MyCompany.Web.Areas.MyApp.Common.ISession" mapTo="MyCompany.Web.Areas.MyApp.Common.SessionHandler">
<lifetime type="singleton"/>
</type>
</types>
</container>
</containers>
</unity>
Exception:
The type name or alias MyProject.Web.Areas.MyApp.Common.ISession could not be resolved. Please check your configuration file and verify this type name.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: The type name or alias MyProject.Web.Areas.MyApp.Common.ISession could not be resolved. Please check your configuration file and verify this type name.
Source Error:
Line 33: {
Line 34: UnityContainer container = new UnityContainer();
Line 35: container.LoadConfiguration();
Line 36:
Line 37: //container.RegisterType(new ContainerControlledLifetimeManager());
Stack Trace:
[InvalidOperationException: The type name or alias MyProject.Web.Areas.MyApp.Common.ISession could not be resolved. Please check your configuration file and verify this type name.]
Microsoft.Practices.Unity.Configuration.ConfigurationHelpers.TypeResolverImpl.ResolveType(String typeNameOrAlias, Boolean throwIfResolveFails) in e:\Builds\Unity\UnityTemp\Compile\Unity\Unity.Configuration\Src\ConfigurationHelpers\TypeResolverImpl.cs:110
Microsoft.Practices.Unity.Configuration.ConfigurationHelpers.TypeResolver.ResolveType(String typeNameOrAlias) in e:\Builds\Unity\UnityTemp\Compile\Unity\Unity.Configuration\Src\ConfigurationHelpers\TypeResolver.cs:47
Microsoft.Practices.Unity.Configuration.RegisterElement.GetRegisteringType() in e:\Builds\Unity\UnityTemp\Compile\Unity\Unity.Configuration\Src\RegisterElement.cs:121
Microsoft.Practices.Unity.Configuration.RegisterElement.ConfigureContainer(IUnityContainer container) in e:\Builds\Unity\UnityTemp\Compile\Unity\Unity.Configuration\Src\RegisterElement.cs:88
Microsoft.Practices.Unity.Configuration.ContainerConfiguringElement.ConfigureContainerInternal(IUnityContainer container) in e:\Builds\Unity\UnityTemp\Compile\Unity\Unity.Configuration\Src\ContainerConfiguringElement.cs:43
Microsoft.Practices.Unity.Configuration.<>c__DisplayClass1.b__0(ContainerConfiguringElement element) in e:\Builds\Unity\UnityTemp\Compile\Unity\Unity.Configuration\Src\ContainerElement.cs:114
Microsoft.Practices.ObjectBuilder2.EnumerableExtensions.ForEach(IEnumerable1 sequence, Action1 action) in e:\Builds\Unity\UnityTemp\Compile\Unity\Unity\Src\ObjectBuilder\Utility\EnumerableExtensions.cs:36
Microsoft.Practices.Unity.Configuration.ContainerElement.ConfigureContainer(IUnityContainer container) in e:\Builds\Unity\UnityTemp\Compile\Unity\Unity.Configuration\Src\ContainerElement.cs:110
Microsoft.Practices.Unity.Configuration.UnityConfigurationSection.Configure(IUnityContainer container, String configuredContainerName) in e:\Builds\Unity\UnityTemp\Compile\Unity\Unity.Configuration\Src\UnityConfigurationSection.cs:151
Microsoft.Practices.Unity.Configuration.UnityContainerExtensions.LoadConfiguration(IUnityContainer container, UnityConfigurationSection section, String containerName) in e:\Builds\Unity\UnityTemp\Compile\Unity\Unity.Configuration\Src\UnityContainerExtensions.cs:37
Microsoft.Practices.Unity.Configuration.UnityContainerExtensions.LoadConfiguration(IUnityContainer container) in e:\Builds\Unity\UnityTemp\Compile\Unity\Unity.Configuration\Src\UnityContainerExtensions.cs:64
MyProject.Web.ApplicationContainer.CreateContainer() in C:\Workspace\MCDev\MyApp\MyApp\MyProject.Web\ApplicationContainer.cs:35
MyProject.Web.ApplicationContainer.Initialize(HttpContext context) in C:\Workspace\MCDev\MyApp\MyApp\MyProject.Web\ApplicationContainer.cs:53
MyProject.Web.MvcApplication.Application_Start() in C:\Workspace\MCDev\MyApp\MyApp\MyProject.Web\Global.asax.cs:41
I don't think it knows what dll to look in:
<unity>
<containers>
<container>
<types>
<type type="MyCompany.Web.Areas.MyApp.Common.ISession, MyCompany" mapTo="MyCompany.Web.Areas.MyApp.Common.SessionHandler, MyCompany">
<lifetime type="singleton"/>
</type>
</types>
</container>
</containers>
</unity>
Related
I have a simple WCF service but when I try to open it in the browser I get this error:
Composition proxy target must implement at least one interface
Markup code
<%# ServiceHost Language="C#" Debug="true" Service="Mvm.SATWeb.ServicesLiquidez.Service.WSRiesgoLiquidez" Factory="Spring.ServiceModel.Activation.ServiceHostFactory" CodeBehind="WSRiesgoLiquidez.svc.cs" %>
IWSRiesgoLiquidez.cs
namespace Mvm.SATWeb.ServicesLiquidez.Service
{
// NOTE: If you change the interface name "IWSEnvioTerceros" here, you must also update the reference to "IWSEnvioTerceros" in Web.config.
[ServiceContract]
interface IWSRiesgoLiquidez
{
[OperationContract]
ResultadoOperacionLiquidez consultaIngresosEgresosPuntosDeAtencion(string USUARIO, string CLAVE, string CODIGORED, string FECHA, string HORAINICIAL, string HORAFINAL);
[OperationContract]
ResultadoOperacionLiquidez consultaValorRealPuntoDeAtencion(string USUARIO, string CLAVE, string CODIGORED, string CODIGOPUNTOATENCION);
[OperationContract]
ResultadoOperacionLiquidez consultaValorRealPorPuntosDeatencion(string USUARIO, string CLAVE, string CODIGORED);
}
}
The implementation: WSRiesgoLiquidez.cs
namespace Mvm.SATWeb.ServicesLiquidez.Service
{
public class WSRiesgoLiquidez : IWSRiesgoLiquidez
{
public ResultadoOperacionLiquidez consultaIngresosEgresosPuntosDeAtencion(string USUARIO, string CLAVE, string CODIGORED, string FECHA, string HORAINICIAL, string HORAFINAL)
{
ResultadoOperacionLiquidez respuesta = new ResultadoOperacionLiquidez();
IServicioMaestros ServicioMaestros = SATWebServiceLocator<IServicioMaestros>.ObtenerServicio();
try
{
using (var riesLiq = new Mvm.SATWeb.ServicesLiquidez.Comun.BusinessDelegate.RiesgoLiqBD())
{
// respuesta.Retorno = riesLiq.consultaIngresosEgresosPuntosDeAtencion(HORAINICIAL, HORAFINAL);
//EnvioTO objLiq = riesLiq.consultaIngresosEgresosPuntosDeAtencion(HORAINICIAL, HORAFINAL);
respuesta = riesLiq.consultaIngresosEgresosPuntosDeAtencion(HORAINICIAL, HORAFINAL);
respuesta.EjecucionExitosa = true;
}
}
catch (Exception ex)
{
respuesta.EjecucionExitosa = false;
respuesta.MensajeValidacion = RemoveLocalizationString(ex.Message);
}
return respuesta;
}
public ResultadoOperacionLiquidez consultaValorRealPuntoDeAtencion(string USUARIO, string CLAVE, string CODIGORED, string CODIGOPUNTOATENCION)
{
ResultadoOperacionLiquidez respuesta = new ResultadoOperacionLiquidez();
return respuesta;
}
public ResultadoOperacionLiquidez consultaValorRealPorPuntosDeatencion(string USUARIO, string CLAVE, string CODIGORED)
{
ResultadoOperacionLiquidez respuesta = new ResultadoOperacionLiquidez();
return respuesta;
}
private string RemoveLocalizationString(string p)
{
string strMensaje = String.Empty;
strMensaje = p.Replace("[", "").Replace(", es-CO]", "");
return strMensaje;
}
}
}
Then in my web.config I have:
<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>
<!-- ConfiguraciĆ³n de spring con los servicios Web -->
<spring>
<context>
Servicios de seguridad, logging, multilenguaje, etc.
<resource uri="~/Servicios/Infraestructura.config" />
La declaracion de los servicios de negocio (Los BO)
<resource uri="~/Servicios/ServiciosNegocio.config" />
</context>
</spring>
On ServiciosNegocios.config I have
<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns="http://www.springframework.net">
<description>Declaracion de objetos de negocio</description>
<!--Objetos de negocio - Seguridad-->
<object id="SATWebSeguridadTarget" type="Mvm.SATWeb.Core.Seguridad.Modelo.Negocio.ServicioSeguridad, Mvm.SATWeb.Core">
</object>
<!--Proxy - Seguridad-->
<object id="SATWebSeguridadService"
type="Spring.Aop.Framework.ProxyFactoryObject">
<property name="target">
<ref object="SATWebSeguridadTarget"/>
</property>
<property name="interceptorNames">
<list>
<value>MethodInterceptor</value>
</list>
</property>
</object>
<object id="Mvm.SATWeb.ServicesLiquidez.Service.WSRiesgoLiquidezTarget"
type="Mvm.SATWeb.ServicesLiquidez.Service.WSRiesgoLiquidez, Mvm.SATWeb.ServicesLiquidez">
</object>
<object id="Mvm.SATWeb.ServicesLiquidez.Service.WSRiesgoLiquidez"
type="Spring.Aop.Framework.ProxyFactoryObject">
<property name="target">
<ref object="Mvm.SATWeb.ServicesLiquidez.Service.WSRiesgoLiquidezTarget"/>
</property>
<property name="interceptorNames">
<list>
<value>MethodInterceptor</value>
</list>
</property>
</object>
</objects>
And Infraestructure.config
<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns="http://www.springframework.net">
<description>Declaracion e integracion de servicios de infraestructura.</description>
<!-- AOP Advices -->
<object id="MethodInterceptor" type="Mvm.SATWeb.Core.Comun.AOP.MethodInterceptor, Mvm.SATWeb.Core" />
<!-- Servicio SIDUtil -->
<object id="UtilService" type="Mvm.SATWeb.ServicesLiquidez.Comun.Helpers.SATWebUtil, Mvm.SATWeb.ServicesLiquidez">
</object>
<!-- configuracion de mensajes -->
<object id="messageSource" type="Spring.Context.Support.ResourceSetMessageSource, Spring.Core">
<property name="ResourceManagers">
<list>
<value>Resources.Mensajes, App_GlobalResources</value>
</list>
</property>
</object>
<object id="LocalizacionLenguajeService"
type="Mvm.Localizacion.Lenguaje.ClientAPI.LenguajeServiceSpringResx, Mvm.Localizacion.Lenguaje.ClientAPI">
</object>
</objects>
The detailed error is this:
Composition proxy target must implement at least one interface.
Description: An unhandled exception occurred during the execution of
the current web request. Please review the stack trace for more
information about the error and where it originated in the code.
Exception Details: System.ArgumentException: Composition proxy target
must implement at least one interface.
Source Error:
An unhandled exception was generated during the execution of the
current web request. Information regarding the origin and location of
the exception can be identified using the exception stack trace below.
Stack Trace:
[ArgumentException: Composition proxy target must implement at least
one interface.]
Spring.Proxy.CompositionProxyTypeBuilder.BuildProxyType() +125
Spring.ServiceModel.Support.ServiceProxyTypeBuilder.BuildProxyType()
+41 Spring.ServiceModel.SpringServiceHost.CreateServiceType(String serviceName, IApplicationContext applicationContext) +173
Spring.ServiceModel.SpringServiceHost..ctor(String serviceName,
IApplicationContext applicationContext, Uri[] baseAddresses) +43
Spring.ServiceModel.Activation.ServiceHostFactory.CreateServiceHost(String
reference, Uri[] baseAddresses) +141
System.ServiceModel.HostingManager.CreateService(String
normalizedVirtualPath) +693
System.ServiceModel.HostingManager.ActivateService(String
normalizedVirtualPath) +42
System.ServiceModel.HostingManager.EnsureServiceAvailable(String
normalizedVirtualPath) +479
I have the following console program in which I attempt to apply the SOLID principles, dependency injection with Unity and Prism 6 modularity:
Main program:
UnityContainer container = new UnityContainer();
container.RegisterInstance<IServiceLocator>(new UnityServiceLocator(container));
container.RegisterType<IModuleInitializer, ModuleInitializer>();
TextLogger logger = new TextLogger();
container.RegisterInstance<ILoggerFacade>(logger);
ConfigurationModuleCatalog catalog = new ConfigurationModuleCatalog();
container.RegisterInstance<IModuleCatalog>(catalog);
container.RegisterType<IModuleManager, ModuleManager>();
IModuleManager manager = container.Resolve<IModuleManager>();
manager.Run();
App config file:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="modules" type="Prism.Modularity.ModulesConfigurationSection, Prism.Wpf" />
</configSections>
<modules>
<module assemblyFile="CalcCommandParsingLib.dll" moduleType="CalcCommandParsingLib.CalculatorCommandParsingModule, CalcCommandParsingLib, Version=1.0.0.0, Culture = neutral, PublicKeyToken=null" moduleName="Parsing" />
<module assemblyFile="CalculatorLibrary.dll" moduleType="CalculatorLibrary.CalculatorModule, CalculatorLibrary, Version=1.0.0.0, Culture = neutral, PublicKeyToken=null" moduleName="Calculator" />
<module assemblyFile="InputOutputLibrary.dll" moduleType="InputOutputLibrary.InputOutputModule, InputOutputLibrary, Version=1.0.0.0, Culture = neutral, PublicKeyToken=null" moduleName="InputOutput" />
<module assemblyFile="MainModuleLibrary.dll" moduleType="MainModuleLibrary.MainModule, MainModuleLibrary, Version=1.0.0.0, Culture = neutral, PublicKeyToken=null" moduleName="Main" >
<dependencies>
<dependency moduleName="Calculator" />
<dependency moduleName="Parsing" />
<dependency moduleName="InputOutput" />
</dependencies>
</module>
</modules>
</configuration>
And one of the four modules which causes the error:
public class MainModule : IModule
{
IServiceLocator serviceLocator;
public MainModule(IServiceLocator serviceLocator)
{
this.serviceLocator = serviceLocator;
}
public void Initialize()
{
ICalculatorReplLoop loop = serviceLocator.GetInstance<ICalculatorReplLoop>();
loop.Run();
}
}
The error occurs on this line:
ICalculatorReplLoop loop = serviceLocator.GetInstance<ICalculatorReplLoop>();
and it is:
An exception of type
'Microsoft.Practices.ServiceLocation.ActivationException' occurred in
Microsoft.Practices.ServiceLocation.dll but was not handled in user
code
Additional information: Activation error occurred while trying to get
instance of type ICalculatorReplLoop, key ""
Can somebody help me with this please?
I think that there are at least 2 things to change:
You should also register ICalculatorReplLoop
container.RegisterType<ICalculatorReplLoop, CalculatorReplLoop>();
It would make more sense for me to inject directly the needed interface, then to resolve it via serviceLocator so:
public MainModule(ICalculatorReplLoop calc)
{
this.calc= calc;
}
I'm building a new Web Application using MVC5 and I need the followings:
Catch errors
Log the details in a file
Send them by email
Add to the detail custom information (for example the Id of the
record I'm trying to read)
Return to the view custom messages to the user
I have found a lot of information regarding the HandleErrorAttribute but none of them allow to add specific details to the error, also I have found information saying that the try catch aproach is too heavy for the server.
For now, I have:
Controller:
public partial class HomeController : Controller
{
private static Logger logger = LogManager.GetCurrentClassLogger();
public virtual ActionResult Index()
{
try
{
return View();
}
catch (Exception e)
{
logger.Error("Error in Index: " + e);
return MVC.Error.Index("Error in Home Controller");
}
}
}
I have found this Extended HandleErrorAttribute that seems complete but don't do everything I need:
private bool IsAjax(ExceptionContext filterContext)
{
return filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest";
}
public override void OnException(ExceptionContext filterContext)
{
if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)
{
return;
}
// if the request is AJAX return JSON else view.
if (IsAjax(filterContext))
{
//Because its a exception raised after ajax invocation
//Lets return Json
filterContext.Result = new JsonResult(){Data=filterContext.Exception.Message,
JsonRequestBehavior=JsonRequestBehavior.AllowGet};
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
}
else
{
//Normal Exception
//So let it handle by its default ways.
base.OnException(filterContext);
}
// Write error logging code here if you wish.
//if want to get different of the request
//var currentController = (string)filterContext.RouteData.Values["controller"];
//var currentActionName = (string)filterContext.RouteData.Values["action"];
}
Your requirement best fit with Elmah. Very good plugin for logging errors.
ELMAH stands for Error Logging Modules And Handlers
ELMAH provides such a high degree of plugability that even Installation of ELMAH does not require compilation of your application.
ELMAH (Error Logging Modules and Handlers) is an application-wide error logging facility that is completely pluggable. It can be dynamically added to a running ASP.NET web application, or even all ASP.NET web applications on a machine, without any need for re-compilation or re-deployment.
Reference from the blog of SCOTT HANSELMAN
Just need to copy binary of ELMAH to bin folder of your application and edit web.config file. That's it!
you need to add following to your web.config and make some other changes described in the following links.
<sectionGroup name="elmah">
<section name="security" requirePermission="false" type="Elmah.SecuritySectionHandler, Elmah" />
<section name="errorLog" requirePermission="false" type="Elmah.ErrorLogSectionHandler, Elmah" />
<section name="errorMail" requirePermission="false" type="Elmah.ErrorMailSectionHandler, Elmah" />
<section name="errorFilter" requirePermission="false" type="Elmah.ErrorFilterSectionHandler, Elmah" />
</sectionGroup>
For example to set up mail account.
<configuration>
<configSections>
<sectionGroup name="elmah">
<section name="errorLog" requirePermission="false" type="Elmah.ErrorLogSectionHandler, Elmah"/>
<section name="errorMail" requirePermission="false" type="Elmah.ErrorMailSectionHandler, Elmah"/>
<section name="errorFilter" requirePermission="false" type="Elmah.ErrorFilterSectionHandler, Elmah"/>
</sectionGroup>
</configSections>
<elmah>
<errorMail from="test#test.com" to="test#test.com"
subject="Application Exception" async="false"
smtpPort="25" smtpServer="***"
userName="***" password="***">
</errorMail>
</elmah>
<system.web>
<customErrors mode="RemoteOnly" defaultRedirect="CustomError.aspx">
<error statusCode="403" redirect="NotAuthorized.aspx" />
<!--<error statusCode="404" redirect="FileNotFound.htm" />-->
</customErrors>
<httpHandlers>
<remove verb="*" path="*.asmx"/>
<add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add verb="*" path="*_AppService.axd" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" validate="false"/>
<add verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" />
</httpHandlers>
<httpModules>
<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah"/>
<add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" />
</httpModules>
</system.web>
</configuration>
Here is some good reference link (that contains detailed reference to installation of ELMAH to your project) for your reference.
https://msdn.microsoft.com/en-us/library/aa479332.aspx
https://code.google.com/p/elmah/wiki/MVC
Update
Add to the detail custom information (for example the Id of the record I'm trying to read)
You can build your own custom exception that derives from Exception.
public class MyException : Exception
{
public MyException(string message, Exception ex) : base(ex.Message, ex)
{
}
}
and then using it like
public virtual ActionResult Index()
{
try
{
return View();
}
catch (Exception e)
{
throw new MyException("detailed exception", e);
}
}
in this way main exception would be wrapped inside the myexception and you can add your detailed custom exception message.
Return to the view custom messages to the user
You just need to add
<system.web>
<customErrors mode="On">
</customErrors>
<sytem.web>
and add Error.cshtml inside the ~/View/Shared folder
Then whenever exception is encountered it will find for Error.cshtml inside view/shared folder and render the content. so you can render there your custom message.
Use Elmah as others have also recommended. I am, and haven't looked back!
It meets all your requirements:
Catches all errors, e.g. 400s, 500s...
Logs to a file, and any other data store you can think of, e.g. database, memory, Azure, more file formats(XML, CSV), RSS feed...
Emails errors: Enable and config mail settings in Web.config - very simple. You can even send emails asynchronously!
Add custom code - in your case add extra details to the error
Use your own custom error pages - custom error node (for 400s, 500s) in web.config and your own error controller
Further on the custom code (2nd last point above), AFAIK you have two options:
1. Create a custom error log implementation.
This isn't that difficult. It's what I did!
Override the default error log data store. For example, taking the SQL Server data store:
In Web.config
<elmah>
<errorLog type="Elmah.SqlErrorLog, Elmah" connectionStringName="myCn" applicationName="myAppName" />
</elmah>
Next, create a class "MySQLServerErrorLog" and derive from Elmah.ErrorLog
All that is then required is to override the Log() method.
public override string Log(Error error)
{
// You have access to all the error details - joy!
= error.HostName,
= error.Type,
= error.Message,
= error.StatusCode
= error.User,
= error.Source,
// Call the base implementation
}
In Web.config, replace the default (above) entry with your implementation:
<elmah>
<errorLog type="myProjectName.MySQLServerErrorLog, myProjectName" />
</elmah>
2. You can programmatically log errors
Using the ErrorSignal class, you may logs errors without having to raise unhandled exceptions.
Syntax:
ErrorSignal.FromCurrentContext().Raise(new NotSupportedException());
Example: A custom exception
var customException = new Exception("My error", new NotSupportedException());
ErrorSignal.FromCurrentContext().Raise(customException);
This gives you the option of using your custom logic to programmatically log whatever you require.
I've written functionality for my Elmah instance to logs errors to Azure Cloud Storage Table and Blob (error stack trace details).
FWIW before I used Elmah, I had written my own exception handling mechanism for MVC which used HandleErrorAttribute and Application_Error (in Global.asax). It worked but was too unwieldy IMO.
If it was me, I'd create my own exception handling Attribute which adds required behaviour to the base implementation of HandleErrorAttribute.
I've had quite good results in the past with having attributes "pointed at" various parts of the request that's of interest (am thinking the bit where you say that you want to log specific details) - so you can use these identifiers to pull the request to pieces using reflection:
CustomHandleErrorAttribute(["id", "name", "model.lastUpdatedDate"])
I've used this approach to secure controller actions (making sure that a customer is requesting things that they're allowed to request) - e.g. a parent is requesting info on their children, and not someone else's children.
Or, you could have a configuration set up whereby you'd "chain" handlers together - so lots of little handlers, all doing very specific bits, all working on the same request and request pointers (as above):
ChainedErrorHandling("emailAndLogFile", ["id", "name", "model.lastUpdatedDate"])
Where "emailAndLogFile" creates a chain of error handlers that inherit from FilterAttribute, the last of which, in the chain, is the standard MVC HandleErrorAttribute.
But by far, the simplest approach would be the former of these 2.
HTH
EDITED TO ADD: Example of inheriting custom error handling:
public class CustomErrorAttribute : HandleErrorAttribute
{
public CustomErrorAttribute(string[] requestPropertiesToLog)
{
this.requestPropertiesToLog = requestPropertiesToLog;
}
public string[] requestPropertiesToLog { get; set; }
public override void OnException(ExceptionContext filterContext)
{
var requestDetails = this.GetPropertiesFromRequest(filterContext);
// do custom logging / handling
LogExceptionToEmail(requestDetails, filterContext);
LogExceptionToFile(requestDetails, filterContext);
LogExceptionToElseWhere(requestDetails, filterContext);// you get the idea
// even better - you could use DI (as you're in MVC at this point) to resolve the custom logging and log from there.
//var logger = DependencyResolver.Current.GetService<IMyCustomErrorLoggingHandler>();
// logger.HandleException(requestDetails, filterContext);
// then let the base error handling do it's thang.
base.OnException(filterContext);
}
private IEnumerable<KeyValuePair<string, string>> GetPropertiesFromRequest(ExceptionContext filterContext)
{
// in requestContext is the queryString, form, user, route data - cherry pick bits out using the this.requestPropertiesToLog and some simple mechanism you like
var requestContext = filterContext.RequestContext;
var qs = requestContext.HttpContext.Request.QueryString;
var form = requestContext.HttpContext.Request.Form;
var user = requestContext.HttpContext.User;
var routeDataOfActionThatThrew = requestContext.RouteData;
yield break;// just break as I'm not implementing it.
}
private void LogExceptionToEmail(IEnumerable<KeyValuePair<string, string>> requestDetails, ExceptionContext filterContext)
{
// send emails here
}
private void LogExceptionToFile(IEnumerable<KeyValuePair<string, string>> requestDetails, ExceptionContext filterContext)
{
// log to files
}
private void LogExceptionToElseWhere(IEnumerable<KeyValuePair<string, string>> requestDetails, ExceptionContext filterContext)
{
// send cash to me via paypal everytime you get an exception ;)
}
}
And On the controller action you'd add something like:
[CustomErrorAttribute(new[] { "formProperty1", "formProperty2" })]
public ActionResult Index(){
return View();
}
Firstly, you can define a filter attribute, and you can register it on startup in an MVC application in global.asax, so you can catch any kind of errors that occur while actions are invoking.
Note: Dependency Resolving is changeable. I'm using Castle Windsor for this story. You can resolve dependencies your own IOC container. For example, ILogger dependency. I used for this property injection while action invoking.
Windsor Action Invoker
For Example Filter:
public class ExceptionHandling : FilterAttribute, IExceptionFilter
{
public ILogger Logger { get; set; }
public void OnException(ExceptionContext filterContext)
{
Logger.Log("On Exception !", LogType.Debug, filterContext.Exception);
if (filterContext.Exception is UnauthorizedAccessException)
{
filterContext.Result = UnauthorizedAccessExceptionResult(filterContext);
}
else if (filterContext.Exception is BusinessException)
{
filterContext.Result = BusinessExceptionResult(filterContext);
}
else
{
// Unhandled Exception
Logger.Log("Unhandled Exception ", LogType.Error, filterContext.Exception);
filterContext.Result = UnhandledExceptionResult(filterContext);
}
}
}
This way you can catch everything.
So:
private static ActionResult UnauthorizedAccessExceptionResult(ExceptionContext filterContext)
{
// Send email, fire event, add error messages
// for example handle error messages
// You can seperate the behaviour by: if (filterContext.HttpContext.Request.IsAjaxRequest())
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
filterContext.Controller.TempData.Add(MessageType.Danger.ToString(), filterContext.Exception.Message);
// So you can show messages using with TempData["Key"] on your action or views
var lRoutes = new RouteValueDictionary(
new
{
action = filterContext.RouteData.Values["action"],
controller = filterContext.RouteData.Values["controller"]
});
return new RedirectToRouteResult(lRoutes);
}
In Global.asax:
protected void Application_Start()
{
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
}
FilterConfig:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new ExceptionHandling());
}
BusinessException:
public class BusinessException : Exception, ISerializable
{
public BusinessException(string message)
: base(message)
{
// Add implemenation (if required)
}
}
So you can access the exception message OnException at ExceptionHandling class with filterContext.Exception.Message
You should use BusinessException on the action after any violated control logic this way: throw new BusinessException("Message").
Why don't you create model that contains your required Error Information and bind data to model when you need to? It will also allow you to create/return view from it
Global error catching with special information can you make with customer exceptions who contains the needed informations (id, tablesname etc.).
In HandleErrorAttribute you "only" have httpContext/ExceptionContext and othe static informations.
I'm trying to log the requests and responses (the raw XML SOAP envelope) between a console application developed by me and a specific third party remote SOAP web service to database for audit purposes, and I can't find a way to do it.
Ideally what I'd like to do is getting the request
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
<soapenv:Header/>
<soapenv:Body>
<tem:SayHello>
<tem:name>Albireo</tem:name>
</tem:SayHello>
</soapenv:Body>
</soapenv:Envelope>
and the response
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<SayHelloResponse xmlns="http://tempuri.org/">
<SayHelloResult>Hello, Albireo.</SayHelloResult>
</SayHelloResponse>
</s:Body>
</s:Envelope>
and save them in the database.
So far every tutorial on the net I found boils down to two approaches, the SoapExtension method and the tracing method.
The SoapExtension method
The SoapExtension method is based on the SOAP Message Modification Using SOAP Extensions guide, in this method you create a class inheriting from SoapExtension and hook it in the application's configuration, the class' ProcessMessage method will allow you to intercept the SOAP messages.
This is an example of the class inherited from SoapExtension:
namespace Playground.Client
{
using System;
using System.Web.Services.Protocols;
public class SoapLogger : SoapExtension
{
public override object GetInitializer(System.Type serviceType)
{
throw new NotImplementedException();
}
public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
{
throw new NotImplementedException();
}
public override void Initialize(object initializer)
{
throw new NotImplementedException();
}
public override void ProcessMessage(SoapMessage message)
{
throw new NotImplementedException();
}
}
}
And this is how it is wired in the configuration:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0"
sku=".NETFramework,Version=v4.5" />
</startup>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IGreeterService" />
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:8080/greeter"
binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IGreeterService"
contract="Services.IGreeterService"
name="BasicHttpBinding_IGreeterService" />
</client>
</system.serviceModel>
<system.web>
<webServices>
<soapExtensionTypes>
<add group="0"
priority="1"
type="Playground.Client.SoapLogger" />
</soapExtensionTypes>
</webServices>
</system.web>
</configuration>
The problem with this method is it seems to work only for web applications, trying to implement it in a console application yield no result.
The tracing method
The tracing method is based upon the Configuring Message Logging guide, in this method you enable .NET's tracing for every SOAP/WCF communication in the application and dump the log somewhere (more information on the configuration can be found in Recommended Settings for Tracing and Message Logging).
This is an example of the tracing configuration:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0"
sku=".NETFramework,Version=v4.5" />
</startup>
<system.diagnostics>
<sources>
<source name="System.ServiceModel"
propagateActivity="true"
switchValue="Verbose, ActivityTracing">
<listeners>
<add initializeData="ServiceModel.svclog"
name="ServiceModel"
type="System.Diagnostics.XmlWriterTraceListener" />
</listeners>
</source>
<source name="System.ServiceModel.MessageLogging">
<listeners>
<add initializeData="MessageLogging.svclog"
name="MessageLogging"
type="System.Diagnostics.XmlWriterTraceListener" />
</listeners>
</source>
</sources>
</system.diagnostics>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IGreeterService" />
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:8080/greeter"
binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IGreeterService"
contract="Services.IGreeterService"
name="BasicHttpBinding_IGreeterService" />
</client>
<diagnostics>
<endToEndTracing activityTracing="true"
messageFlowTracing="true"
propagateActivity="true" />
<messageLogging logEntireMessage="true"
logKnownPii="true"
logMalformedMessages="true"
logMessagesAtServiceLevel="true"
logMessagesAtTransportLevel="true" />
</diagnostics>
</system.serviceModel>
</configuration>
The content of ServiceModel.svclog and MessageLogging.svclog can be found in a GitHub's Gist as it's too big to fit here.
The problem with this method is it logs every SOAP/WCF message in the application and it seems the generated logs are not really useful, they contains loads of informations and I can't understand if and how filter only what I'm interested in, the only practical way to read them seems to be Microsoft's Service Trace Viewer.
I've tried to add a custom TraceListener too:
namespace Playground.Client
{
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Linq;
public class CustomTraceListener : TraceListener
{
public override void Write(string message)
{
File.AppendAllLines("CustomTraceListener.txt", new[] { message });
}
public override void WriteLine(string message)
{
message = this.FormatXml(message);
File.AppendAllLines("CustomTraceListener.txt", new[] { message });
}
private string FormatXml(string message)
{
using (var stringWriter = new StringWriter())
{
var xmlWriterSettings = new XmlWriterSettings();
xmlWriterSettings.Encoding = Encoding.UTF8;
xmlWriterSettings.Indent = true;
xmlWriterSettings.OmitXmlDeclaration = true;
using (var xmlTextWriter = XmlWriter.Create(stringWriter, xmlWriterSettings))
{
XDocument.Parse(message).Save(xmlTextWriter);
}
return Convert.ToString(stringWriter);
}
}
}
}
But even though it allows me to intercept the messages, it doesn't save any metadata:
<MessageLogTraceRecord Time="2013-07-16T10:50:04.5396082+02:00" Source="ServiceLevelSendRequest" Type="System.ServiceModel.Channels.BodyWriterMessage" xmlns="http://schemas.microsoft.com/2004/06/ServiceModel/Management/MessageTrace">
<HttpRequest>
<Method>POST</Method>
<QueryString></QueryString>
<WebHeaders>
<VsDebuggerCausalityData>uIDPo4bOsuSXlSVEkmfof4AP2psAAAAAlEIoNto3KEWKgCnIGryjp9f3wbRlp+ROhY9Oy6bed/cACQAA</VsDebuggerCausalityData>
</WebHeaders>
</HttpRequest>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://tempuri.org/IGreeterService/SayHello</Action>
<ActivityId CorrelationId="964a7c4f-3b18-4b5d-8085-e00ae03b58d1" xmlns="http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics">80101cc1-dfb5-4c8e-8d19-ec848ab69100</ActivityId>
</s:Header>
<s:Body>
<SayHello xmlns="http://tempuri.org/">
<name>Albireo</name>
</SayHello>
</s:Body>
</s:Envelope>
</MessageLogTraceRecord>
<MessageLogTraceRecord Time="2013-07-16T10:50:04.6176897+02:00" Source="TransportSend" Type="System.ServiceModel.Channels.BodyWriterMessage" xmlns="http://schemas.microsoft.com/2004/06/ServiceModel/Management/MessageTrace">
<Addressing>
<Action>http://tempuri.org/IGreeterService/SayHello</Action>
<To>http://localhost:8080/greeter</To>
</Addressing>
<HttpRequest>
<Method>POST</Method>
<QueryString></QueryString>
<WebHeaders>
<VsDebuggerCausalityData>uIDPo4bOsuSXlSVEkmfof4AP2psAAAAAlEIoNto3KEWKgCnIGryjp9f3wbRlp+ROhY9Oy6bed/cACQAA</VsDebuggerCausalityData>
</WebHeaders>
</HttpRequest>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<ActivityId CorrelationId="964a7c4f-3b18-4b5d-8085-e00ae03b58d1" xmlns="http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics">80101cc1-dfb5-4c8e-8d19-ec848ab69100</ActivityId>
</s:Header>
<s:Body>
<SayHello xmlns="http://tempuri.org/">
<name>Albireo</name>
</SayHello>
</s:Body>
</s:Envelope>
</MessageLogTraceRecord>
With this information it's impossible to rebuild the request/response flow as all the messages are mixed together.
Compare them to the *.svclog generated by the native XmlWriterTraceListener:
<E2ETraceEvent xmlns="http://schemas.microsoft.com/2004/06/E2ETraceEvent">
<System xmlns="http://schemas.microsoft.com/2004/06/windows/eventlog/system">
<EventID>0</EventID>
<Type>3</Type>
<SubType Name="Information">0</SubType>
<Level>8</Level>
<TimeCreated SystemTime="2013-07-16T08:50:04.6176897Z" />
<Source Name="System.ServiceModel.MessageLogging" />
<Correlation ActivityID="{80101cc1-dfb5-4c8e-8d19-ec848ab69100}" />
<Execution ProcessName="Playground.Client" ProcessID="4348" ThreadID="1" />
<Channel />
<Computer>ESP-DEV-9</Computer>
</System>
<ApplicationData>
<TraceData>
<DataItem>
<MessageLogTraceRecord Time="2013-07-16T10:50:04.6176897+02:00" Source="TransportSend" Type="System.ServiceModel.Channels.BodyWriterMessage" xmlns="http://schemas.microsoft.com/2004/06/ServiceModel/Management/MessageTrace">
<Addressing>
<Action>http://tempuri.org/IGreeterService/SayHello</Action>
<To>http://localhost:8080/greeter</To>
</Addressing>
<HttpRequest>
<Method>POST</Method>
<QueryString></QueryString>
<WebHeaders>
<VsDebuggerCausalityData>uIDPo4bOsuSXlSVEkmfof4AP2psAAAAAlEIoNto3KEWKgCnIGryjp9f3wbRlp+ROhY9Oy6bed/cACQAA</VsDebuggerCausalityData>
</WebHeaders>
</HttpRequest>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<ActivityId CorrelationId="964a7c4f-3b18-4b5d-8085-e00ae03b58d1" xmlns="http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics">80101cc1-dfb5-4c8e-8d19-ec848ab69100</ActivityId>
</s:Header>
<s:Body>
<SayHello xmlns="http://tempuri.org/">
<name>Albireo</name>
</SayHello>
</s:Body>
</s:Envelope>
</MessageLogTraceRecord>
</DataItem>
</TraceData>
</ApplicationData>
</E2ETraceEvent>
<E2ETraceEvent xmlns="http://schemas.microsoft.com/2004/06/E2ETraceEvent">
<System xmlns="http://schemas.microsoft.com/2004/06/windows/eventlog/system">
<EventID>0</EventID>
<Type>3</Type>
<SubType Name="Information">0</SubType>
<Level>8</Level>
<TimeCreated SystemTime="2013-07-16T08:50:04.6957712Z" />
<Source Name="System.ServiceModel.MessageLogging" />
<Correlation ActivityID="{80101cc1-dfb5-4c8e-8d19-ec848ab69100}" />
<Execution ProcessName="Playground.Client" ProcessID="4348" ThreadID="1" />
<Channel />
<Computer>ESP-DEV-9</Computer>
</System>
<ApplicationData>
<TraceData>
<DataItem>
<MessageLogTraceRecord Time="2013-07-16T10:50:04.6801549+02:00" Source="TransportReceive" Type="System.ServiceModel.Channels.BufferedMessage" xmlns="http://schemas.microsoft.com/2004/06/ServiceModel/Management/MessageTrace">
<HttpResponse>
<StatusCode>OK</StatusCode>
<StatusDescription>OK</StatusDescription>
<WebHeaders>
<Content-Length>207</Content-Length>
<Content-Type>text/xml; charset=utf-8</Content-Type>
<Date>Tue, 16 Jul 2013 08:50:04 GMT</Date>
<Server>Microsoft-HTTPAPI/2.0</Server>
</WebHeaders>
</HttpResponse>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header></s:Header>
<s:Body>
<SayHelloResponse xmlns="http://tempuri.org/">
<SayHelloResult>Hello, Albireo.</SayHelloResult>
</SayHelloResponse>
</s:Body>
</s:Envelope>
</MessageLogTraceRecord>
</DataItem>
</TraceData>
</ApplicationData>
</E2ETraceEvent>
Here the <Correlation ActivityID="{80101cc1-dfb5-4c8e-8d19-ec848ab69100}" /> tag establishes a relation between each request and response, allowing a developer to rebuild the whole session.
Is there a way to accomplish what I'm trying to do?
If the service is registered as a WCF web-service (not as an old-school ASMX web-service) it's possible to do it through IClientMessageInspector and IEndpointBehavior.
First you have to create a class inheriting from IClientMessageInspector that will handle the logging of both the requests and the replies.
namespace Playground.Sandbox
{
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
public class MyClientMessageInspector : IClientMessageInspector
{
public object BeforeSendRequest(
ref Message request,
IClientChannel channel)
{
// TODO: log the request.
// If you return something here, it will be available in the
// correlationState parameter when AfterReceiveReply is called.
return null;
}
public void AfterReceiveReply(
ref Message reply,
object correlationState)
{
// TODO: log the reply.
// If you returned something in BeforeSendRequest
// it will be available in the correlationState parameter.
}
}
}
Then you have to create a class inheriting from IEndpointBehavior that will register the inspector in the client.
namespace Playground.Sandbox
{
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
public class MyEndpointBehavior : IEndpointBehavior
{
public void Validate(
ServiceEndpoint endpoint)
{
}
public void AddBindingParameters(
ServiceEndpoint endpoint,
BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(
ServiceEndpoint endpoint,
EndpointDispatcher endpointDispatcher)
{
}
public void ApplyClientBehavior(
ServiceEndpoint endpoint,
ClientRuntime clientRuntime)
{
var myClientMessageInspector = new MyClientMessageInspector();
clientRuntime.ClientMessageInspectors.Add(myClientMessageInspector);
}
}
}
Then when you want to use the behavior you can manually register it before using the service.
namespace Playground.Sandbox
{
public static class Program
{
public static void Main()
{
using (var client = new MyWcfClient())
{
var myEndpointBehavior = new MyEndpointBehavior();
client.Endpoint.Behaviors.Add(myEndpointBehavior);
// TODO: your things with the client.
}
}
}
}
If you don't want to register the behavior manually or you need it to be always active, you can register it in the configuration file.
First you need to create a class inheriting from BehaviorExtensionElement, this class will tell the .NET Framework which behavior will be applied and will create the instance when needed.
namespace Playground.Sandbox
{
using System;
using System.ServiceModel.Configuration;
public class MyBehaviorExtensionElement : BehaviorExtensionElement
{
protected override object CreateBehavior()
{
var myEndpointBehavior = new MyEndpointBehavior();
return myEndpointBehavior;
}
public override Type BehaviorType
{
get
{
return typeof(MyEndpointBehavior);
}
}
}
}
Then you need to register the BehaviorExtensionElement in the configuration file (only the relevant part of the configuration file are shown).
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime sku=".NETFramework,Version=v4.5"
version="v4.0" />
</startup>
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="withMyBehaviorExtensionElement">
<myBehaviorExtensionElement />
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<endpoint address="..."
behaviorConfiguration="withMyBehaviorExtensionElement"
binding="..."
bindingConfiguration="..."
contract="..."
name="..." />
</client>
<extensions>
<behaviorExtensions>
<add name="myBehaviorExtensionElement"
type="Playground.Sandbox.MyBehaviorExtensionElement, Playground.Sandbox" />
</behaviorExtensions>
</extensions>
</system.serviceModel>
</configuration>
Now you can use the service without manually registering the behavior each time:
namespace Playground.Sandbox
{
public static class Program
{
public static void Main()
{
using (var client = new MyWcfService())
{
// TODO: your things with the client.
}
}
}
}
You can find a guide on how to do this in the MSDN's Message Inspectors article.
We are currently trying to implement NServiceBus 3.0 in our WCF services.
We use a custom ServiceHostFactory to initialize our services in WAS. We use net.tcp to access the services and the following code to set it up:
public class DoServiceServiceHostFactory : DefaultServiceHostFactory
{
private static IWindsorContainer _container;
public DoServiceServiceHostFactory()
: base(CreateKernel())
{
}
private static IKernel CreateKernel()
{
_container = new WindsorContainer();
IocModules.Configure(_container, new WcfConfigurationModule());
IocModules.Configure(_container, new WcfAdapterModule());
IocModules.Configure(_container, new ManagerModule());
IocModules.Configure(_container, new FactoryModule());
IBus bus = Configure.WithWeb()
.DefineEndpointName("OurProjectPublisher")
.DefiningEventsAs(t => t.Namespace != null && t.Namespace.StartsWith("MY.Bus.Contracts.Events"))
.CastleWindsorBuilder()
.Log4Net()
.XmlSerializer()
.MsmqTransport()
.UnicastBus()
.CreateBus()
.Start(() => Configure.Instance.ForInstallationOn<NServiceBus.Installation.Environments.Windows>().Install());
//_container.Register(Component.For<IBus>().Instance(bus).LifeStyle.Singleton);
WindsorServiceLocator serviceLocator = new WindsorServiceLocator(_container);
ServiceLocator.SetLocatorProvider(() => serviceLocator);
return _container.Kernel;
}
}
when we call the service we receive this error:
Object reference not set to an instance of an object.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
Source Error:
Line 33: IocModules.Configure(_container, new FactoryModule());
Line 34:
Line 35: IBus bus = Configure.WithWeb()
Line 36: .DefineEndpointName("OurProjectPublisher")
Line 37: .DefiningEventsAs(t => t.Namespace != null && t.Namespace.StartsWith("MY.Bus.Contracts.Events"))
Source File: xxx\ServiceHostFactory.cs Line: 35
Stack Trace:
[NullReferenceException: Object reference not set to an instance of an object.]
NServiceBus.Unicast.UnicastBus.NServiceBus.IStartableBus.Start(Action startupAction) in d:\BuildAgent-03\work\nsb.masterbuild0\src\unicast\NServiceBus.Unicast\UnicastBus.cs:762
xxx.ServiceHostFactory.CreateKernel() in xxx\ServiceHostFactory.cs:35
xxx.ServiceHostFactory..ctor() in xxx\ServiceHostFactory.cs:21
[TargetInvocationException: Exception has been thrown by the target of an invocation.]
System.RuntimeMethodHandle._InvokeConstructor(IRuntimeMethodInfo method, Object[] args, SignatureStruct& signature, RuntimeType declaringType) +0
System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) +651
System.ServiceModel.HostingManager.CreateService(String normalizedVirtualPath) +1204
System.ServiceModel.HostingManager.ActivateService(String normalizedVirtualPath) +50
System.ServiceModel.HostingManager.EnsureServiceAvailable(String normalizedVirtualPath) +1132
[ServiceActivationException: The service '/MembershipService.svc' cannot be activated due to an exception during compilation. The exception message is: Exception has been thrown by the target of an invocation..]
System.Runtime.AsyncResult.End(IAsyncResult result) +890624
System.ServiceModel.Activation.HostedHttpRequestAsyncResult.End(IAsyncResult result) +180062
System.Web.CallHandlerExecutionStep.OnAsyncHandlerCompletion(IAsyncResult ar) +136
And the config section in the web.config
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
<section name="castle" type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />
<section name="MessageForwardingInCaseOfFaultConfig" type="NServiceBus.Config.MessageForwardingInCaseOfFaultConfig, NServiceBus.Core" />
<section name="MsmqTransportConfig" type="NServiceBus.Config.MsmqTransportConfig, NServiceBus.Core" />
<section name="UnicastBusConfig" type="NServiceBus.Config.UnicastBusConfig, NServiceBus.Core" />
</configSections>
<MessageForwardingInCaseOfFaultConfig ErrorQueue="error" />
<MsmqTransportConfig NumberOfWorkerThreads="1" MaxRetries="5" />
<UnicastBusConfig ForwardReceivedMessagesTo="auditqueue">
<MessageEndpointMappings>
<add Messages="MY.Bus.Contracts" Endpoint="OurProjectPublisher" />
</MessageEndpointMappings>
</UnicastBusConfig>
Any ideas about this?
Usually (at least in 2.6), you would pass the implementation of your Windsor Container to the .CastleWindsorBuilder configuration. This allows NSB to create the correct object graph when initializing. So, it would look like:
_container = new WindsorContainer();
IocModules.Configure(_container, new WcfConfigurationModule());
IocModules.Configure(_container, new WcfAdapterModule());
IocModules.Configure(_container, new ManagerModule());
IocModules.Configure(_container, new FactoryModule());
IBus bus = Configure.WithWeb()
.DefineEndpointName("OurProjectPublisher")
.DefiningEventsAs(t => t.Namespace != null && t.Namespace.StartsWith("MY.Bus.Contracts.Events"))
.CastleWindsorBuilder(_container) // added here
.Log4Net()
.XmlSerializer()
.MsmqTransport()
.UnicastBus()
.CreateBus()
.Start(() => Configure.Instance.ForInstallationOn<NServiceBus.Installation.Environments.Windows>().Install());
//_container.Register(Component.For<IBus>().Instance(bus).LifeStyle.Singleton);
Does that help?