Upgrade Version 4 to 5

Component: NServiceBus

Upgrading a major dependency like NServiceBus requires careful planning, see the general recommendations article to learn more about the optimal upgrade process.

Move to .NET 4.5

The minimum .NET version for NServiceBus version 5 is .NET 4.5.

**This means consumers must update all projects that reference NServiceBus to .NET 4.5 before updating to NServiceBus version 5. **

Note that smaller changes are easier to verify; as such it is recommended to update to .NET 4.5 and perform a full migration to production before updating to NServiceBus version 5.

https://github.com/Particular/NServiceBus/issues/2078

Interfaces assembly and NuGet deprecated

The NServiceBus interfaces dll was created to allow a smaller dependency when creating a messages assembly. With the introduction of message conventions that is no longer required. As such NServiceBus.dll has been merged into NServiceBus.Core.dll. Also the NServiceBus.Interfaces NuGet has been deprecated.

https://github.com/Particular/NServiceBus/issues/2113

If using NuGet packages

No extra work should be required. A normal NuGet update should result in no NServiceBus.Interfaces NuGet being used and version 5 of the NServiceBus NuGet being used. This is achieved through some NuGet sleight of hand.

If for some reason this sleight of hand fails, then manually remove usages of the NServiceBus.Interfaces NuGet and ensure that version 5 of the NServiceBus NuGet is used.

If using manual references

  • Remove all references to NServiceBus.dll
  • Update all references of NServiceBus.Core.dll to version 5

Reflection

Reflection calls that makes assumptions on the assembly name of NServiceBus.dll must be updated and re-tested.

Binding redirects

Any binding redirects pointing to NServiceBus.dll should be removed.

Use of interfaces and attributes from NServiceBus.Interfaces.dll

The following have been moved into NServiceBus.Core.dll (part of the NServiceBus NuGet)

  • IMessage
  • IEvent
  • ICommand
  • ExpressAttribute
  • DataBusProperty<T> and IDataBusProperty
  • TimeToBeReceivedAttribute
  • WireEncryptedString
  • ExpressAttribute

If these markers are still required, they can be referenced via NServiceBus.Core.dll.

If "no reference" is required, the equivalent conventions can be used instead.

// --- NServiceBus 4.x ---
// configure.DefiningCommandsAs(...);

// --- NServiceBus 5.x ---
var conventions = busConfiguration.Conventions();
conventions.DefiningCommandsAs(...);

Configure API

The configuration API has been modified to fix several bugs related to method invocation order. The new API takes an Action that configures a ConfigurationBuilder.

// --- NServiceBus 4.x ---
// var configure = Configure.With(AllAssemblies.Except("NotThis.dll"));
// configure.DefaultBuilder();
// configure.DefineEndpointName("MyEndpointName");
// configure.DefiningEventsAs(...);

// --- NServiceBus 5.x ---
busConfiguration.AssembliesToScan(AllAssemblies.Except("NotThis.dll"));
var conventions = busConfiguration.Conventions();
conventions.DefiningEventsAs(...);
busConfiguration.EndpointName("MyEndpointName");

https://github.com/Particular/NServiceBus/issues/356

Transport definitions for UseTransport have been renamed

When using UseTransport<T> the transport definition types have been suffixed with Transport.

// --- NServiceBus 4.x ---
// Choose one of the following:
// configure.UseTransport<Msmq>();
// configure.UseTransport<RabbitMQ>();
// configure.UseTransport<SqlServer>();
// configure.UseTransport<AzureStorageQueue>();
// configure.UseTransport<AzureServiceBus>();

// --- NServiceBus 5.x ---
// Choose one of the following:
busConfiguration.UseTransport<MsmqTransport>();
busConfiguration.UseTransport<RabbitMQTransport>();
busConfiguration.UseTransport<SqlServerTransport>();
busConfiguration.UseTransport<AzureStorageQueueTransport>();
busConfiguration.UseTransport<AzureServiceBusTransport>();

Logging

NServiceBus now has sensible defaults for logging built in.

NLog and Log4net integration have been extracted to external NuGet packages. For more information see Logging in NServiceBus

Obsolete sending and publishing batches of messages together in a single call

Batch sending of messages has been removed in NServiceBus version 5.

Previous code such as:

bus.Send(new Message1(), new Message2(), new Message3());

should be changed to:

