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

.NET 6.0 support + DI compatibility #222

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
29 changes: 22 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,41 @@ Reference the `Mandrill.dll` library in your project or download from NuGet.
[https://mandrillapp.com/api/docs/](https://mandrillapp.com/api/docs/)

#### Example
```csharp
/// Startup.cs
services.AddMandrillApi("xxxxx-xxxx-xxxx-xxxx");

MandrillApi api = new MandrillApi("xxxxx-xxxx-xxxx-xxxx");
UserInfo info = await api.UserInfo();
Console.WriteLine(info.Reputation);
/// In code
public class MyService {

private IMandrillApi _mandrill;

public MyService(IMandrillApi mandrill){
_mandrill = mandrill;
}

public Task<int> GetReputation(){
var info = await _mandrill.UserInfo();
return info.Reputation;
}
}
```

All endpoints are covered by integration tests and can be used as a reference.

## Necessary prerequisites

### Net Core / NetStandard
### Net Core / NetStandard / NET 6.0

Mandrill.net now supports these platforms.

### .NET 4.5
### .NET 4.7.1

This wrapper uses async and await, hence the dependency on .NET 4.5.
Support for .NET 4.7.1 has been dropped. The last build for .NET 4.7.1 is the NuGet version `3.1.0`. The code can be found on tag [.net-4.7.1](https://github.com/shawnmclean/Mandrill-dotnet/tree/net-4.7.1).

### .NET 4

Support for .NET 4 has be dropped. The last build for .NET 4 is the NuGet version `1.3.1`. The code can be found on tag [.net-4.0](https://github.com/shawnmclean/Mandrill-dotnet/tree/net-4.0).
Support for .NET 4 has been dropped. The last build for .NET 4 is the NuGet version `1.3.1`. The code can be found on tag [.net-4.0](https://github.com/shawnmclean/Mandrill-dotnet/tree/net-4.0).
Async and Sync methods were merged into using the async pattern as suggested as [best practice](http://blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx) by the parallel programming team at microsoft.

## Contributing
Expand Down
92 changes: 92 additions & 0 deletions src/Mandrill/Converters/DictionaryJsonConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Mandrill.Converters
{
public class DictionaryJsonConverter<TValue> : JsonConverter<Dictionary<string, TValue>>
{
private readonly JsonConverter<TValue> _valueConverter;
private readonly Type _valueType;

public DictionaryJsonConverter()
{
// Cache the key and value types.
_valueType = typeof(TValue);
}

public DictionaryJsonConverter(JsonSerializerOptions options) : this()
{
// For performance, use the existing converter if available.
_valueConverter = (JsonConverter<TValue>)options.GetConverter(typeof(TValue));
}

public override Dictionary<string, TValue> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartObject)
{
throw new JsonException();
}

var dictionary = new Dictionary<string, TValue>();

while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndObject)
{
return dictionary;
}

// Get the key.
if (reader.TokenType != JsonTokenType.PropertyName)
{
throw new JsonException();
}

var key = reader.GetString();

// Get the value.
TValue value;

if (_valueConverter != null)
{
reader.Read();
value = _valueConverter.Read(ref reader, _valueType, options)!;
}
else
{
value = JsonSerializer.Deserialize<TValue>(ref reader, options)!;
}

// Add to dictionary.
dictionary.Add(key, value);
}

throw new JsonException();
}

public override void Write(Utf8JsonWriter writer, Dictionary<string, TValue> dictionary, JsonSerializerOptions options)
{
writer.WriteStartObject();

foreach (var item in dictionary)
{
var propertyName = item.Key.ToString();
writer.WritePropertyName
(options.PropertyNamingPolicy?.ConvertName(propertyName) ?? propertyName);

if (_valueConverter != null)
{
_valueConverter.Write(writer, item.Value, options);
}
else
{
JsonSerializer.Serialize(writer, item.Value, options);
}
}

writer.WriteEndObject();
}
}
}
112 changes: 112 additions & 0 deletions src/Mandrill/Converters/JsonStringEnumMemberConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
using System.Reflection;

namespace System.Text.Json.Serialization
{
/// <summary>
/// <see cref="JsonConverterFactory"/> to convert enums to and from strings, respecting <see cref="EnumMemberAttribute"/> decorations. Supports nullable enums.
/// </summary>
public class JsonStringEnumMemberConverter : JsonConverterFactory
{
private readonly JsonNamingPolicy? _NamingPolicy;
private readonly bool _AllowIntegerValues;

/// <summary>
/// Initializes a new instance of the <see cref="JsonStringEnumMemberConverter"/> class.
/// </summary>
public JsonStringEnumMemberConverter()
: this(namingPolicy: null, allowIntegerValues: true)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="JsonStringEnumMemberConverter"/> class.
/// </summary>
/// <param name="namingPolicy">
/// Optional naming policy for writing enum values.
/// </param>
/// <param name="allowIntegerValues">
/// True to allow undefined enum values. When true, if an enum value isn't
/// defined it will output as a number rather than a string.
/// </param>
public JsonStringEnumMemberConverter(JsonNamingPolicy? namingPolicy = null, bool allowIntegerValues = true)
{
_NamingPolicy = namingPolicy;
_AllowIntegerValues = allowIntegerValues;
}

/// <inheritdoc/>
public override bool CanConvert(Type typeToConvert)
{
// Don't perform a typeToConvert == null check for performance. Trust our callers will be nice.
#pragma warning disable CA1062 // Validate arguments of public methods
return typeToConvert.IsEnum
|| (typeToConvert.IsGenericType && TestNullableEnum(typeToConvert).IsNullableEnum);
#pragma warning restore CA1062 // Validate arguments of public methods
}

/// <inheritdoc/>
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
{
(var IsNullableEnum, var UnderlyingType) = TestNullableEnum(typeToConvert);

return IsNullableEnum
? (JsonConverter)Activator.CreateInstance(
typeof(NullableEnumMemberConverter<>).MakeGenericType(UnderlyingType),
BindingFlags.Instance | BindingFlags.Public,
binder: null,
args: new object?[] { _NamingPolicy, _AllowIntegerValues },
culture: null)
: (JsonConverter)Activator.CreateInstance(
typeof(EnumMemberConverter<>).MakeGenericType(typeToConvert),
BindingFlags.Instance | BindingFlags.Public,
binder: null,
args: new object?[] { _NamingPolicy, _AllowIntegerValues },
culture: null);
}

private static (bool IsNullableEnum, Type? UnderlyingType) TestNullableEnum(Type typeToConvert)
{
var UnderlyingType = Nullable.GetUnderlyingType(typeToConvert);

return (UnderlyingType?.IsEnum ?? false, UnderlyingType);
}

#pragma warning disable CA1812 // Remove class never instantiated
private class EnumMemberConverter<TEnum> : JsonConverter<TEnum>
where TEnum : struct, Enum
#pragma warning restore CA1812 // Remove class never instantiated
{
private readonly JsonStringEnumMemberConverterHelper<TEnum> _JsonStringEnumMemberConverterHelper;

public EnumMemberConverter(JsonNamingPolicy? namingPolicy, bool allowIntegerValues)
{
_JsonStringEnumMemberConverterHelper = new JsonStringEnumMemberConverterHelper<TEnum>(namingPolicy, allowIntegerValues);
}

public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> _JsonStringEnumMemberConverterHelper.Read(ref reader);

public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options)
=> _JsonStringEnumMemberConverterHelper.Write(writer, value);
}

#pragma warning disable CA1812 // Remove class never instantiated
private class NullableEnumMemberConverter<TEnum> : JsonConverter<TEnum?>
where TEnum : struct, Enum
#pragma warning restore CA1812 // Remove class never instantiated
{
private readonly JsonStringEnumMemberConverterHelper<TEnum> _JsonStringEnumMemberConverterHelper;

public NullableEnumMemberConverter(JsonNamingPolicy? namingPolicy, bool allowIntegerValues)
{
_JsonStringEnumMemberConverterHelper = new JsonStringEnumMemberConverterHelper<TEnum>(namingPolicy, allowIntegerValues);
}

public override TEnum? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> _JsonStringEnumMemberConverterHelper.Read(ref reader);

public override void Write(Utf8JsonWriter writer, TEnum? value, JsonSerializerOptions options)
=> _JsonStringEnumMemberConverterHelper.Write(writer, value!.Value);
}
}
}
Loading