Skip to content

Commit 81db506

Browse files
feature: store type resolution info as a dictionary
this way, each platform can have its own way of storing resolving info, and if someone needs to cross-platforms, they have the info required to resolve the cross-platform type by knowing what the headers are for the other platform
1 parent a17ab24 commit 81db506

File tree

21 files changed

+274
-166
lines changed

21 files changed

+274
-166
lines changed
Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
11
using System;
2-
using System.Reflection;
2+
using System.Collections.Generic;
33

44
namespace EntityDb.Abstractions.Strategies
55
{
66
/// <summary>
7-
/// Represents a type that resolves a <see cref="Type"/> by using the <see cref="Assembly.FullName"/>, <see cref="Type.FullName"/>, and <see cref="MemberInfo.Name"/>.
7+
/// Represents a type that resolves a <see cref="Type"/> or returns null.
88
/// </summary>
99
public interface IResolvingStrategy
1010
{
1111
/// <summary>
1212
/// Returns the resolved <see cref="Type"/> or null if the <see cref="Type"/> cannot be resolved.
1313
/// </summary>
14-
/// <param name="assemblyFullName">The <see cref="Assembly.FullName"/> of the <see cref="Type.Assembly"/>.</param>
15-
/// <param name="typeFullName">The <see cref="Type.FullName"/>.</param>
16-
/// <param name="typeName">The <see cref="MemberInfo.Name"/>.</param>
14+
/// <param name="headers">Describes the type that needs to be resolved.</param>
1715
/// <returns>The resolved <see cref="Type"/> or null if the <see cref="Type"/> cannot be resolved.</returns>
18-
Type? ResolveType(string? assemblyFullName, string? typeFullName, string? typeName);
16+
Type? ResolveType(IReadOnlyDictionary<string, string> headers);
1917
}
2018
}
Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
using System;
2-
using System.Reflection;
2+
using System.Collections.Generic;
33

44
namespace EntityDb.Abstractions.Strategies
55
{
@@ -11,10 +11,8 @@ public interface IResolvingStrategyChain
1111
/// <summary>
1212
/// Returns the resolved <see cref="Type"/> or throws if the <see cref="Type"/> cannot be resolved.
1313
/// </summary>
14-
/// <param name="assemblyFullName">The <see cref="Assembly.FullName"/> of the <see cref="Type.Assembly"/>.</param>
15-
/// <param name="typeFullName">The <see cref="Type.FullName"/>.</param>
16-
/// <param name="typeName">The <see cref="MemberInfo.Name"/>.</param>
14+
/// <param name="headers">Describes the type that needs to be resolved.</param>
1715
/// <returns>The resolved <see cref="Type"/>.</returns>
18-
Type ResolveType(string? assemblyFullName, string? typeFullName, string? typeName);
16+
Type ResolveType(IReadOnlyDictionary<string, string> headers);
1917
}
2018
}

EntityDb.Common.Tests/Startup.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public void ConfigureServices(IServiceCollection serviceCollection)
1717
serviceCollection.AddDefaultResolvingStrategy();
1818

1919
// This is only here to add coverage
20-
serviceCollection.AddTypeNameResolvingStrategy(new[]
20+
serviceCollection.AddMemberInfoNameResolvingStrategy(new[]
2121
{
2222
typeof(object),
2323
});

EntityDb.Common.Tests/Strategies/Resolving/DefaultResolvingStrategyTests.cs

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
using EntityDb.Common.Extensions;
1+
using EntityDb.Common.Envelopes;
22
using EntityDb.Common.Strategies.Resolving;
33
using Shouldly;
4+
using System.Collections.Generic;
45
using System.IO;
56
using Xunit;
67

@@ -17,31 +18,31 @@ public void GivenFullNames_WhenLoadingType_ThenReturnType()
1718

1819
var expectedType = record.GetType();
1920

20-
var (assemblyFullName, typeFullName, _) = record.GetType().GetTypeInfo();
21+
var headers = EnvelopeHelper.GetTypeHeaders(expectedType, includeFullNames: true, includeMemberInfoName: false);
2122

2223
var resolvingStrategy = new DefaultResolvingStrategy();
2324

2425
// ACT
2526

26-
var actualType = resolvingStrategy.ResolveType(assemblyFullName, typeFullName, null);
27+
var actualType = resolvingStrategy.ResolveType(headers);
2728

2829
// ASSERT
2930

3031
actualType.ShouldBe(expectedType);
3132
}
3233