bus.Send(new Message1());
bus.Send(new Message2());
bus.Send(new Message3());

The full list of replacements is below

Old MethodReplacement Method
Publish(T[] messages);Publish(T message);
SendLocal(object[] messages);SendLocal(object message);
Send(object[] messages);Send(object message);
Send(string destination, object[] messages);Send(string destination, object message);
Send(Address address, object[] messages);Send(Address address, object message);
Send(string destination, string correlationId, object[] messages);Send(string destination, string correlationId, object message);
SendToSites(IEnumerable siteKeys, object[] messages);SendToSites(IEnumerable siteKeys, object message);
Defer(TimeSpan delay, object[] messages);Defer(TimeSpan delay, object message);
Defer(DateTime processAt, object[] messages);Defer(DateTime processAt, object message);
Reply(object[] messages);Reply(object message);

https://github.com/Particular/NServiceBus/issues/1346

InMemory transport removal

Due to significant confusion about the usage of this API, the InMemory transport has been removed in NServiceBus version 5. People using this API should either

  • Move to Publish or
  • Call the required methods explicitly

https://github.com/Particular/NServiceBus/issues/2084

As an alternative, consider using an implemeantion of the mediator pattern or the event aggregator pattern:

Remove IMessageModule in favor of IManageUnitsOfWork

In NServiceBus version 4, an improved abstraction (IManageUnitsOfWork) was introduced and IMessageModule was deprecated.

In version 5, this change is being completed with the remove of IMessageModule.

Uses of IMessageModule should be replaced with IManageUnitsOfWork.

https://github.com/Particular/NServiceBus/issues/2191

Remove Bus.CreateInstance

In NServiceBus version 4, messages could be created with two approaches. In version 5, the same can be achieved using only one of those approaches.

// --- NServiceBus 4.x ---
// var message = Bus.CreateInstance<MyInterfaceMessage>(
//     action: interfaceMessage =>
//     {
//         interfaceMessage.OrderNumber = 1234;
//     });
// Bus.Publish(message);
// or:
// Bus.Publish<MyInterfaceMessage>(
//     messageConstructor: interfaceMessage =>
//     {
//         interfaceMessage.OrderNumber = 1234;
//     });

// --- NServiceBus 5.x ---
Bus.Publish<MyInterfaceMessage>(o =>
{
    o.OrderNumber = 1234;
});

How to publish an interface event using reflection

In some contexts it may not be possible to invoke the generic IBus.Publish(), for example, when receiving a message via a non-typed webservice. In these cases, use IMessageCreator to construct the instance.

IMessageCreator can be accessed via the container. The recommended way to do this is to pass it into the constructor of the class (eg Handler, Saga, IWantToRunWhenBusStartsAndStops, etc).

// This type would be derived from some other runtime information
var messageType = typeof(MyInterfaceMessage);

var instance = messageCreator.CreateInstance(messageType);

// use reflection to set properties on the constructed instance

Bus.Publish(instance);

https://github.com/Particular/NServiceBus/issues/2244

Pull gateway out of the core

The gateway has been moved to a separate NServiceBus.Gateway NuGet.

https://github.com/Particular/NServiceBus/issues/1603

Update to RavenDB 2.5

NServiceBus version 5 has been updated to use Raven 2.5.

The standard approach for updating RavenDB is to update the server first and then update the client. The benefit of this approach is that the risk is split and each part can be verified individually.

While this approach can be taken when updating to RavenDB 2.5, there are known issues when talking from a Raven 2.0 client to a RavenDB 2.5 server. However this issue only occurs in rare circumstances, so it is the decision of the consumer to decide which approach is best for their scenario.

See using RavenDb in NServiceBus for additional details on upgrading from Raven 2.0 to 2.5.

RavenDB is split from the core

To allow a more regular cadence of RavenDB updates and isolate those changes, the RavenDB integration for NServiceBus has been extracted to its own NuGet.

To move over to this NuGet after updating to version 5:

https://github.com/Particular/NServiceBus/issues/1605

1. Install the NServiceBus.RavenDB NuGet package

Install-Package NServiceBus.RavenDB

2. Use the new configuration API

// --- NServiceBus 4.x ---
// configure.RavenPersistence("http://localhost:8080", "MyDatabase");

