How to use Rebus.Logging.ILog - rebus

Is it possible to use ILog below with Azure Function logging or Serilog etc?
I cannot find code example on how to use it.
Rebus.Logging.ILog
.Options(o =>
{
o.Decorate<IErrorHandler>(c =>
new ErrorMessageHandler(c.Get<IErrorHandler>(), c.Get<ILog>()));

It's certainly possibly – but since Rebus' loggers are created with a type (the type works as a context of sorts – I think Serilog calls it "source context"), you do not inject the logger, you inject a logger factory:
.Options(o =>
{
o.Decorate<IErrorHandler>(c => {
var errorHandler = c.Get<IErrorHandler>();
var loggerFactory = c.Get<IRebusLoggerFactory>();
return new ErrorMessageHandler(errorHandler, loggerFactory));
});
}
and then, in the constructor of ErrorMessageHandler, you can get the logger:
public class ErrorMessageHandler : IErrorHandler
{
readonly IErrorHandler errorHandler;
readonly ILog log;
public ErrorMessageHandler(IErrorHandler errorHandler, IRebusLoggerFactory loggerFactory)
{
this.errorHandler = errorHandler;
log = loggerFactory.GetLogger<ErrorMessageHandler>();
}
public async Task HandlePoisonMessage(TransportMessage transportMessage, ITransactionContext transactionContext, Exception exception)
{
// do stuff in here
}
}

Related

Disable Authorization filter extending TypeFilterAttribute unit testing

for below code I want to do setup in ConfigureTestServices so that this particular attribute gets disabled while running the test.
`public class AuthorizePermissionAttribute : TypeFilterAttribute
{
public AuthorizePermissionAttribute(Type type, params string[] permissions) : base(typeof(AuthorizePermissionPreferenceFilter<>).MakeGenericType(type))
{
Arguments = new object[] { permissions };
}
}
public class AuthorizePermissionPreFilter<T> : AuthorizePermissionFilter, IAsyncAuthorizationFilter where T : IPrefScope
{
public AuthorizePermissionPrefFilter(
IRequestContext context,
IPermissionProvider permissionProvider,
ILogger<AuthorizePermissionPreFilter<T>> logger,
IOptionsSnapshot<CoreAppConfiguration> config,
string[] permissions) : base(context, permissionProvider, logger, config, permissions)
{
}
public new async Task OnAuthorizationAsync(AuthorizationFilterContext authContext)
{
await base.OnAuthorizationAsync(authContext);
}
}`
This has been added through middleware app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
I tried using MOQ, Moq.AutoMock with xunit but nothing helped

How to use AutoMapper 9.0.0 in Asp.Net Web Api 2 without dependency injection?

I haven't been able to find any info where to put this code inside my project. Right now I am use using this in each action I need the mapper. Is there a better way to do this with out dependency injection?
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<Source, Dest>();
});
IMapper iMapper = config.CreateMapper();
var destList= iMapper.Map<Dest[]>(sourceList);
Dependency injection added a whole level of complexity to my legacy project that I just didn't want to deal with. 9.0 removed the api to call it staticly.
So I just reverse engineered what it was doing in 8.0 and wrote a wrapper for it.
public static class MapperWrapper
{
private const string InvalidOperationMessage = "Mapper not initialized. Call Initialize with appropriate configuration. If you are trying to use mapper instances through a container or otherwise, make sure you do not have any calls to the static Mapper.Map methods, and if you're using ProjectTo or UseAsDataSource extension methods, make sure you pass in the appropriate IConfigurationProvider instance.";
private const string AlreadyInitialized = "Mapper already initialized. You must call Initialize once per application domain/process.";
private static IConfigurationProvider _configuration;
private static IMapper _instance;
private static IConfigurationProvider Configuration
{
get => _configuration ?? throw new InvalidOperationException(InvalidOperationMessage);
set => _configuration = (_configuration == null) ? value : throw new InvalidOperationException(AlreadyInitialized);
}
public static IMapper Mapper
{
get => _instance ?? throw new InvalidOperationException(InvalidOperationMessage);
private set => _instance = value;
}
public static void Initialize(Action<IMapperConfigurationExpression> config)
{
Initialize(new MapperConfiguration(config));
}
public static void Initialize(MapperConfiguration config)
{
Configuration = config;
Mapper = Configuration.CreateMapper();
}
public static void AssertConfigurationIsValid() => Configuration.AssertConfigurationIsValid();
}
To initialize it have a configure method
public static class AutoMapperConfig
{
public static void Configure()
{
MapperWrapper.Initialize(cfg =>
{
cfg.CreateMap<Foo1, Foo2>();
});
MapperWrapper.AssertConfigurationIsValid();
}
}
And just call it in your startup
AutoMapperConfig.Configure();
To use it just Add MapperWrapper before your Mapper call. Can be called anywhere.
MapperWrapper.Mapper.Map<Foo2>(Foo1);

