Autofac interceptor not working for some classes - asp.net-core-2.0

I have to do some extra logic layer on existing classes. I'm using autofac.
The project is Windows service having also Kestrel server. Program.cs e.g.
void Main(string[] args) {
var customQueue = new CustomQueue();
var someClass = new SomeClass(customQueue);
var randomClass = new RandomClass();
// do some logic here with using declared instances
var server = new Server(someClass, randomClass);
server.Start();
}
And here is the Server.cs
class Server {
private IWebHost _host;
public Server(SomeClass cls, RandomClass cls1) {
// set to fields
}
void Start() {
_host = new WebHostBuilder()
.UseKestrel()
.ConfigureServices(services =>
{
services.AddAutoFac(); // first as per doc in order to scaffold 'ConfigureContainer'?
services.AddSingleton(someClass); // fields
services.AddSingleton(randomClass); // fields
})
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration(...)
.ConfigureLogging(...);
.UseStartup<Startuo>()
.Build();
_host.StartAsync();
}
}
Startup.cs
public class Startup
{
public ILifetimeScope AutofacContainer { get; private set; }
public IServiceProvider ConfigureServices(IServiceCollection services)
{
// create a container-builder and register dependencies
var builder = new ContainerBuilder();
// populate the service-descriptors added to `IServiceCollection`
// BEFORE you add things to Autofac so that the Autofac
// registrations can override stuff in the `IServiceCollection`
// as needed
builder.Populate(services);
builder.RegisterType<SomeClass>()
.As<ISomeClass>()
.EnableInterfaceInterceptors()
.InterceptedBy(typeof(SomeClassInterceptor));
builder.Register(_ => new SomeClassInterceptor());
AutofacContainer = builder.Build();
return new AutofacServiceProvider(AutofacContainer);
}
}
And last SomeClassInterceptor.cs
public class SomeClassInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
try
{
invocation.Proceed();
if (invocation.ReturnValue is Task taskResult)
{
taskResult.ContinueWith(
t =>
{
Console.WriteLine("OOHH YEAHH");
}, TaskContinuationOptions.None);
}
else
{
Console.WriteLine("WOW");
}
}
catch (Exception ex)
{
Console.WriteLine("EXCEPTIOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNN");
}
}
}
I've tried with Named and Typed registration with having Intercept(...) attribute but still no success. Nothing gets triggered.
Also ISomeClass is inheriting other interfaces, I've tried setting .As<>() also with those but no.
What am I missing?

In order to let the interceptor works. The class should be build by Autofac.
services.AddAutofac();
services.AddSingleton(someClass);
services.AddSingleton(randomClass);
In your case you configure Autofac using the AddAutofac method then add SomeClass as a singleton using the AddSingleton which will override the Autofac configuration. There is no way that Autofac can inject the interceptor in it.
If you want to register SomeClass as singleton you should register it using the SingleInstance() method
builder.RegisterType<SomeClass>()
.As<ISomeClass>()
.SingleInstance()
.EnableInterfaceInterceptors()
.InterceptedBy(typeof(SomeClassInterceptor));

Related

Configure AutoMapper using LightInject

Does anyone know how to configure AutoMapper using LightInject? The AutoMapper documentation only has examples for Ninject and Simple Injector.
I am having difficulty trying to register the AutoMapper configuration.
I'm using ASP.NET MVC C#.
public class CompositionRoot : ICompositionRoot
{
public void Compose(IServiceRegistry serviceRegistry)
{
serviceRegistry.Register(c => new AutoMapperConfiguration());
}
}
public static class AutoMapperConfiguration
{
public AutoMapperConfiguration()
{
Mapper.Initialize(cfg =>
cfg.AddProfiles(typeof(Namespace.Class).Assembly)
);
}
}
I figured it out. The code below is in the CompositionRoot, where the factory is registered using IServiceRegistry. I will be moving the var config = new MapperConfiguration(cfg => cfg.AddProfiles(typeof(CustomProfileClass).Assembly)); code to a custom MapperConfiguration class that I will create.
public class CompositionRoot : ICompositionRoot
{
public void Compose(IServiceRegistry serviceRegistry)
{
var config = new MapperConfiguration(cfg => cfg.AddProfiles(typeof(CustomProfileClass)));
serviceRegistry.Register(c => config.CreateMapper());
}
}

System.IO.InvalidDataException : ServiceStackHost.Instance has already been set (BasicAppHost)