3334
[Fact]
34-
public void GivenTypeName_WhenLoadingType_ThenReturnNull()
35+
public void GivenMemberInfoName_WhenLoadingType_ThenReturnNull()
3536
{
3637
// ARRANGE
3738

38-
var (_, _, typeName) = typeof(object).GetTypeInfo();
39+
var headers = EnvelopeHelper.GetTypeHeaders(typeof(object), includeFullNames: false, includeMemberInfoName: true);
3940

4041
var resolvingStrategy = new DefaultResolvingStrategy();
4142

4243
// ACT
4344

44-
var actualType = resolvingStrategy.ResolveType(null, null, typeName);
45+
var actualType = resolvingStrategy.ResolveType(headers);
4546

4647
// ASSERT
4748

@@ -55,9 +56,14 @@ public void GivenNoTypeInformation_WhenLoadingType_ThenReturnNull()
5556

5657
var resolvingStrategy = new DefaultResolvingStrategy();
5758

59+
var headers = new Dictionary<string, string>
60+
{
61+
[EnvelopeHelper.Platform] = EnvelopeHelper.ThisPlatform,
62+
};
63+
5864
// ACT
5965

60-
var actualType = resolvingStrategy.ResolveType(null, null, null);
66+
var actualType = resolvingStrategy.ResolveType(headers);
6167

6268
// ASSERT
6369

@@ -71,12 +77,17 @@ public void GivenGarbageTypeInformation_WhenLoadingType_ThenThrow()
7177

7278
var resolvingStrategy = new DefaultResolvingStrategy();
7379

80+
var headers = new Dictionary<string, string>
81+
{
82+
[EnvelopeHelper.Platform] = EnvelopeHelper.ThisPlatform,
83+
[EnvelopeHelper.AssemblyFullName] = "Garbage",
84+
[EnvelopeHelper.TypeFullName] = "Garbage",
85+
[EnvelopeHelper.MemberInfoName] = "Garbage",
86+
};
87+
7488
// ASSERT
7589

76-
Should.Throw<FileNotFoundException>(() =>
77-
{
78-
resolvingStrategy.ResolveType("Garbage", "Garbage", "Garbage");
79-
});
90+
Should.Throw<FileNotFoundException>(() => resolvingStrategy.ResolveType(headers));
8091
}
8192
}
8293
}

EntityDb.Common.Tests/Strategies/Resolving/LifoResolvingStrategyChainTests.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using Moq;
66
using Shouldly;
77
using System;
8+
using System.Collections.Generic;
89
using Xunit;
910

1011
namespace EntityDb.Common.Tests.Strategies.Resolving
@@ -31,7 +32,7 @@ public void GivenResolvingStrategyThrows_WhenExceptionThrown_ThenExceptionIsLogg
3132
var resolvingStrategyMock = new Mock<IResolvingStrategy>();
3233

3334
resolvingStrategyMock
34-
.Setup(strategy => strategy.ResolveType(It.IsAny<string?>(), It.IsAny<string?>(), It.IsAny<string?>()))
35+
.Setup(strategy => strategy.ResolveType(It.IsAny<Dictionary<string, string>>()))
3536
.Throws(new Exception());
3637

