after using massTransit (8.0.8) I got following error :
'RoutingSlipCompleted' does not contain a definition for 'GetVariable'
and the best extension method overload
'RoutingSlipEventExtensions.GetVariable(ConsumeContext,
string, Guid)' requires a receiver of type
'ConsumeContext'
here is my code:
using MassTransit;
using MassTransit.Courier.Contracts;
using MassTransit.Courier;
public class CheckInventoriesConsumer: IConsumer<ICheckInventoryRequest>
, IConsumer<RoutingSlipCompleted>
, IConsumer<RoutingSlipFaulted>
{
private readonly IEndpointNameFormatter _formatter;
public CheckInventoriesConsumer(IEndpointNameFormatter formatter)
{
_formatter = formatter;
}
public async Task Consume(ConsumeContext<ICheckInventoryRequest> context)
{
var routingSlip = CreateRoutingSlip(context);
await context.Execute(routingSlip);
}
private RoutingSlip CreateRoutingSlip(ConsumeContext<ICheckInventoryRequest> context)
{ // lot of code here
}
public async Task Consume(ConsumeContext<RoutingSlipCompleted> context)
{
// error is here
context.Message.GetVariable<Guid>(nameof(ConsumeContext.RequestId));
throw new NotImplementedException();
}
}
It is not going to find GetVariable method from MassTransit.Courier and I encounter with this error.
As you've already found based upon your comments:
context.GetVariable<Guid>(nameof(ConsumeContext.RequestId));
Is the right solution.
MassTransit Version 8 has more extensive serialization support, and the SerializationContext (from ConsumeContext) is needed to properly deserialize the variable from the routing slip event.
Related
I've a java binding for android which somewhat works bar the new feature I'm trying to integrate with. Only now I have realised that the intended callback is not happening. Here are the classes (decompiled to java) in question:
At the top level we have
public interface MyPackage {
MyPackage.Companion Companion = MyPackage.Companion.$$INSTANCE;
public static final class Companion {
#Nullable
private static MyEventHandler myEventHandler;
// $FF: synthetic field
static final MyPackage.Companion $$INSTANCE;
#Nullable
public final MyEventHandler getMyEventHandler() {
return myEventHandler;
}
public final void setMyEventHandler(#Nullable MyEventHandler var1) {
myEventHandler = var1;
}
private Companion() {
}
static {
MyPackage.Companion var0 = new MyPackage.Companion();
$$INSTANCE = var0;
}
}
}
MyEventHandler class:
public abstract class MyEventHandler {
public abstract void handleEvent(#NotNull String var1, #NotNull Properties var2);
}
Properties class:
import java.util.Map;
public class Properties extends r {
public Properties() {
}
Properties(Map<String, Object> var1) {
super(var1);
}
public Properties a(String var1, Object var2) {
super.b(var1, var2);
return this;
}
}
and the problematic r class:
public class r implements Map<String, Object> {
private final Map<String, Object> a;
various implementations...
}
So I noticed the issue when I couldnt override the HandleEvent method at the integration level and started looking at the Binding logs and found:
Warning=>
BINDINGSGENERATOR: Warning BG8801: Invalid parameter type MyPackage...Properties in method HandleEvent in managed type MyPackage.MyEventHandler. (BG8801)
And in build logs:
message BG0000: warning BG8102: Class MyPackage....Properties has unknown base type MyPackage....r.
warning BG8801: Invalid parameter type MyPackage...Properties in method HandleEvent in managed type MyPackage.MyEventHandler.
As it was obvious r is an obfuscated class I need to make chagnes to my Metadata so I went ahead and popped in:
<attr path="/api/package[#name='MyPackage']/class[#name='r']" name="obfuscated">false</attr>
Which resulted in the R being generated but now I get the 5 following compile error:
Error CS0535: 'R' does not implement interface member 'IMap.EntrySet()' (CS0535)
Error CS0738: 'R' does not implement interface member 'IMap.KeySet()'. 'R.KeySet()' cannot implement 'IMap.KeySet()' because it does not have the matching return type of 'ICollection'. (CS0738)
Error CS0535: 'R' does not implement interface member 'IMap.Put(Object?, Object?)' (CS0535)
Error CS0535: 'R' does not implement interface member 'IMap.PutAll(IDictionary?)' (CS0535)
Error CS0738: 'R' does not implement interface member 'IMap.Values()'. 'R.Values()' cannot implement 'IMap.Values()' because it does not have the matching return type of 'ICollection'. (CS0738)
I tried to make a managed return using
<attr path="/api/package[#name='MyPackage']/class[#name='r']/method[#name='entrySet' and count(parameter)=0]" name="managedReturn">Java.Util.IMap</attr>
With same number of compile error as above. Then I tried removing the node using:
<remove-node path="/api/package[#name='MyPackage']/class[#name='r']/method[#name='entrySet']"/>
Still no luck. :(
What am I missing here? Any pointers/suggestions will be appreciated!
It seems like you are trying to expose a Map to C# and as you stated, Java Generics are not handled very well.
In a very popular social network you received an answer from #mattleibow. I do not take credit for his answer but I went to check nonetheless and it seems fine.
If you look at the description of the Java.Lang.HashMap type
https://learn.microsoft.com/en-us/dotnet/api/java.util.hashmap?view=xamarin-android-sdk-9 it's a good candidate for you to expose.
You can also try with the corresponding interface for better safety https://learn.microsoft.com/en-us/dotnet/api/java.util.imap?view=xamarin-android-sdk-9
If it works you will still have to cast the types yourself.
Please answer to the comment to say that problem is solved for the sake of future generations arriving on this post :D
Credit is not mine so don't give it to me :-)
John,
I got arround fixing it by providing implementation of the the said methods in a partial class. Basically added a new file called R.cs under Additions folder as follows:
namespace YourNameSpace
{
public partial class R
{
public void PutAll(System.Collections.IDictionary p0)
{
PutAll(p0);
}
public Java.Lang.Object Put(Java.Lang.Object key, Java.Lang.Object value)
{
return Put(key, value);
}
public System.Collections.ICollection EntrySet()
{
return EntrySet();
}
public System.Collections.ICollection KeySet()
{
return KeySet();
}
public System.Collections.ICollection Values()
{
return Values();
}
}
}
I couldn't get it to work by adding XML transformation, but I think there was some tooling issue.
I have a following problem. I register my components and initialize them in Unity like this (example is for a Console application):
public class SharePointBootstrapper : UnityBootstrapper
{
...
public object Initialize(Type type, object parameter) =>
Container.Resolve(type,
new DependencyOverride<IClientContext>(Container.Resolve<IClientContext>(parameter.ToString())),
new DependencyOverride<ITenantRepository>(Container.Resolve<ITenantRepository>(parameter.ToString())));
public void RegisterComponents()
{
Container
.RegisterType<IClientContext, SharePointOnlineClientContext>(SharePointClientContext.Online.ToString())
.RegisterType<IClientContext, SharePointOnPremiseClientContext>(SharePointClientContext.OnPremise.ToString())
.RegisterType<ITenantRepository, DocumentDbTenantRepository>(SharePointClientContext.Online.ToString())
.RegisterType<ITenantRepository, JsonTenantRepository>(SharePointClientContext.OnPremise.ToString());
}
}
public enum SharePointClientContext
{
Online,
OnPremise
}
class Program
{
static void Main(string[] args)
{
...
bootstrap.RegisterComponents();
var bla = bootstrap.Initialize(typeof(ISharePointManager), SharePointClientContext.Online);
}
}
So, I register my components in MVC, WCF, Console etc. once with RegisterComponents() and initialize them with Initialize().
My question is, if I want to initialize specific named registration at runtime, from e.g. user input, can it be done otherwise as the code presented (with InjectionFactory or similar)?
This code works fine, but I'm not happy with its implementation. I have a feeling that it could be written in RegisterComponents() instead of Initialize() so that it accepts a parameter of some type, but I don't know how to do it.
Or, is maybe my whole concept wrong? If so, what would you suggest? I need to resolve named registration from a parameter that is only known at runtime, regardless of the technology (MVC, WCF, Console, ...).
Thanks!
Instead of doing different registrations, I would do different resolves.
Let's say that you need to inject IClientContext, but you want different implementations depending on a runtime parameter.
I wrote a similiar answer here. Instead of injecting IClientContext, you could inject IClientContextFactory, which would be responsible for returning the correct IClientContext. It's called Strategy Pattern.
public interface IClientContextFactory
{
string Context { get; } // Add context to the interface.
}
public class SharePointOnlineClientContext : IClientContextFactory
{
public string Context
{
get
{
return SharePointClientContext.Online.ToString();
}
}
}
// Factory for resolving IClientContext.
public class ClientContextFactory : IClientContextFactory
{
public IEnumerable<IClientContext> _clientContexts;
public Factory(IClientContext[] clientContexts)
{
_clientContexts = clientContexts;
}
public IClientContext GetClientContext(string parameter)
{
IClientContext clientContext = _clientContexts.FirstOrDefault(x => x.Context == parameter);
return clientContext;
}
}
Register them all, just as you did. But instead of injecting IClientContext you inject IClientContextFactor.
There also another solution where you use a Func-factory. Look at option 3, in this answer. One may argue that this is a wrapper for the service locator-pattern, but I'll leave that discussion for another time.
public class ClientContextFactory : IClientContextFactory
{
private readonly Func<string, IClientContext> _createFunc;
public Factory(Func<string, IClientContext> createFunc)
{
_createFunc = createFunc;
}
public IClientContext CreateClientContext(string writesTo)
{
return _createFunc(writesTo);
}
}
And use named registrations:
container.RegisterType<IClientContext, SharePointOnlineClientContext>(SharePointClientContext.Online.ToString());
container.RegisterType<IClientContext, SharePointOnPremiseClientContext>(SharePointClientContext.OnPremise.ToString());
container.RegisterType<IFactory, Factory>(
new ContainerControlledLifetimeManager(), // Or any other lifetimemanager.
new InjectionConstructor(
new Func<string, IClientContext>(
context => container.Resolve<IClientContext>(context));
Usage:
public class MyService
{
public MyService(IClientContextFactory clientContextFactory)
{
_clientContextFactory = clientContextFactory;
}
public void DoStuff();
{
var myContext = SharePointClientContext.Online.ToString();
IClientContextclientContext = _clientContextFactory.CreateClientContext(myContext);
}
}
I'm trying to use the ResourceAuthorize attribute from Thinktecture.IdentityModel, but everything stops because there is no owin context.
I have a owin startup class which setups the authorization manager
[assembly: OwinStartup(typeof(My.WebApi.Startup))]
namespace My.WebApi
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
AuthConfig.Configure(app);
}
}
}
public class AuthConfig
{
public static void Configure(IAppBuilder app)
{
app.UseResourceAuthorization(new ResourceAuthorizationMiddlewareOptions
{
Manager = GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IResourceAuthorizationManager)) as IResourceAuthorizationManager
});
}
}
and I know that it is detected and invoked. But later on, when hitting the following code from IdentityModel, I get a null pointer exception:
public static Task<bool> CheckAccessAsync(this HttpRequestMessage request, IEnumerable<Claim> actions, IEnumerable<Claim> resources)
{
var authorizationContext = new ResourceAuthorizationContext(
request.GetOwinContext().Authentication.User ?? Principal.Anonymous,
actions,
resources);
return request.CheckAccessAsync(authorizationContext);
}
I have stepped through and sees that it's caused by the GetOwinContext() returning null, since there is no MS_OwinContext or MS_OwinEnvironment property on the request.
What am I missing?
UPDATE:
I have found that i have an owin.environment property available, but it's part of the `HttpContextWrapper, not the request.
By searching around, I found some code inside of System.Web.Http.WebHost.HttpControllerHandler that looks like it should have converted the owin.environment to an MS_OwinEnvironment, but apparently, that code is never called in my case...
internal static readonly string OwinEnvironmentHttpContextKey = "owin.Environment";
internal static readonly string OwinEnvironmentKey = "MS_OwinEnvironment";
internal static HttpRequestMessage ConvertRequest(HttpContextBase httpContextBase, IHostBufferPolicySelector policySelector)
{
HttpRequestBase requestBase = httpContextBase.Request;
HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethodHelper.GetHttpMethod(requestBase.HttpMethod), requestBase.Url);
bool bufferInput = policySelector == null || policySelector.UseBufferedInputStream((object) httpContextBase);
httpRequestMessage.Content = HttpControllerHandler.GetStreamContent(requestBase, bufferInput);
foreach (string str in (NameObjectCollectionBase) requestBase.Headers)
{
string[] values = requestBase.Headers.GetValues(str);
HttpControllerHandler.AddHeaderToHttpRequestMessage(httpRequestMessage, str, values);
}
HttpRequestMessageExtensions.SetHttpContext(httpRequestMessage, httpContextBase);
HttpRequestContext httpRequestContext = (HttpRequestContext) new WebHostHttpRequestContext(httpContextBase, requestBase, httpRequestMessage);
System.Net.Http.HttpRequestMessageExtensions.SetRequestContext(httpRequestMessage, httpRequestContext);
IDictionary items = httpContextBase.Items;
if (items != null && items.Contains((object) HttpControllerHandler.OwinEnvironmentHttpContextKey))
httpRequestMessage.Properties.Add(HttpControllerHandler.OwinEnvironmentKey, items[(object) HttpControllerHandler.OwinEnvironmentHttpContextKey]);
httpRequestMessage.Properties.Add(HttpPropertyKeys.RetrieveClientCertificateDelegateKey, (object) HttpControllerHandler._retrieveClientCertificate);
httpRequestMessage.Properties.Add(HttpPropertyKeys.IsLocalKey, (object) new Lazy<bool>((Func<bool>) (() => requestBase.IsLocal)));
httpRequestMessage.Properties.Add(HttpPropertyKeys.IncludeErrorDetailKey, (object) new Lazy<bool>((Func<bool>) (() => !httpContextBase.IsCustomErrorEnabled)));
return httpRequestMessage;
}
UPDATE 2:
Inside of mvc controllers, the context is available. But not in webapi controllers.
A team mate found a solution. He simply added the following line to the owin startup class:
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
Why this solves the issue is another mystery, though. But we are using wsFederation, so I guess it's needed some how. But what if we didn't use wsFed? Would we still need it to get a context? Who knows...
I am actually working in an ASP.Net MVC 4 web application where we are using NInject for dependency injection. We are also using UnitOfWork and Repositories based on Entity framework.
We would like to use Quartz.net in our application to start some custom job periodically. I would like that NInject bind automatically the services that we need in our job.
It could be something like this:
public class DispatchingJob : IJob
{
private readonly IDispatchingManagementService _dispatchingManagementService;
public DispatchingJob(IDispatchingManagementService dispatchingManagementService )
{
_dispatchingManagementService = dispatchingManagementService ;
}
public void Execute(IJobExecutionContext context)
{
LogManager.Instance.Info(string.Format("Dispatching job started at: {0}", DateTime.Now));
_dispatchingManagementService.DispatchAtomicChecks();
LogManager.Instance.Info(string.Format("Dispatching job ended at: {0}", DateTime.Now));
}
}
So far, in our NInjectWebCommon binding is configured like this (using request scope):
kernel.Bind<IDispatchingManagementService>().To<DispatchingManagementService>();
Is it possible to inject the correct implementation into our custom job using NInject ? and how to do it ? I have read already few posts on stack overflow, however i need some advises and some example using NInject.
Use a JobFactory in your Quartz schedule, and resolve your job instance there.
So, in your NInject config set up the job (I'm guessing at the correct NInject syntax here)
// Assuming you only have one IJob
kernel.Bind<IJob>().To<DispatchingJob>();
Then, create a JobFactory: [edit: this is a modified version of #BatteryBackupUnit's answer here]
public class NInjectJobFactory : IJobFactory
{
private readonly IResolutionRoot resolutionRoot;
public NinjectJobFactory(IResolutionRoot resolutionRoot)
{
this.resolutionRoot = resolutionRoot;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
// If you have multiple jobs, specify the name as
// bundle.JobDetail.JobType.Name, or pass the type, whatever
// NInject wants..
return (IJob)this.resolutionRoot.Get<IJob>();
}
public void ReturnJob(IJob job)
{
this.resolutionRoot.Release(job);
}
}
Then, when you create the scheduler, assign the JobFactory to it:
private IScheduler GetSchedule(IResolutionRoot root)
{
var schedule = new StdSchedulerFactory().GetScheduler();
schedule.JobFactory = new NInjectJobFactory(root);
return schedule;
}
Quartz will then use the JobFactory to create the job, and NInject will resolve the dependencies for you.
Regarding scoping of the IUnitOfWork, as per a comment of the answer i linked, you can do
// default for web requests
Bind<IUnitOfWork>().To<UnitOfWork>()
.InRequestScope();
// fall back to `InCallScope()` when there's no web request.
Bind<IUnitOfWork>().To<UnitOfWork>()
.When(x => HttpContext.Current == null)
.InCallScope();
There's only one caveat that you should be aware of:
With incorrect usage of async in a web request, you may mistakenly be resolving a IUnitOfWork in a worker thread where HttpContext.Current is null. Now without the fallback binding, this would fail with an exception which would show you that you've done something wrong. With the fallback binding however, the issue may present itself in an obscured way. That is, it may work sometimes, but sometimes not. This is because there will be two (or even more) IUnitOfWork instances for the same request.
To remedy this, we can make the binding more specific. For this, we need some parameter to tell us to use another than InRequestScope(). Have a look at:
public class NonRequestScopedParameter : Ninject.Parameters.IParameter
{
public bool Equals(IParameter other)
{
if (other == null)
{
return false;
}
return other is NonRequestScopedParameter;
}
public object GetValue(IContext context, ITarget target)
{
throw new NotSupportedException("this parameter does not provide a value");
}
public string Name
{
get { return typeof(NonRequestScopedParameter).Name; }
}
// this is very important
public bool ShouldInherit
{
get { return true; }
}
}
now adapt the job factory as follows:
public class NInjectJobFactory : IJobFactory
{
private readonly IResolutionRoot resolutionRoot;
public NinjectJobFactory(IResolutionRoot resolutionRoot)
{
this.resolutionRoot = resolutionRoot;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
return (IJob) this.resolutionRoot.Get(
bundle.JobDetail.JobType,
new NonrequestScopedParameter()); // parameter goes here
}
public void ReturnJob(IJob job)
{
this.resolutionRoot.Release(job);
}
}
and adapt the IUnitOfWork bindings:
Bind<IUnitOfWork>().To<UnitOfWork>()
.InRequestScope();
Bind<IUnitOfWork>().To<UnitOfWork>()
.When(x => x.Parameters.OfType<NonRequestScopedParameter>().Any())
.InCallScope();
This way, if you use async wrong, there'll still be an exception, but IUnitOfWork scoping will still work for quartz tasks.
For any users that could be interested, here is the solution that finally worked for me.
I have made it working doing some adjustment to match my project. Please note that in the method NewJob, I have replaced the call to Kernel.Get by _resolutionRoot.Get.
As you can find here:
public class JobFactory : IJobFactory
{
private readonly IResolutionRoot _resolutionRoot;
public JobFactory(IResolutionRoot resolutionRoot)
{
this._resolutionRoot = resolutionRoot;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
try
{
return (IJob)_resolutionRoot.Get(
bundle.JobDetail.JobType, new NonRequestScopedParameter()); // parameter goes here
}
catch (Exception ex)
{
LogManager.Instance.Info(string.Format("Exception raised in JobFactory"));
}
}
public void ReturnJob(IJob job)
{
}
}
And here is the call schedule my job:
public static void RegisterScheduler(IKernel kernel)
{
try
{
var scheduler = new StdSchedulerFactory().GetScheduler();
scheduler.JobFactory = new JobFactory(kernel);
....
}
}
Thank you very much for your help
Thanks so much for your response. I have implemented something like that and the binding is working :):
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
var resolver = DependencyResolver.Current;
var myJob = (IJob)resolver.GetService(typeof(IJob));
return myJob;
}
As I told before I am using in my project a service and unit of work (based on EF) that are both injected with NInject.
public class DispatchingManagementService : IDispatchingManagementService
{
private readonly IUnitOfWork _unitOfWork;
public DispatchingManagementService(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
}
Please find here how I am binding the implementations:
kernel.Bind<IUnitOfWork>().To<EfUnitOfWork>()
kernel.Bind<IDispatchingManagementService>().To<DispatchingManagementService>();
kernel.Bind<IJob>().To<DispatchingJob>();
To resume, the binding of IUnitOfWork is done for:
- Eevery time a new request is coming to my application ASP.Net MVC: Request scope
- Every time I am running the job: InCallScope
What are the best practices according to the behavior of EF ? I have find information to use CallInScope. Is it possible to tell NInject to get a scope ByRequest everytime a new request is coming to the application, and a InCallScope everytime my job is running ? How to do that ?
Thank you very much for your help
I have a Spring 3.2 Controller with basic request mappings like
#RequestMapping("/action")
public String action(#RequestParam("param") String param) {
//do stuff...
return "view";
}
This controller handles links created by non-technical business users. Sometimes the users mess it up and create links with duplicate parameters, e.g.,
www.example.com/action?param=value¶m=value
The parameter is an exact duplicate and probably a copy/paste error.
My problem is that Spring is concatenating these dupes together, so that the url above will give "value,value" for param, when I want only "value".
What is a good way to detect and handle these duplicates? I know I could change all my #RequestParams to List<String>s and go from there, but that's a whole lot of boilerplate over dozens of request mappings.
Ideally there would be a way to intercept and modify the url parameters before Spring attempts to bind them -- but only for this controller.
I found that I can register a custom String property editor to do this.
class DuplicateParameterReducingPropertyEditor extends PropertyEditorSupport {
Object value;
#Override
public void setValue(Object value) {
if (value instanceof String[]) {
String[] strings = (String[])value;
Set<String> unique = Sets.newHashSet(strings);
this.value = unique.toArray();
} else {
this.value = value;
}
}
#Override
public void setAsText(String text) throws IllegalArgumentException {
this.value = text;
}
#Override
public String getAsText() {
return value.toString();
}
#Override
public Object getValue() {
return value;
}
};
I added this to my controller:
#InitBinder
public void initBinder(WebDataBinder binder) {
PropertyEditor stringEditor = new DuplicateParameterReducingPropertyEditor();
binder.registerCustomEditor(String.class, stringEditor);
}
So whenever Spring encounters a #RequestParam-annotated String method argument, the PropertyEditor is invoked to transform the incoming data if needed. In the case of duplicate parameters, Spring passes a String[] of the values to the property editor setValue, which I can then manipulate.
This does have the results I am looking for. I'm not sure of all the implications of this, though, so I can't endorse it as good solution yet. Not having to alter any handler method signatures is a big plus though.
A good idea would be to extend AbstractNamedValueMethodArgumentResolver with your own strategy. Then the strategy could be used wherever you deem necessary.
This strategy only works for Spring 3.1+ which is not a problem for you since you are using Spring 3.2
I faced the same issue in Spring boot. Eventually I came up with this solution using converter, in case it helps anyone.
This method should be added as part of your WebMvcConfigurer class.
#Override
public void addFormatters(FormatterRegistry registry) {
// Duplicate query parameters converter
registry.addConverter(new Converter<String[], String>() {
public String convert(String[] arr) {
return arr[arr.length - 1]; // Return the last value
}
});
}