I'm getting an error when i try to run some tests on my servicestack web service.
I'm using ServiceStack 4.5.8 and Nunit 3.5. The solution was created initially from a ServiceStackVS template.
The error, which appears on a number of tests, is
System.IO.InvalidDataException : ServiceStackHost.Instance has already been set (BasicAppHost)</br>
TearDown : System.NullReferenceException : Object reference not set to an instance of an object.</br>
at ServiceStack.ServiceStackHost.Init()</br>
at MyApp.Tests.EchoServiceUnitTests.OneTimeSetup() in </br>
C:\Repos\MyApp\Myapp\MyApp.Tests\EchoServiceUnitTests.cs:line 45 </br>
--TearDown</br>
at MyApp.Tests.EchoServiceUnitTests.TestFixtureTearDown() in </br>C:\Repos\MyApp\MyApp\MyApp.Tests\EchoServiceUnitTests.cs:line 54
One of the tests that regularly generates this error is
namespace Tests
{
[TestFixture]
public class EchoServiceUnitTests
{
private ServiceStackHost appHost;
[OneTimeSetUp]
public void OneTimeSetup()
{
this.appHost = new BasicAppHost(typeof(EchoService).Assembly).Init();
}
[OneTimeTearDown]
public void TestFixtureTearDown()
{
this.appHost.Dispose();
}
[Test]
public void TestService()
{
const string Message = "Hello";
var service = this.appHost.Container.Resolve <EchoService>();
var response = (EchoResponse)service.Any(new Echo
{
Message = Message
});
Assert.That(response.Message,
Is.EqualTo(Message));
}
}
}
the service for this is
namespace ServiceInterface
{
public class EchoService : Service
{
public object Any(Echo request)
{
return new EchoResponse {Message = request.Message};
}
}
}
[Route("/Echo")]
[Route("/Echo/{Message}")]
public class Echo : IReturn<EchoResponse>
{
public string Message { get; set; }
}
public class EchoResponse : IHasResponseStatus
{
public EchoResponse()
{
this.ResponseStatus = new ResponseStatus();
}
public string Message { get; set; }
public ResponseStatus ResponseStatus { get; set; }
}
And finally my apphost
namespace MyApplication
{
using System;
using Funq;
using ServiceInterface;
using ServiceModel.Validators;
using ServiceStack;
using ServiceStack.Admin;
using ServiceStack.Api.Swagger;
using ServiceStack.Caching;
using ServiceStack.Configuration;
using ServiceStack.Logging;
using ServiceStack.Logging.NLogger;
using ServiceStack.MsgPack;
using ServiceStack.OrmLite;
using ServiceStack.OrmLite.SqlServer.Converters;
using ServiceStack.ProtoBuf;
using ServiceStack.Razor;
using ServiceStack.Validation;
using ServiceStack.VirtualPath;
using ServiceStack.Wire;
public class AppHost : AppHostBase
{
public static ILog Log = LogManager.GetLogger(typeof(AppHost));
public AppHost()
: base("MyApp",
typeof(HelloService).Assembly) { }
public override void Configure(Container container)
{
LogManager.LogFactory = new NLogFactory();
Log = LogManager.GetLogger(this.GetType());
this.Plugins.Add(new RazorFormat());
this.Plugins.Add(new PostmanFeature());
this.Plugins.Add(new SwaggerFeature());
this.Plugins.Add(new AdminFeature());
var ormSettings = new AppSettings();
container.Register <ICacheClient>(new MemoryCacheClient());
var dbFactory = new OrmLiteConnectionFactory(ormSettings.GetString("SqlDbConnection"),
SqlServerDialect.Provider);
dbFactory.RegisterConnection("Database2",
ormSettings.GetString("Sql2Connection"),
SqlServerDialect.Provider);
SqlServerDialect.Provider.RegisterConverter<DateTime?>(new SqlServerDateTimeConverter());
this.Plugins.Add(new RequestLogsFeature
{
RequestLogger = new CsvRequestLogger(files: new FileSystemVirtualPathProvider(this,
this.Config.WebHostPhysicalPath),
requestLogsPattern: "requestlogs/{year}-{month}/{year}-{month}-{day}.csv",
errorLogsPattern: "requestlogs/{year}-{month}/{year}-{month}-{day}-errors.csv",
appendEvery: TimeSpan.FromSeconds(1)),
EnableRequestBodyTracking = true,
EnableResponseTracking = true,
EnableErrorTracking = true,
});
this.Plugins.Add(new AutoQueryDataFeature
{
MaxLimit = 1000
});
this.Plugins.Add(new AutoQueryFeature());
var sse = new ServerEventsFeature
{
StreamPath = "/event-stream",
HeartbeatPath = "/event-heartbeat",
UnRegisterPath = "/event-unregister",
SubscribersPath = "/event-subscribers",
LimitToAuthenticatedUsers = false,
IdleTimeout = TimeSpan.FromSeconds(30),
HeartbeatInterval = TimeSpan.FromSeconds(10),
NotifyChannelOfSubscriptions = true,
};
this.Plugins.Add(sse);
Plugins.Add(new AdminFeature());
Plugins.Add(new WireFormat());
Plugins.Add(new MsgPackFormat());
Plugins.Add(new ProtoBufFormat());
}
}
}
I've tried a variety of suggestions including making the apphost in the test static, but nothing seems to work for me. I then tried the following test which also generated the same error which suggests to me that there is something in the apphost which is wrong but I can't see what.
[TestFixture(Category = "AppHost")]
public class AppHostTests
{
/// <summary>
/// The app host doesnt throw exception.
/// </summary>
[Test]
public void AppHostDoesntThrowException()
{
var apphost = new AppHost();
Assert.That(() => apphost.Init(),
Throws.Nothing);
}
}
The tests that generate this error whether I am using NCRUNCH (set to run one at a time) or if I use resharpers run all tests. It's generally the same tests that generate this error, though that seems to vary. In all cases, if I then run the tests manually they all pass.
You can only have 1 AppHost initialized and running at the same time where somehow NCrunch test is being run whilst there is another AppHost still in use. Maybe you can try debugging and setting a breakpoint that checks if ServiceStackHost.Instance is not null before trying to initialize another AppHost.
Note the AppHostBase is an ASP.NET Web App which may be causing the interference if it's running in the same project as the unit tests. If you want an integration test use AppSelfHostBase instead which you would use in place of BasicAppHost where you'd want to run a full integration test.