Get transistent/scoped Database access in singletonservice

i updating my app from asp core 1.0 to 2.0. In 1.0 i have a soulution for my longlive import-task, initialated as singleton. The singleton used the DBContext. But in core 2.0 this soulution dosn't work. Can you help me?
My soulution in aps core 1.0 was
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("LocalConnection")));
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<IDataStore, DataStore>();
services.AddSingleton<IImportRepository, ImportRepository>();
with
public class ImportRepository : IImportRepository
{
Importer Importer;
private readonly ApplicationDbContext DBContext;
private readonly IDataStore store;
private ImportSet runningSet = null;
public ImportRepository(ApplicationDbContext context, IDataStore store)
{
this.DBContext = context;
this.store = store;
Importer = new Importer(DBContext, store);
}
With this soulutions i get errormessages (in german, but i try to translate). "you cannot use scoped services in singleton"
Last attempt i used this solution
services.AddSingleton<ImportService>(
provider => new ImportService((ApplicationDbContext)provider.GetService(typeof(ApplicationDbContext)))
);
But here i get the errormessage "Cannot resolve scoped service 'Portal.Data.ApplicationDbContext' from root provider."
How can i get access to my database in my Import-Service?
You may resolve dependencies manually using IServiceProvider instance.
public class ImportRepository : IImportRepository
{
private readonly IServiceProvider _provider;
public ImportRepository(IServiceProvider provider)
{
_provider = provider;
...
}
public void DoSomething()
{
var dBContext = (ApplicationDbContext) provider.GetService(typeof(ApplicationDbContext));
...
}
}
By the way, there is an extension method GetService<T>(); defined in Microsoft.Extensions.DependencyInjection namespace:
// using Microsoft.Extensions.DependencyInjection;
var dBContext = provider.GetService<ApplicationDbContext>();
Since your singleton lives longer and is shared, the only option I see is that you take it as a parameter to the functions.
public class ImportRepository : IImportRepository
{
public void DoSomething(ApplicationDbContext context, IDataStore store)
{
}
}
The other option is to make ImportRepository scoped as well.
Ok. I have a soulution, that works, but not perfektly.
Like Juunas example i build a long life funktion
public async Task RunImportAsync(string fileName, DataService data)
{
await Task.Run(() =>
{
if (!System.IO.File.Exists(internalPath + fileName))
{
throw new Exception($"Datei {fileName} nicht gefunden.");
}
[long Operations...]
data.DBContext.Add(new ImportHistory(set));
data.DBContext.SaveChanges();
});
}
the call is simple
[HttpPost]
[Route("runImport")]
public async Task<IActionResult> RunImport([FromBody]dynamic body)
{
string id = "";
try
{
id = body.filename;
_logger.LogInformation($"Import from {id}");
await ImportService.RunImportAsync(id, DB);
return StatusCode(StatusCodes.Success_2xx.OK);
}
catch (Exception e)
{
return SendError(e);
}
}
But postmen get no Response with this solution. Is there a idea, how i can fix it?

Where are plug-ins supposed to be added in ServiceStack

So simple yet I can't find any info or examples that explain exacty where this should happen. I'm guessing at this point that it should be in the Configure method.
Thank you,
Stephen
Global
public class AppHost : AppHostBase
{
public AppHost() : base("Web Services", typeof(ContactsService).Assembly) { }
public override void Configure(Container container)
{
//Set JSON web services to return idiomatic JSON camelCase properties
ServiceStack.Text.JsConfig.EmitCamelCaseNames = true;
//Show StackTrace in Web Service Exceptions
SetConfig(new EndpointHostConfig { DebugMode = true });
//Register any dependencies you want injected into your services
container.Register<ICacheClient>(new MemoryCacheClient());
/* // Redis
container.Register<IRedisClientsManager>(c => new PooledRedisClientManager());
container.Register<IRepository>(c => new Repository(c.Resolve<IRedisClientsManager>()));*/
container.Register<IRepository>(new Repository());
container.Register<IBusinessService>(new BusinessService());
//Configure Custom User Defined REST Paths for your services
/*ConfigureServiceRoutes();*/
//Add a request filter to check if the user has a session initialized
/*this.RequestFilters.Add((httpReq, httpResp, requestDto) =>
{
var sessionId = httpReq.GetCookieValue("user-session");
if (sessionId == null)
{
httpResp.ReturnAuthRequired();
}
});*/
RequestFilters.Add((httpReq, httpResp, requestDto) => new LogRequestAttribute().Execute(httpReq, httpResp, requestDto));
Plugins.Add(new SwaggerFeature());
}
public static void Start()
{
new AppHost().Init();
}
}
Updated
public AppHost() : base("Web Services", typeof(ContactsService).Assembly) { }
public override void Configure(Container container)
{
....
ConfigurePlugins();
}
private void ConfigurePlugins()
{
Plugins.Add(new ProtoBufFormat());
Plugins.Add(new RequestLogsFeature());
Plugins.Add(new SwaggerFeature());
}
private void ConfigureServiceRoutes()
{
}
public static void Start()
{
new AppHost().Init();
}
There is no info because Plugins in ServiceStack can be added anywhere inside your AppHost.Configure() method. This is true of all ServiceStack configuration and registration of dependencies, services, filters, etc.
It doesn't matter where in the AppHost.Configure() method they're added because they're only Initialized by ServiceStack after it has been called.
They are however initialized (i.e. IPlugin.Register() is called) in the same order that they were added.

ServiceStack IOC not injecting property in Attribute (object is null)

I'm trying to log/persist all my requests/responses, and thought that I give it a try with a global attribute, but when I go to actually using the repo, it's null? Is this possible?
Are there other ways to achieve what I'm looking to do?
Thank you,
Stephen
Attribute
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class LogRequestAttribute : RequestFilterAttribute
{
public IRepository Repo { get; set; }
public LogRequestAttribute(ApplyTo applyTo)
: base(applyTo)
{
this.Priority = -200;
}
public LogRequestAttribute()
: this(ApplyTo.All) {}
public override void Execute(IHttpRequest req, IHttpResponse res, object requestDto)
{
try
{
// Convert the req obj into something that can be persisted...
Repo.LogRequest("Logging the rquest");
}
catch (Exception ex)
{
System.Diagnostics.Trace.TraceError(ex.ToString());
}
}
}
AppHost Config
public override void Configure(Container container)
{
//Set JSON web services to return idiomatic JSON camelCase properties
ServiceStack.Text.JsConfig.EmitCamelCaseNames = true;
//Show StackTrace in Web Service Exceptions
SetConfig(new EndpointHostConfig { DebugMode = true });
//Register any dependencies you want injected into your services
container.Register<ICacheClient>(new MemoryCacheClient());
/* // Redis
container.Register<IRedisClientsManager>(c => new PooledRedisClientManager());
container.Register<IRepository>(c => new Repository(c.Resolve<IRedisClientsManager>()));*/
container.Register<IRepository>(new Repository());
container.Register<IBusinessService>(new BusinessService());
//Configure Custom User Defined REST Paths for your services
/*ConfigureServiceRoutes();*/
//Add a request filter to check if the user has a session initialized
/*this.RequestFilters.Add((httpReq, httpResp, requestDto) =>
{
var sessionId = httpReq.GetCookieValue("user-session");
if (sessionId == null)
{
httpResp.ReturnAuthRequired();
}
});*/
RequestFilters.Add((httpReq, httpResp, requestDto) => new LogRequestAttribute().Execute(httpReq, httpResp, requestDto));
}
Repository
public interface IRepository
{
void LogRequest(string request);
void LogResponse(string request);
}
public class Repository : IRepository
{
private static readonly ILog Log = LogManager.GetLogger("API.Repository");
public Repository()
{
}
public void LogRequest(string request)
{
Log.Debug(request);
}
public void LogResponse(string request)
{
Log.Debug(request);
}
}
Updated
//Add a 'global' request filter
this.RequestFilters.Add((httpReq, httpResp, requestDto) =>
{
/* Code here */
});
//Add a 'global' response filter
this.ResponseFilters.Add((httpReq, httpResp, responseDto) =>
{
/* Code here */
});
If you're trying to log requests in ServiceStack you should look to see if Request Logger plugin is useful. The RequestLogsFeature Plugin allows you to use your own custom IRequestLogger instead of the InMemoryRollingRequestLogger that's used by default.
Filter Attributes
Although you've defined a Request Filter attribute correctly you're not applying it correctly, which should be used just like any other C# Attribute (i.e. decorated). Filter Attributes can only be decorated on either the Service Type, its Request DTO or a Service Action where it is only run to the scope they are applied to.
Global Request Filters
There is no Global Request Filter Attribute, the Global Request filters only let you specify a delegate to get executed, which is all that's happening here:
RequestFilters.Add((httpReq, httpResp, requestDto) =>
new LogRequestAttribute().Execute(httpReq, httpResp, requestDto));
A new instance of the LogRequestAttribute type is constructed inline (and as seen above, is not resolved from the IOC) so it is not auto-wired. The fact that the method you're calling is an instance of a FilterAttribute is irrelevant since all the C# delegate is calling is a method on an empty LogRequestAttribute instance.
If registering a global filter inside Configure() you can access the container directly, e.g:
RequestFilters.Add((httpReq, httpResp, requestDto) =>
container.Resolve<IRepository>().LogRequest("Logging the request"));
Anywhere else, you can access ServiceStack's IOC with the singleton: AppHostBase.Resolve<T>().

Resources