2
votes

I'm attempting to follow the instructions to install the windows service host using NServiceBus.Host.exe for the VideoStore sample app. I'm following the instructions from the web site.

My application runs fine when doing an F5 session in Visual Studio. It's hosting using the console app mode for the host. When I attempt to use the command line to perform the installation, I get multiple errors.

The command line I'm running is:

NServiceBus.Host.exe /install /serviceName:"VideoStore.Sales" /displayName:"VideoStore.Sales" /description:"Endpoint for VideoStore.Sales" /endpointConfigurationType:"VideoStore.Sales.EndpointConfig, VideoStore.Sales" /username:".\MySvc" /password:"MyPassword" NServiceBus.Production

Running this resulted in the following exception:

Initializing the installer in the Install AppDomain

Unhandled Exception: System.InvalidOperationException: Sequence contains more than one matching element at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable1 source, Func2 predicate) at System.Linq.Enumerable.WhereSelectArrayIterator2.MoveNext() at System.Linq.Enumerable.WhereEnumerableIterator1.MoveNext() at System.Collections.Generic.List1..ctor(IEnumerable1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable1 source) at NServiceBus.Hosting.Profiles.ProfileManager..ctor(List1 assembliesToScan, IConfigureThisEndpoint specifier, String[] args, List`1 defaultProfiles)

It turns out, this error is caused because my application is referencing both the NServiceBus.Host assembly as well as the NServiceBus.Hosting.Azure assembly. This is because my application is being deployed both in a Windows environment as well as to an Azure worker role. I can switch between the azure emulator and the console-mode for worker roles without issue simply by changing which projects i'm starting when debugging. (Azure cloud service project vs each of the worker projects.)

I was able to resolve this by deleting the NServiceBus.Hosting.Azure.dll assembly to prevent the assembly scanning from finding it. IMHO, this is a bug. Either allow me to specify the host type explicitly or handle a scenario where multiple types are found.

This stopped the previous exception, and instead introduced a new one:

Unhandled Exception: System.Configuration.ConfigurationErrorsException: Command line argument 'endpointConfigurationType' has specified to use the type 'VideoStore.Sales.EndpointConfig, VideoStore.Sales' but that type could not be loaded. at NServiceBus.Hosting.Windows.EndpointTypeDeterminer.TryGetEndpointConfigurationTypeFromArguments(HostArguments arguments, Type& type) in y:\BuildAgent\work \31f8c64a6e8a2d7c\src\NServiceBus.Hosting.Windows\EndpointTypeDeterminer.cs:line 101 at NServiceBus.Hosting.Windows.Program.Main(String[] args) in y:\BuildAgent\work\31f8c64a6e8a2d7c\src\NServiceBus.Hosting.Windows\Program.cs:line 38

Both that type and that assembly exist. I've even verified that .NET is loading the type via enabling fusion loader logging:

The operation was successful. Bind result: hr = 0x0. The operation completed successfully.

Assembly manager loaded from: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\clr.dll Running under executable C:\Projects\Testing\NServiceBus.Azure.Samples-master\VideoStore.AzureServiceBus.Cloud\VideoStore.Sales\bin\Debug\NServiceBus.Host.exe --- A detailed error log follows.

=== Pre-bind state information === LOG: DisplayName = VideoStore.Sales (Partial) WRN: Partial binding information was supplied for an assembly: WRN: Assembly Name: VideoStore.Sales | Domain ID: 1 WRN: A partial bind occurs when only part of the assembly display name is provided. WRN: This might result in the binder loading an incorrect assembly. WRN: It is recommended to provide a fully specified textual identity for the assembly, WRN: that consists of the simple name, version, culture, and public key token. WRN: See whitepaper http://go.microsoft.com/fwlink/?LinkId=109270 for more information and common solutions to this issue. LOG: Appbase = file:///C:/Projects/Testing/NServiceBus.Azure.Samples-master/VideoStore.AzureServiceBus.Cloud/VideoStore.Sales/bin/Debug/ LOG: Initial PrivatePath = NULL LOG: Dynamic Base = NULL LOG: Cache Base = NULL LOG: AppName = NServiceBus.Host.exe Calling assembly : NServiceBus.Host, Version=4.6.0.0, Culture=neutral, PublicKeyToken=9fc386479f8a226c.

What am I doing wrong here?

EDIT

I believe I see the issue. Basically, trying to avoid the first error where NServiceBus is resolving multiple Profiles due to the fact it's finding them in both NServiceBus.Core and NServicebus.Hosting.Azure is causing the second error.

This is because in order to load my EndpointConfig type, .NET also needs to load the NServicebus.Hosting.Azure assembly, as it implements AsA_Worker, which lives in NServiceBus.Hosting.Azure.

So be deleting that assembly, I'm preventing it from loading the EndpointConfig.

I'm still unclear as to how to resolve this. I need to get NServiceBus to stop scanning both hosting assemblies. This suggests that christof13's answer is correct, but I'm unable to get NServiceBus to ignore its own assemblies.

2

2 Answers

1
votes

The root cause of this issue is that when NServiceBus enumerates profiles available, it scans all available assemblies and then does a LINQ statement to filter them down by type name.

The problem is that there are two NServiceBus.Production profiles, one defined in the NServiceBus.Host.dll, and one in the NServiceBus.Hosting.Azure assembly. Their type names are identical, and so the LINQ SingleOrDefault fails.

The suggestion to filter the assemblies searched wouldn't work because NServiceBus always loads its own assemblies. The filter only applies to my project's assemblies.

I was able to resolve this by creating a custom profile that doesn't have the same name as any other profile defined in any assemblies in my project. Something like:

public class DualCloudLocalProfile : IProfile {}

public class DualCloudLocalProfileHandler : IHandleProfile<DualCloudLocalProfile>
{
    public void ProfileActivated()
    {
        if (LogManager.LoggerFactory is NullLoggerFactory || LogManager.LoggerFactory == null)
        {
            Configure.Instance.TraceLogger().ConsoleLogger();
        }            
    }
}

public class CloudProfileLoggingHandler : IConfigureLoggingForProfile<DualCloudLocalProfile>
{
    public void Configure(IConfigureThisEndpoint specifier)
    {
        //nothing for now
    }
}

Once this was added into my project, I modified the install command to specify the new profile:

NServiceBus.Host.exe /install /serviceName:"VideoStore.Sales" /displayName:"VideoStore.Sales" /description:"Endpoint for VideoStore.Sales" /endpointConfigurationType:"VideoStore.Sales.EndpointConfig, VideoStore.Sales" /username:".\MySvc" /password:"MyPassword" VideoStore.Sales.DualCloudLocalProfile

This resolved the problem. I can now run the project as a cloud service and a NServiceBus.Host.exe hosted service, as well as deploy via the /install command for the host.

0
votes

You can try to filter the assemblies with one the following methods

Configure.With(string probeDirectory)
Configure.With(params Assembly[] assemblies)
Configure.With(IEnumerable<Type> typesToScan)

By default I think that nservicebus scans all the assemblies in the folder, so if you filter with only the needed assemblies it will prevent from receiving this kind of error

http://docs.particular.net/nservicebus/the-nservicebus-host