ServiceBusTrigger POCO Deserialization

I would like to see if/how it would be possible to plug into the deserialization process for a parameter that's decorated with the ServiceBusTrigger?
Say I have a function that looks like:
public static void HandleMessage([ServiceBusTrigger("myqueue")] MyCustomType myCustomType) { }
How would I go about taking over the deserialization? I know that there is a notion of an IArgumentBindingProvider and IArgumentBinding but it does not look like ServiceBusTrigger supports these concepts.
I know I can use GetBody<Stream>() and deserialize that way but I'd like to know if I can plug into the ServiceBusTrigger's pipeline. By the looks at the SDK, the ServiceBusTrigger has a hard coded list of IQueueArgumentBindingProviders and so I can't add my own.
If you have a look at the Azure WebJobs SDK Extensions, there is an overview on how to create your own bindings :
Binding Extensions Overview
Otherwise the ServiceBusConfiguration exposes a MessagingProvider property that allows you to intercept the ServiceBusTrigger pipeline:
private static void Main()
{
var sbConfig = new ServiceBusConfiguration()
{
MessagingProvider = // you implemetation of the MessagingProvider class goes here !!!
};
var config = new JobHostConfiguration();
config.UseServiceBus(sbConfig);
new JobHost(config).RunAndBlock();
}
Here is a simple skeleton of a MessagingProvider implementation:
public sealed class MyMessagingProvider : MessagingProvider
{
private readonly ServiceBusConfiguration _config;
public MyMessagingProvider(ServiceBusConfiguration config)
: base(config)
{
_config = config;
}
public override MessageProcessor CreateMessageProcessor(string entityPath)
{
return new MyMessageProcessor(_config.MessageOptions);
}
private class MyMessageProcessor : MessageProcessor
{
public MyMessageProcessor(OnMessageOptions messageOptions)
: base(messageOptions)
{
}
public override Task<bool> BeginProcessingMessageAsync(BrokeredMessage message, CancellationToken cancellationToken)
{
// Intercept the message before the execution of the triggerred function
return base.BeginProcessingMessageAsync(message, cancellationToken);
}
public override Task CompleteProcessingMessageAsync(BrokeredMessage message, FunctionResult result, CancellationToken cancellationToken)
{
// Intercept the message after the execution of the triggerred function and before being completed
return base.CompleteProcessingMessageAsync(message, result, cancellationToken);
}
}
}
So you're main function now looks like that:
private static void Main()
{
var sbConfig = new ServiceBusConfiguration();
sbConfig.MessagingProvider = new MyMessagingProvider(sbConfig);
var config = new JobHostConfiguration();
config.UseServiceBus(sbConfig);
new JobHost(config).RunAndBlock();
}

