How can I let Rebus determine an endpoint for an interface and not for an implementation of that interface

核能气质少年 提交于 2019-12-23 21:20:53

问题


When I Send an instance created by AutoMapper.Mapper.DynamicMap() I run into an error where Rebus seems to want to determine an endpoint for proxy type returned by DynamicMap. This instance will contain an implementation of T assuming T is an interface type.

Is there a way in which I can let Rebus determine an endpoint for the interface type T and not for the implementing type returned by DynamicMap()?

I tried playing around with IDetermineMessageOwnership but had no luck so far.

public static void Send<T>(this IBus bus, object source)
{
    var message = Mapper.DynamicMap<T>(source);
    bus.Send<T>(message);
}

Thanks in advance!


回答1:


Sure! In your case, you could create your own implementation of IDetermineMessageOwnership which is the service that Rebus uses to map a message type to an endpoint.

If you want to leverage Rebus' existing logic, you could decorate any chosen strategy and extend it with a keep-looking-at-all-implemented-interfaces-until-one-can-be-mapped strategy like so:

Configure.With(adapter)
         .(...)
         .MessageOwnership(o => o.FromRebusConfigurationSection())
         .Decorators(d => d.AddDecoration(DecorateOwnershipMapping)))
         .CreateBus()
         .Start();

where DecorateOwnershipMapping would install a decorator on top of whatever is configured like so:

void DecorateOwnershipMapping(ConfigurationBackbone b)
{
    b.DetermineMessageOwnership = new CustomizedEndpointMapper(b.DetermineMessageOwnership);
}

and a possible implementation could look like this:

class CustomizedEndpointMapper : IDetermineMessageOwnership
{
    readonly IDetermineMessageOwnership wrappedEndpointMapper;

    public CustomizedEndpointMapper(IDetermineMessageOwnership wrappedEndpointMapper)
    {
        this.wrappedEndpointMapper = wrappedEndpointMapper;
    }

    public string GetEndpointFor(Type messageType)
    {
        var mappingCandidates = new[] {messageType}
            .Concat(messageType.GetInterfaces())
            .ToList();

        foreach (var typeToTry in mappingCandidates)
        {
            try
            {
                return wrappedEndpointMapper.GetEndpointFor(typeToTry);
            }
            catch{}
        }

        throw new InvalidOperationException(string.Format("Could not determine the owner of message of type {0} - attempted to map the following types: {1}",
            messageType, string.Join(", ", mappingCandidates)));
    }
}

thus iterating through the concrete type as well as all inherited interface types when trying to determine the owning endpoint.

In your case, I believe this would work flawlessly when determining the message owner. Only problem is that the serializer will most likely complain, because the dynamically generated type cannot be recognized again when the message is received.

Hence, this trick requires customization of the serializer as well. If you're using the (default) JSON serializer, you might get away with some custom resolvers like so:

Configure.With(...)
    .(...)
    .Serialization(s => s.UseJsonSerializer()
                         .AddNameResolver(CustomNameResolver)
                         .AddTypeResolver(CustomTypeResolver))
    .(...)

where CustomNameResolver and CustomTypeResolver are methods that must then take care of mapping the type to a type name and mapping the type name to a type that can then be deserialized into. In order to make this work with AutoMapper, you'll probably need to either

a) somehow use AutoMapper to look up the type of the received message and return that type from CustomTypeResolver, or

b) customize the serializer to have AutoMapper somehow participate in generating the object to be returned

but I must admit that I'm unsure whether the last part will play out smoothly.

A final note: if you succeed in making this work, I suggest you pack up your configuration spells into a reusable extension method on RebusConfigurer so that your endpoints can just go

Configure.With(...)
    .(...)
    .UseMyFunkyAutoMapperMessagesJustTheWayILikeIt()
    .CreateBus().Start();

in all your Rebus endpoints...

I'll appreciate it if you'll let me know how this works out for you! :)



来源:https://stackoverflow.com/questions/17609531/how-can-i-let-rebus-determine-an-endpoint-for-an-interface-and-not-for-an-implem

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!