3738
var resolvingStrategyChain = new LifoResolvingStrategyChain(loggerFactoryMock.Object, new[]
@@ -41,7 +42,7 @@ public void GivenResolvingStrategyThrows_WhenExceptionThrown_ThenExceptionIsLogg
4142

4243
// ASSERT
4344

44-
Should.Throw<CannotResolveTypeException>(() => resolvingStrategyChain.ResolveType(default!, default!, default!));
45+
Should.Throw<CannotResolveTypeException>(() => resolvingStrategyChain.ResolveType(default!));
4546

4647
loggerMock.Verify();
4748
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using EntityDb.Common.Envelopes;
2+
using EntityDb.Common.Strategies.Resolving;
3+
using Shouldly;
4+
using System;
5+
using System.Collections.Generic;
6+
using Xunit;
7+
8+
namespace EntityDb.Common.Tests.Strategies.Resolving
9+
{
10+
public class MemberInfoNameResolvingStrategyTests
11+
{
12+
[Fact]
13+
public void GivenMemberInfoNameResolvingStrategyKnowsExpectedType_WhenResolvingType_ThenReturnExpectedType()
14+
{
15+
// ARRANGE
16+
17+
var expectedType = typeof(string);
18+
19+
var headers = new Dictionary<string, string>
20+
{
21+
[EnvelopeHelper.Platform] = EnvelopeHelper.ThisPlatform,
22+
[EnvelopeHelper.MemberInfoName] = expectedType.Name,
23+
};
24+
25+
var resolvingStrategy = new MemberInfoNameResolvingStrategy(new[] { expectedType });
26+
27+
// ACT
28+
29+
var actualType = resolvingStrategy.ResolveType(headers);
30+
31+
// ASSERT
32+
33+
actualType.ShouldBe(expectedType);
34+
}
35+
36+
[Fact]
37+
public void GivenNonEmptyMemberInfoResolvingStrategy_WhenResolvingTypeWithNoInformation_ThenReturnNull()
38+
{
39+
// ARRANGE
40+
41+
var resolvingStrategy = new MemberInfoNameResolvingStrategy(new[] { typeof(string) });
42+
43+
var headers = new Dictionary<string, string>();
44+
45+
// ACT
46+
47+
var actualType = resolvingStrategy.ResolveType(headers);
48+
49+
// ASSERT
50+
51+
actualType.ShouldBeNull();
52+
}
53+
54+
[Fact]
55+
public void GivenEmptyMemberInfoNameResolvingStrategy_WhenResolvingType_ThenReturnNull()
56+
{
57+
// ARRANGE
58+
59+
var resolvingStrategy = new MemberInfoNameResolvingStrategy(Array.Empty<Type>());
60+
61+
var headers = new Dictionary<string, string>
62+
{
63+
[EnvelopeHelper.Platform] = EnvelopeHelper.ThisPlatform,
64+
[EnvelopeHelper.MemberInfoName] = "",
65+
};
66+
67+
// ACT
68+
69+
var actualType = resolvingStrategy.ResolveType(headers);
70+
71+
// ASSERT
72+
73+
actualType.ShouldBeNull();
74+
}
75+
}
76+
}

EntityDb.Common.Tests/Strategies/Resolving/TypeNameResolvingStrategyTests.cs

Lines changed: 0 additions & 60 deletions
This file was deleted.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
using EntityDb.Common.Strategies.Resolving;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
6+
namespace EntityDb.Common.Envelopes
7+
{
8+
internal static class EnvelopeHelper
9+
{
10+
public const string Platform = nameof(Platform);
11+
public const string ThisPlatform = ".NET";
12+
public const string Type = nameof(Type);
13+
public const string AssemblyFullName = nameof(AssemblyFullName);
14+
public const string TypeFullName = nameof(TypeFullName);
15+
public const string MemberInfoName = nameof(MemberInfoName);
16+
17+
public static bool NotThisPlatform(IReadOnlyDictionary<string, string> headers)
18+
{
19+
return headers.TryGetValue(Platform, out var platform) == false || platform != ThisPlatform;
20+
}
21+
22+
public static bool TryGetAssemblyFullName(IReadOnlyDictionary<string, string> headers, out string? assemblyFullName)
23+
{
24+
return headers.TryGetValue(AssemblyFullName, out assemblyFullName);
25+
}
26+
27+
public static bool TryGetTypeFullName(IReadOnlyDictionary<string, string> headers, out string? typeFullName)
28+
{
29+
return headers.TryGetValue(TypeFullName, out typeFullName);
30+
}
31+
32+
public static bool TryGetMemberInfoName(IReadOnlyDictionary<string, string> headers, out string? memberInfoName)
33+
{
34+
return headers.TryGetValue(MemberInfoName, out memberInfoName);
35+
}
36+
37+
public static Dictionary<string, string> GetTypeHeaders
38+
(
39+
Type type,
40+
bool includeFullNames = true,
41+
bool includeMemberInfoName = true
42+
)
43+
{
44+
var headers = new Dictionary<string, string>
45+
{
46+
[Platform] = ThisPlatform,
47+
[Type] = type.Name,
48+
};
49+
50+
if (includeFullNames)
51+
{
52+
var assemblyFullName = type.Assembly.FullName;
53+
54+
if (assemblyFullName != null)
55+
{
56+
headers.Add(AssemblyFullName, assemblyFullName);
57+
}
58+
59+
var typeFullName = type.FullName;
60+
61+
if (typeFullName != null)
62+
{
63+
headers.Add(TypeFullName, typeFullName);
64+
}
65+
}
66+
67+
if (includeMemberInfoName)
68+
{
69+
headers.Add(MemberInfoName, type.Name);
70+
}
71+
72+
return headers;
73+
}
74+
75+
public static string[] GetTypeHeaderValues(this Type[] types)
76+
{
77+
return types.Select(type => type.Name).ToArray();
78+
}
79+
}
80+
}

EntityDb.Common/Extensions/IServiceCollectionExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ public static void AddDefaultResolvingStrategy(this IServiceCollection serviceCo
3030
/// </summary>
3131
/// <param name="serviceCollection">The service collection.</param>
3232
/// <param name="types">The types that can be resolved by <see cref="MemberInfo.Name"/>.</param>
33-
public static void AddTypeNameResolvingStrategy(this IServiceCollection serviceCollection, Type[] types)
33+
public static void AddMemberInfoNameResolvingStrategy(this IServiceCollection serviceCollection, Type[] types)
3434
{
35-
serviceCollection.AddSingleton<IResolvingStrategy>((serviceProvider) => new TypeNameResolvingStrategy(types));
35+
serviceCollection.AddSingleton<IResolvingStrategy>((serviceProvider) => new MemberInfoNameResolvingStrategy(types));
3636
}
3737

3838
/// <summary>

0 commit comments

Comments
 (0)