// --- NServiceBus 5.x ---
var documentStore = new DocumentStore
{
    Url = "http://localhost:8080",
    DefaultDatabase = "MyDatabase",
};
documentStore.Initialize();

var busConfiguration = new BusConfiguration();

var persistence = busConfiguration.UsePersistence<RavenDBPersistence>();
persistence.SetDefaultDocumentStore(documentStore);

Pull the distributor out of the core

The distributor feature has been extracted into its own NuGet. More details on this change: https://github.com/Particular/NServiceBus/issues/1604

To move over to this NuGet after updating to version 5:

  1. Install the NServiceBus.Distributor.MSMQ NuGet package
  2. Replace old profile names with their new counterparts:
Old ProfileNew Profile
NServiceBus.DistributorNServiceBus.MsmqDistributor
NServiceBus.MasterNServiceBus.MsmqMaster
NServiceBus.WorkerNServiceBus.MsmqWorker

Obsolete the IWantToRunBeforeConfiguration API

The IWantToRunBeforeConfiguration API is no longer needed and is obsolete. The replacement is to use either INeedInitalization or use a feature where a Default(s=>..) can be setup in the constructor of the feature.

https://github.com/Particular/NServiceBus/pull/2180

Remove UnicastBus.Start(Action)

The startupAction parameter of UnicastBus.Start executed the action immediately before the start of the bus. This provided no real value since a consumer can execute the action prior to calling Start.

// --- NServiceBus 4.x ---
// configure.UnicastBus();
// var startableBus = configure.CreateBus();
// startableBus.Start(
//     startupAction: () =>
//     {
//         MyCustomAction();
//     });

// --- NServiceBus 5.x ---
var startableBus = Bus.Create(new BusConfiguration());
MyCustomAction();
startableBus.Start();

https://github.com/Particular/NServiceBus/issues/2168

Remove ForInstallationOn, Windows and IEnvironment

This API has been simplified.

// --- NServiceBus 4.x ---
// var configure = Configure.With();
// configure.UnicastBus();
// var startableBus = configure.CreateBus();
// startableBus.Start(
//     startupAction: () =>
//     {
//         configure.ForInstallationOn<Windows>().Install();
//     });

// --- NServiceBus 5.x ---
busConfiguration.EnableInstallers();
// this will run the installers
Bus.Create(busConfiguration);

https://github.com/Particular/NServiceBus/issues/2167

Make Scheduler API instance-based

public class ScheduleMyTasks :
    IWantToRunWhenBusStartsAndStops
{
    IBus bus;
    Schedule schedule;

    public ScheduleMyTasks(IBus bus, Schedule schedule)
    {
        this.bus = bus;
        this.schedule = schedule;
    }

    public void Start()
    {
        // --- NServiceBus 4.x ---
        // Schedule.Every(TimeSpan.FromMinutes(5))
        //     .Action(() =>
        //     {
        //         var myMessage = new MyMessage();
        //         bus.SendLocal(myMessage);
        //     });

        // --- NServiceBus 5.x ---
        schedule.Every(
            timeSpan: TimeSpan.FromMinutes(5),
            task: () =>
            {
                var message = new MyMessage();
                bus.SendLocal(message);
            });
    }

    ...
}

https://github.com/Particular/NServiceBus/pull/2192

Make ConfigureHowToFindSaga abstract

Many bugs are caused by this method not being overridden. As such it is abstract in NServiceBus version 5.