ASP.NET Web Api 2, Ninject, OWIN, and IIS

I'm using Ninject for dependency injection in my ASP.NET Web Api 2 project. Everything is working perfectly locally through Visual Studio and IIS Express, but when I deploy to IIS, the dependency's are not resolved. Below is my Startup.cs
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
var webApiConfiguration = new HttpConfiguration();
webApiConfiguration.EnableCors();
webApiConfiguration.SuppressDefaultHostAuthentication();
webApiConfiguration.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
webApiConfiguration.MapHttpAttributeRoutes();
app.UseNinjectMiddleware(CreateKernel).UseNinjectWebApi(webApiConfiguration);
ConfigureAuth(app);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
app.Run(async context =>
{
await context.Response.WriteAsync("Welcome to Web API");
});
}
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
kernel.Load(new CourseModule(), new DataPullModule(), new DegreeModule(), new ResponseModule(), new RestSharpModule());
return kernel;
}
}
The error I get is when trying to access one of my controllers is below:
An error occurred when trying to create a controller of type 'DegreeController'. Make sure that the controller has a parameterless public constructor.
Here is my constructor for the DegreeController:
public DegreeController(IDegreeMapper degreeMapper, IDegreeRepository degreeRepository)
{
_degreeMapper = degreeMapper;
_degreeRepository = degreeRepository;
}
And here is the DegreeModule where I bind interfaces to classes.
public class DegreeModule : NinjectModule
{
public override void Load()
{
Bind<IDegreeController>().To<DegreeController>().InRequestScope();
Bind<IDegreeMapper>().To<DegreeMapper>().InRequestScope();
Bind<IDegreeRepository>().To<DegreeRepository>().InRequestScope();
Bind<IDegreeRatingCalculator>().To<DegreeRatingCalculator>().InRequestScope();
}
}
var kernel = CreateKernel();
app.UseNinjectMiddleware(() => kernel).UseNinjectWebApi(configuration);

How to set log4net's log file location in autofac?

I am now adding controller log by following this post: Using Autofac to inject log4net into controller
After doing that, I can get my application run correctly. Below are the details:
LogInjectionModule here:
public class LogInjectionModule:Module
{
protected override void AttachToComponentRegistration(Autofac.Core.IComponentRegistry componentRegistry, Autofac.Core.IComponentRegistration registration)
{
registration.Preparing += OnComponentPreparing;
}
static void OnComponentPreparing(object sender, PreparingEventArgs e)
{
var t = e.Component.Activator.LimitType;
e.Parameters = e.Parameters.Union(new[]
{
new ResolvedParameter((p, i) => p.ParameterType == typeof(ILog), (p, i) => LogManager.GetLogger(t))
});
}
}
DependencyRegister here:
private void RegisterDependency()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>)).InstancePerHttpRequest();
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerHttpRequest();
builder.RegisterType<BookContext>().As<IDbContext>().SingleInstance().PreserveExistingDefaults();
builder.RegisterType<ManagerRepository>().As<IManager>().InstancePerHttpRequest();
builder.RegisterType<BookLendRepository>().As<IBookLend>().InstancePerHttpRequest();
builder.RegisterType<BookPlaceRepository>().As<IBookPlace>().InstancePerHttpRequest();
builder.RegisterType<BookRepository>().As<IBook>().InstancePerHttpRequest();
builder.RegisterType<BookTypeRepository>().As<IBookType>().InstancePerHttpRequest();
builder.RegisterType<StudentRepository>().As<IStudent>().InstancePerHttpRequest();
builder.RegisterType<ManagerService>().As<IManagerService>().InstancePerHttpRequest();
builder.RegisterModule(new LogInjectionModule());
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
MyController here:
public HomeController(
IManagerService managerService
,ILog logger
)
{
this.managerService = managerService;
this.logger = logger;
}
private readonly IManagerService managerService;
private readonly ILog logger;
public ActionResult Index(Manager manager)
{
logger.Info("test");
return View();
}
And when I debug to logger.Info("test") , I can get the log instance. But the problem is , where is the log file's location? is there any config for the integrated log4net that I can decide where to put the log file?
The question isn't related to Autofac. Log4net is a standalone library. You can read more about log4net on its homepage and how to configure it here.

Resources