Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable Nullable Reference Types #518

Merged
merged 1 commit into from
Jun 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/MediatR/IMediator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public interface IMediator
/// <param name="request">Request object</param>
/// <param name="cancellationToken">Optional cancellation token</param>
/// <returns>A task that represents the send operation. The task result contains the type erased handler response</returns>
Task<object> Send(object request, CancellationToken cancellationToken = default);
Task<object?> Send(object request, CancellationToken cancellationToken = default);

/// <summary>
/// Asynchronously send a notification to multiple handlers
Expand Down
2 changes: 1 addition & 1 deletion src/MediatR/IPipelineBehavior.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace MediatR
/// </summary>
/// <typeparam name="TRequest">Request type</typeparam>
/// <typeparam name="TResponse">Response type</typeparam>
public interface IPipelineBehavior<in TRequest, TResponse>
public interface IPipelineBehavior<in TRequest, TResponse> where TRequest : notnull
{
/// <summary>
/// Pipeline handler. Perform any additional behavior and await the <paramref name="next"/> delegate as necessary
Expand Down
1 change: 1 addition & 0 deletions src/MediatR/Internal/HandlersOrderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace MediatR.Internal
internal static class HandlersOrderer
{
public static IList<object> Prioritize<TRequest>(IList<object> handlers, TRequest request)
where TRequest : notnull
{
if (handlers.Count < 2)
{
Expand Down
6 changes: 3 additions & 3 deletions src/MediatR/Internal/RequestHandlerWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace MediatR.Internal

internal abstract class RequestHandlerBase
{
public abstract Task<object> Handle(object request, CancellationToken cancellationToken,
public abstract Task<object?> Handle(object request, CancellationToken cancellationToken,
ServiceFactory serviceFactory);

protected static THandler GetHandler<THandler>(ServiceFactory factory)
Expand Down Expand Up @@ -42,7 +42,7 @@ public abstract Task<TResponse> Handle(IRequest<TResponse> request, Cancellation
internal class RequestHandlerWrapperImpl<TRequest, TResponse> : RequestHandlerWrapper<TResponse>
where TRequest : IRequest<TResponse>
{
public override Task<object> Handle(object request, CancellationToken cancellationToken,
public override Task<object?> Handle(object request, CancellationToken cancellationToken,
ServiceFactory serviceFactory)
{
return Handle((IRequest<TResponse>)request, cancellationToken, serviceFactory)
Expand All @@ -52,7 +52,7 @@ public override Task<object> Handle(object request, CancellationToken cancellati
{
ExceptionDispatchInfo.Capture(t.Exception.InnerException).Throw();
}
return (object)t.Result;
return (object?)t.Result;
}, cancellationToken);
}

Expand Down
4 changes: 3 additions & 1 deletion src/MediatR/MediatR.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
<PropertyGroup>
<Description>Simple, unambitious mediator implementation in .NET</Description>
<Copyright>Copyright Jimmy Bogard</Copyright>
<TargetFrameworks>net461;netstandard2.0</TargetFrameworks>
<TargetFrameworks>net461;netstandard2.0;netstandard2.1</TargetFrameworks>
<LangVersion>8</LangVersion>
<Nullable>enable</Nullable>
<AssemblyName>MediatR</AssemblyName>
<PackageId>MediatR</PackageId>
<PackageTags>mediator;request;response;queries;commands;notifications</PackageTags>
Expand Down
4 changes: 2 additions & 2 deletions src/MediatR/Mediator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public Task<TResponse> Send<TResponse>(IRequest<TResponse> request, Cancellation
return handler.Handle(request, cancellationToken, _serviceFactory);
}

public Task<object> Send(object request, CancellationToken cancellationToken = default)
public Task<object?> Send(object request, CancellationToken cancellationToken = default)
{
if (request == null)
{
Expand All @@ -58,7 +58,7 @@ public Task<object> Send(object request, CancellationToken cancellationToken = d
throw new ArgumentException($"{nameof(request)} does not implement ${nameof(IRequest)}");
}

var responseType = requestInterfaceType.GetGenericArguments()[0];
var responseType = requestInterfaceType!.GetGenericArguments()[0];
var handler = _requestHandlers.GetOrAdd(requestType,
t => Activator.CreateInstance(typeof(RequestHandlerWrapperImpl<,>).MakeGenericType(requestType, responseType)));

Expand Down
140 changes: 140 additions & 0 deletions src/MediatR/NullableAttributes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#pragma warning disable MA0048 // File name must match type name
#define INTERNAL_NULLABLE_ATTRIBUTES
#if NETSTANDARD2_0 || NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 || NET45 || NET451 || NET452 || NET6 || NET461 || NET462 || NET47 || NET471 || NET472 || NET48

// https://github.com/dotnet/corefx/blob/48363ac826ccf66fbe31a5dcb1dc2aab9a7dd768/src/Common/src/CoreLib/System/Diagnostics/CodeAnalysis/NullableAttributes.cs

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace System.Diagnostics.CodeAnalysis
{
/// <summary>Specifies that null is allowed as an input even if the corresponding type disallows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
internal
#else
public
#endif
sealed class AllowNullAttribute : Attribute
{ }

/// <summary>Specifies that null is disallowed as an input even if the corresponding type allows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
internal
#else
public
#endif
sealed class DisallowNullAttribute : Attribute
{ }

/// <summary>Specifies that an output may be null even if the corresponding type disallows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
internal
#else
public
#endif
sealed class MaybeNullAttribute : Attribute
{ }

/// <summary>Specifies that an output will not be null even if the corresponding type allows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
internal
#else
public
#endif
sealed class NotNullAttribute : Attribute
{ }

/// <summary>Specifies that when a method returns <see cref="ReturnValue"/>, the parameter may be null even if the corresponding type disallows it.</summary>
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
internal
#else
public
#endif
sealed class MaybeNullWhenAttribute : Attribute
{
/// <summary>Initializes the attribute with the specified return value condition.</summary>
/// <param name="returnValue">
/// The return value condition. If the method returns this value, the associated parameter may be null.
/// </param>
public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;

/// <summary>Gets the return value condition.</summary>
public bool ReturnValue { get; }
}

/// <summary>Specifies that when a method returns <see cref="ReturnValue"/>, the parameter will not be null even if the corresponding type allows it.</summary>
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
internal
#else
public
#endif
sealed class NotNullWhenAttribute : Attribute
{
/// <summary>Initializes the attribute with the specified return value condition.</summary>
/// <param name="returnValue">
/// The return value condition. If the method returns this value, the associated parameter will not be null.
/// </param>
public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;

/// <summary>Gets the return value condition.</summary>
public bool ReturnValue { get; }
}

/// <summary>Specifies that the output will be non-null if the named parameter is non-null.</summary>
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
internal
#else
public
#endif
sealed class NotNullIfNotNullAttribute : Attribute
{
/// <summary>Initializes the attribute with the associated parameter name.</summary>
/// <param name="parameterName">
/// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null.
/// </param>
public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName;

/// <summary>Gets the associated parameter name.</summary>
public string ParameterName { get; }
}

/// <summary>Applied to a method that will never return under any circumstance.</summary>
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
internal
#else
public
#endif
sealed class DoesNotReturnAttribute : Attribute
{ }

/// <summary>Specifies that the method will not return if the associated Boolean parameter is passed the specified value.</summary>
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
#if INTERNAL_NULLABLE_ATTRIBUTES
internal
#else
public
#endif
sealed class DoesNotReturnIfAttribute : Attribute
{
/// <summary>Initializes the attribute with the specified parameter value.</summary>
/// <param name="parameterValue">
/// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to
/// the associated parameter matches this value.
/// </param>
public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue;

/// <summary>Gets the condition parameter value.</summary>
public bool ParameterValue { get; }
}
}
#endif
4 changes: 4 additions & 0 deletions src/MediatR/Pipeline/IRequestExceptionAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace MediatR.Pipeline
/// <typeparam name="TRequest">Request type</typeparam>
/// <typeparam name="TException">Exception type</typeparam>
public interface IRequestExceptionAction<in TRequest, in TException>
where TRequest : notnull
where TException : Exception
{
/// <summary>
Expand All @@ -30,6 +31,7 @@ public interface IRequestExceptionAction<in TRequest, in TException>
/// </summary>
/// <typeparam name="TRequest">The type of failed request</typeparam>
public interface IRequestExceptionAction<in TRequest> : IRequestExceptionAction<TRequest, Exception>
where TRequest : notnull
{
}

Expand Down Expand Up @@ -60,6 +62,7 @@ async Task IRequestExceptionAction<TRequest, Exception>.Execute(TRequest request
/// <typeparam name="TRequest">Request type</typeparam>
/// <typeparam name="TException">Exception type</typeparam>
public abstract class RequestExceptionAction<TRequest, TException> : IRequestExceptionAction<TRequest, TException>
where TRequest : notnull
where TException : Exception
{
Task IRequestExceptionAction<TRequest, TException>.Execute(TRequest request, TException exception, CancellationToken cancellationToken)
Expand All @@ -81,6 +84,7 @@ Task IRequestExceptionAction<TRequest, TException>.Execute(TRequest request, TEx
/// </summary>
/// <typeparam name="TRequest">Request type</typeparam>
public abstract class RequestExceptionAction<TRequest> : IRequestExceptionAction<TRequest>
where TRequest : notnull
{
Task IRequestExceptionAction<TRequest, Exception>.Execute(TRequest request, Exception exception, CancellationToken cancellationToken)
{
Expand Down
5 changes: 5 additions & 0 deletions src/MediatR/Pipeline/IRequestExceptionHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace MediatR.Pipeline
/// <typeparam name="TResponse">Response type</typeparam>
/// <typeparam name="TException">Exception type</typeparam>
public interface IRequestExceptionHandler<in TRequest, TResponse, TException>
where TRequest : notnull
where TException : Exception
{
/// <summary>
Expand All @@ -30,6 +31,7 @@ public interface IRequestExceptionHandler<in TRequest, TResponse, TException>
/// <typeparam name="TRequest">Request type</typeparam>
/// <typeparam name="TResponse">Response type</typeparam>
public interface IRequestExceptionHandler<in TRequest, TResponse> : IRequestExceptionHandler<TRequest, TResponse, Exception>
where TRequest : notnull
{
}

Expand All @@ -39,6 +41,7 @@ public interface IRequestExceptionHandler<in TRequest, TResponse> : IRequestExce
/// <typeparam name="TRequest">Request type</typeparam>
/// <typeparam name="TResponse">Response type</typeparam>
public abstract class AsyncRequestExceptionHandler<TRequest, TResponse> : IRequestExceptionHandler<TRequest, TResponse>
where TRequest : notnull
{
async Task IRequestExceptionHandler<TRequest, TResponse, Exception>.Handle(TRequest request, Exception exception, RequestExceptionHandlerState<TResponse> state, CancellationToken cancellationToken)
{
Expand All @@ -62,6 +65,7 @@ async Task IRequestExceptionHandler<TRequest, TResponse, Exception>.Handle(TRequ
/// <typeparam name="TResponse">Response type</typeparam>
/// <typeparam name="TException">Exception type</typeparam>
public abstract class RequestExceptionHandler<TRequest, TResponse, TException> : IRequestExceptionHandler<TRequest, TResponse, TException>
where TRequest : notnull
where TException : Exception
{
Task IRequestExceptionHandler<TRequest, TResponse, TException>.Handle(TRequest request, TException exception, RequestExceptionHandlerState<TResponse> state, CancellationToken cancellationToken)
Expand All @@ -85,6 +89,7 @@ Task IRequestExceptionHandler<TRequest, TResponse, TException>.Handle(TRequest r
/// <typeparam name="TRequest">Request type</typeparam>
/// <typeparam name="TResponse">Response type</typeparam>
public abstract class RequestExceptionHandler<TRequest, TResponse> : IRequestExceptionHandler<TRequest, TResponse>
where TRequest : notnull
{
Task IRequestExceptionHandler<TRequest, TResponse, Exception>.Handle(TRequest request, Exception exception, RequestExceptionHandlerState<TResponse> state, CancellationToken cancellationToken)
{
Expand Down
2 changes: 1 addition & 1 deletion src/MediatR/Pipeline/IRequestPostProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace MediatR.Pipeline
/// </summary>
/// <typeparam name="TRequest">Request type</typeparam>
/// <typeparam name="TResponse">Response type</typeparam>
public interface IRequestPostProcessor<in TRequest, in TResponse>
public interface IRequestPostProcessor<in TRequest, in TResponse> where TRequest : notnull
{
/// <summary>
/// Process method executes after the Handle method on your handler
Expand Down
2 changes: 1 addition & 1 deletion src/MediatR/Pipeline/IRequestPreProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace MediatR.Pipeline
/// Defined a request pre-processor for a handler
/// </summary>
/// <typeparam name="TRequest">Request type</typeparam>
public interface IRequestPreProcessor<in TRequest>
public interface IRequestPreProcessor<in TRequest> where TRequest : notnull
{
/// <summary>
/// Process method executes before calling the Handle method on your handler
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace MediatR.Pipeline
/// <typeparam name="TRequest">Request type</typeparam>
/// <typeparam name="TResponse">Response type</typeparam>
public class RequestExceptionActionProcessorBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : notnull
{
private readonly ServiceFactory _serviceFactory;

Expand Down
7 changes: 5 additions & 2 deletions src/MediatR/Pipeline/RequestExceptionHandlerState.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Diagnostics.CodeAnalysis;

namespace MediatR.Pipeline
{
/// <summary>
Expand All @@ -14,13 +16,14 @@ public class RequestExceptionHandlerState<TResponse>
/// <summary>
/// The response that is returned if <see cref="Handled"/> is <code>true</code>.
/// </summary>
public TResponse Response { get; private set; }
[MaybeNull]
public TResponse Response { get; private set; } = default!;

/// <summary>
/// Call to indicate whether the current exception should be considered handled and the specified response should be returned.
/// </summary>
/// <param name="response">Set the response that will be returned.</param>
public void SetHandled(TResponse response = default)
public void SetHandled(TResponse response)
{
Handled = true;
Response = response;
Expand Down
5 changes: 3 additions & 2 deletions src/MediatR/Pipeline/RequestExceptionProcessorBehavior.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace MediatR.Pipeline
/// <typeparam name="TRequest">Request type</typeparam>
/// <typeparam name="TResponse">Response type</typeparam>
public class RequestExceptionProcessorBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : notnull
{
private readonly ServiceFactory _serviceFactory;

Expand All @@ -30,7 +31,7 @@ public async Task<TResponse> Handle(TRequest request, CancellationToken cancella
catch (Exception exception)
{
var state = new RequestExceptionHandlerState<TResponse>();
Type exceptionType = null;
Type? exceptionType = null;

while (!state.Handled && exceptionType != typeof(Exception))
{
Expand All @@ -53,7 +54,7 @@ public async Task<TResponse> Handle(TRequest request, CancellationToken cancella
throw;
}

return state.Response;
return state.Response!; //cannot be null if Handled
}
}

Expand Down
1 change: 1 addition & 0 deletions src/MediatR/Pipeline/RequestPostProcessorBehavior.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace MediatR.Pipeline
/// <typeparam name="TRequest">Request type</typeparam>
/// <typeparam name="TResponse">Response type</typeparam>
public class RequestPostProcessorBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : notnull
{
private readonly IEnumerable<IRequestPostProcessor<TRequest, TResponse>> _postProcessors;

Expand Down
1 change: 1 addition & 0 deletions src/MediatR/Pipeline/RequestPreProcessorBehavior.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace MediatR.Pipeline
/// <typeparam name="TRequest"></typeparam>
/// <typeparam name="TResponse"></typeparam>
public class RequestPreProcessorBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : notnull
{
private readonly IEnumerable<IRequestPreProcessor<TRequest>> _preProcessors;

Expand Down
2 changes: 1 addition & 1 deletion test/MediatR.Tests/MediatR.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net461;netcoreapp2.1</TargetFrameworks>
<TargetFrameworks>net461;netcoreapp2.1;netcoreapp3.1</TargetFrameworks>
<AssemblyName>MediatR.Tests</AssemblyName>
<PackageId>MediatR.Tests</PackageId>
</PropertyGroup>
Expand Down