public class MySaga :
    Saga<MySagaData>,
    IAmStartedByMessages<Message1>,
    IHandleMessages<Message2>
{
    // --- NServiceBus 4.x ---
    // public override void ConfigureHowToFindSaga()
    // {
    //     ConfigureMapping<Message2>(message => message.SomeId)
    //         .ToSaga(sagaData => sagaData.SomeId);
    // }

    // --- NServiceBus 5.x ---
    protected override void ConfigureHowToFindSaga(SagaPropertyMapper<MySagaData> mapper)
    {
        mapper.ConfigureMapping<Message2>(message => message.SomeId)
            .ToSaga(sagaData => sagaData.SomeId);
    }

https://github.com/Particular/NServiceBus/issues/2137

Standardize extension methods to configure persisters

In the interest of keeping the API consistent, a standard approach for choosing and configuring persistence has been applied.

// --- NServiceBus 4.x ---
// Configure to use InMemory
// configure.InMemorySagaPersister();
// configure.UseInMemoryTimeoutPersister();
// configure.InMemorySubscriptionStorage();
// configure.RunGatewayWithInMemoryPersistence();
// configure.UseInMemoryGatewayDeduplication();

// --- NServiceBus 5.x ---
// Configure to use InMemory for all persistence types
busConfiguration.UsePersistence<InMemoryPersistence>();
// Or configure to use InMemory for specific persistence types
busConfiguration.UsePersistence<InMemoryPersistence>()
    .For(
        Storage.Sagas,
        Storage.Subscriptions);

// --- NServiceBus 4.x ---
// Configure to use NHibernate
// configure.UseNHibernateSagaPersister();
// configure.UseNHibernateTimeoutPersister();
// configure.UseNHibernateSubscriptionPersister();
// configure.UseNHibernateGatewayPersister();
// configure.UseNHibernateGatewayDeduplication();

// --- NServiceBus 5.x ---
// Configure to use NHibernate for all persistence types
busConfiguration.UsePersistence<NHibernatePersistence>();
// Or configure to use NHibernate for specific persistence types
busConfiguration.UsePersistence<NHibernatePersistence>()
    .For(
        Storage.Sagas,
        Storage.Subscriptions);

// --- NServiceBus 4.x ---
// Configure to use RavenDB
// configure.RavenSagaPersister();
// configure.UseRavenTimeoutPersister();
// configure.RavenSubscriptionStorage();
// configure.RunGatewayWithRavenPersistence();
// configure.UseRavenGatewayDeduplication();

// --- NServiceBus 5.x ---
// Configure to use RavenDB for all persistence types
busConfiguration.UsePersistence<RavenDBPersistence>();
// Or configure to use RavenDB for specific persistence types
busConfiguration.UsePersistence<RavenDBPersistence>()
    .For(
        Storage.Sagas,
        Storage.Subscriptions);

https://github.com/Particular/NServiceBus/issues/2102

Obsolete the SerializationSettings properties WrapSingleMessages and DontWrapSingleMessages

In NServiceBus version 5, multi-message sends is removed and wrapping messages is no longer required. Usages of these settings should be removed.

https://github.com/Particular/NServiceBus/issues/2104

Change IMessageSerializer.Serialize to take a single message

In NServiceBus version 5, multi-message sends has been removed and serialization of an array of messages is no longer required.

Implementations of IMessageSerializer should be updated to take a single object.

Usages of IMessageSerializer should no longer pass in an array of objects.

https://github.com/Particular/NServiceBus/issues/2105

Replace ISaga with non-generic base class Saga

Use abstract classes Saga<T> (for generic access to the saga data) or Saga (for non-generic access to the saga data) instead.

This should only effect scenarios that are leveraging more advanced NServiceBus extensions. This is because in version 4, it was not possible to use ISaga to create a saga.

https://github.com/Particular/NServiceBus/issues/2095

Remove Bus.Subscribe predicate overloads

Instead create a handler that does this filtering logic and then optionally calls DoNotContinueDispatchingCurrentMessageToHandlers. This handler should be ordered to run before other handlers.

https://github.com/Particular/NServiceBus/issues/2088

Remove this.Bus() extension method for IHandleMessages implementations

In NServiceBus version 4, there was an extension method on IHandleMessages that supplied a static instance of the IBus. This allowed consumers to write this.Bus().Reply(response);.

While this was convenient, the tax of allowing static access to an instance of the IBus was too high. As such this extension has been removed.

The replacement is to inject an instance of IBus as a constructor parameter and then assign it to a field for later use.

// --- NServiceBus 4.x ---
// public class MyHandler : IHandleMessages<MyMessage>
// {
//     public void Handle(MyMessage message)
//     {
//         var otherMessage = new OtherMessage();
//         this.Bus().Reply(otherMessage);
//     }
// }

// --- NServiceBus 5.x ---
public class MyHandler : IHandleMessages<MyMessage>
{
    IBus bus;

    public MyHandler(IBus bus)
    {
        this.bus = bus;
    }

    public void Handle(MyMessage message)
    {
        var otherMessage = new OtherMessage();
        bus.Reply(otherMessage);
    }
}

https://github.com/Particular/NServiceBus/issues/2082

Remove .RunCustomAction()

// --- NServiceBus 4.x ---
// var configure = Configure.With();
// configure.UnicastBus();
// configure.RunCustomAction(MyCustomAction);
// var startableBus = configure.CreateBus();
// startableBus.Start();

// --- NServiceBus 5.x ---
var startableBus = Bus.Create(new BusConfiguration());
MyCustomAction();
startableBus.Start();

https://github.com/Particular/NServiceBus/issues/1366

Moved DefineCriticalErrorAction to be a ConfigurationBuilder extension

// --- NServiceBus 4.x ---
// var configure = Configure.With();
// configure.DefineCriticalErrorAction(
//     (message, exception) =>
//     {
//         ...
//     });

// --- NServiceBus 5.x ---
busConfiguration.DefineCriticalErrorAction(
    (message, exception) =>
    {
        ...
    });    

https://github.com/Particular/NServiceBus/issues/2254

Moved FileShareDataBus to a ConfigurationBuilder extension

// --- NServiceBus 4.x ---
// var configure = Configure.With();
// configure.FileShareDataBus(databusPath);

// --- NServiceBus 5.x ---
var dataBus = busConfiguration.UseDataBus<FileShareDataBus>();
dataBus.BasePath(databusPath);

https://github.com/Particular/NServiceBus/issues/2257

Moved PurgeOnStartup to be a ConfigurationBuilder extension

// --- NServiceBus 4.x ---
// var configure = Configure.With();
// configure.PurgeOnStartup(true);

// --- NServiceBus 5.x ---
busConfiguration.PurgeOnStartup(true);

https://github.com/Particular/NServiceBus/issues/2257

Moved License configuration API to be a ConfigurationBuilder extension

// --- NServiceBus 4.x ---
// configure.LicensePath("PathToLicense");

// --- NServiceBus 5.x ---
busConfiguration.LicensePath("PathToLicense");
            
// --- NServiceBus 4.x ---
// configure.License("YourCustomLicenseText");

// --- NServiceBus 5.x ---
busConfiguration.License("YourCustomLicenseText");

https://github.com/Particular/NServiceBus/issues/2278

Moved EncryptionService to be a ConfigurationBuilder extension

RijndaelEncryption

// --- NServiceBus 4.x ---
// var configure = Configure.With();
// configure.RijndaelEncryptionService();

// --- NServiceBus 5.x ---
busConfiguration.RijndaelEncryptionService();

https://github.com/Particular/NServiceBus/issues/2265

Custom IEncryptionService

// where EncryptionService implements IEncryptionService
var configure = Configure.With();
var components = configure.Configurer;
components.RegisterSingleton<IEncryptionService>(new EncryptionService());

https://github.com/Particular/NServiceBus/pull/2270

Moved Transaction config to ConfigurationBuilder

// --- NServiceBus 4.x ---
// Enable
// Configure.Transactions.Enable();
// Disable
// Configure.Transactions.Disable();

// --- NServiceBus 5.x ---
// Enable
busConfiguration.Transactions().Enable();
// Disable
busConfiguration.Transactions().Disable();

https://github.com/Particular/NServiceBus/pull/2283

Moved static endpoint config to ConfigurationBuilder

// --- NServiceBus 4.x ---
// var endpoint = Configure.Endpoint;
// endpoint.AsSendOnly();

// --- NServiceBus 5.x ---
Bus.CreateSendOnly(busConfiguration);
// --- NServiceBus 4.x ---
// var endpoint = Configure.Endpoint;
// endpoint.Advanced(settings => settings.DisableDurableMessages());
// endpoint.Advanced(settings => settings.EnableDurableMessages());

// --- NServiceBus 5.x ---
busConfiguration.DisableDurableMessages();
busConfiguration.EnableDurableMessages();
// --- NServiceBus 4.x ---
// var endpoint = Configure.Endpoint;
// endpoint.AsVolatile();

// --- NServiceBus 5.x ---
var transactions = busConfiguration.Transactions();
transactions.Disable();
busConfiguration.DisableDurableMessages();
busConfiguration.UsePersistence<InMemoryPersistence>();

Moved performance monitoring and SLA to ConfigurationBuilder

// --- NServiceBus 4.x ---
// var configure = Configure.With();
// configure.EnablePerformanceCounters();
// configure.SetEndpointSLA(TimeSpan.FromMinutes(3));

// --- NServiceBus 5.x ---            
busConfiguration.EnableSLAPerformanceCounter();
// or
busConfiguration.EnableSLAPerformanceCounter(TimeSpan.FromMinutes(3));

https://github.com/Particular/NServiceBus/issues/2284

Configure.Instance deprecated

Configure.Instance has been deprecated. The alternatives for configuring an instance now include:

At configuration time

In NServiceBus version 4, some configuration APIs require access to an instance of Configure via Configure.Instance. For example IWantToRunBeforeConfigurationIsFinalized and IWantToRunWhenConfigurationIsComplete. These interfaces have been modified in version 5 so an instance of Configure is passed in.

To resolve IBuilder

In version 4, it is possible to use Configure.Instance to get access to an instance of IBuilder. Instead of using IBuilder to access other runtime instances that exist in dependency injection, resolve them via dependency injection. An alternative to using IBuilder is to configure NServiceBus to use a custom dependency injection instance. After starting the bus, this instance can be used to resolve instances. Each of the dependency injection integrations have an associated sample that show how to pass in a custom instance.

To statically resolve IBus

One common use of Configure.Instance in NServiceBus version 4 was to access an instance of IBus in the static context, i.e. with Configure.Instance.Builder.Build<IBus>(). It is recommended to avoid using the static context and instead construct the instances via the container. For example, to inject IBus, add a constructor parameter of the type IBus and store that in a field for when it is needed.

If the class cannot be constructed though dependency injection store the instance of IBus returned from Bus.Create(busConfiguration).Start() in a static variable and access it in the global context.

Static resolution via IWantToRunWhenBusStartsAndStops

Access to IBuilder and IBus via Bus.Create(busConfiguration).Start() is no longer available when using the NServiceBus host or plugging into NServiceBus in a generic library. If this functionality is necessary, capture both IBuilder and IBus via IWantToRunWhenBusStartsAndStops and storing them statically.

public class BusInstance :
    IWantToRunWhenBusStartsAndStops
{
    public BusInstance(IBus bus, IBuilder builder)
    {
        Bus = bus;
        Builder = builder;
    }

    public static IBuilder Builder;
    public static IBus Bus;
    
    ...
}

Note that this approach should be considered only when it's not possible to resolve the components via dependency injection.

Moved SendOnly mode to ConfigurationBuilder

// --- NServiceBus 4.x ---
// configure.UnicastBus();
// var bus = configure.SendOnly();

// --- NServiceBus 5.x ---
var sendOnlyBus = Bus.CreateSendOnly(busConfiguration);

https://github.com/Particular/NServiceBus/pull/2295

Removed EndpointName to ConfigurationBuilder and removed Func overload

// --- NServiceBus 4.x ---
// configure.DefineEndpointName("MyEndpoint");

// --- NServiceBus 5.x ---
busConfiguration.EndpointName("MyEndpoint");

https://github.com/Particular/NServiceBus/pull/2275

Moved DoNotCreateQueues to a ConfigurationBuilder extension

// --- NServiceBus 4.x ---
// configure.DoNotCreateQueues();

// --- NServiceBus 5.x ---
busConfiguration.DoNotCreateQueues();

https://github.com/Particular/NServiceBus/issues/2263

Remove RunHandlersUnderIncomingPrincipal

The RunHandlersUnderIncomingPrincipal method has been removed.

This API was frequently assumed to be a security feature. In fact, this API exposes the username from the message sending code to executing handlers. It does this by setting Thread.CurrentPrincipal to a fake principal containing the username of the user who sent the message and extracts this username from the message headers. The handler code is free use Thread.CurrentPrincipal or to ignore. It does not add any security or perform any true impersonation.

To re-enable this feature, a message mutator can be used .

public class PrincipalMutator :
    IMutateIncomingTransportMessages
{
    public void MutateIncoming(TransportMessage message)
    {
        var headers = message.Headers;
        var windowsIdentityName = headers[Headers.WindowsIdentityName];
        var identity = new GenericIdentity(windowsIdentityName);
        var principal = new GenericPrincipal(identity, new string[0]);
        Thread.CurrentPrincipal = principal;
    }
}

Another option is to use a custom header as illustrated in the Appending username using headers sample.

INeedToInstallInfrastructure

The interface INeedToInstallInfrastructure<T> has been removed. Use PowerShell commandlets as an alternative.


Last modified