Skip to content

Commit b2d0459

Browse files
authored
Split mapping of EntityDefinition and BsonClassMap (#335)
These changes make it possible for a fluent model builder to exist, creating an EntityDefinition which is then fed into the driver by the rest of the mapping system.
1 parent 393765e commit b2d0459

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+566
-473
lines changed

src/MongoFramework/Infrastructure/Commands/AddToBucketCommand.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ public class AddToBucketCommand<TGroup, TSubEntity> : IWriteCommand<EntityBucket
1111
{
1212
private TGroup Group { get; }
1313
private TSubEntity SubEntity { get; }
14-
private IEntityProperty EntityTimeProperty { get; }
14+
private IEntityPropertyDefinition EntityTimeProperty { get; }
1515
private int BucketSize { get; }
1616

1717
public Type EntityType => typeof(EntityBucket<TGroup, TSubEntity>);
1818

19-
public AddToBucketCommand(TGroup group, TSubEntity subEntity, IEntityProperty entityTimeProperty, int bucketSize)
19+
public AddToBucketCommand(TGroup group, TSubEntity subEntity, IEntityPropertyDefinition entityTimeProperty, int bucketSize)
2020
{
2121
Group = group;
2222
SubEntity = subEntity;
@@ -42,7 +42,7 @@ public IEnumerable<WriteModel<EntityBucket<TGroup, TSubEntity>>> GetModel(WriteM
4242
.Min(b => b.Min, itemTimeValue)
4343
.Max(b => b.Max, itemTimeValue)
4444
.SetOnInsert(b => b.BucketSize, BucketSize)
45-
.SetOnInsert(b => b.Id, entityDefinition.KeyGenerator.Generate());
45+
.SetOnInsert(b => b.Id, entityDefinition.Key.KeyGenerator.Generate());
4646

4747
yield return new UpdateOneModel<EntityBucket<TGroup, TSubEntity>>(filter, updateDefinition)
4848
{
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System;
2+
using MongoDB.Bson.Serialization.Serializers;
3+
using MongoDB.Bson.Serialization;
4+
using MongoDB.Bson;
5+
using System.Diagnostics;
6+
using MongoFramework.Infrastructure.Serialization;
7+
8+
namespace MongoFramework.Infrastructure;
9+
10+
/// <summary>
11+
/// Provides a single entry point to configure common areas of the driver for MongoFramework
12+
/// </summary>
13+
internal static class DriverAbstractionRules
14+
{
15+
public static void ApplyRules()
16+
{
17+
RegisterSerializer<decimal>(new DecimalSerializer(BsonType.Decimal128));
18+
RegisterSerializer<decimal?>(new NullableSerializer<decimal>(new DecimalSerializer(BsonType.Decimal128)));
19+
20+
BsonSerializer.RegisterSerializationProvider(TypeDiscoverySerializationProvider.Instance);
21+
}
22+
23+
private static void RegisterSerializer<TTarget>(IBsonSerializer serializer)
24+
{
25+
try
26+
{
27+
BsonSerializer.RegisterSerializer(typeof(TTarget), serializer);
28+
}
29+
catch (BsonSerializationException ex) when (ex.Message.Contains("already a serializer registered"))
30+
{
31+
// Already registered
32+
}
33+
catch (Exception ex)
34+
{
35+
Debug.WriteLine(ex.Message, "MongoFramework");
36+
}
37+
}
38+
}

src/MongoFramework/Infrastructure/Indexing/IndexModelBuilder.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,23 +43,23 @@ public static IEnumerable<CreateIndexModel<TEntity>> BuildModel()
4343
}
4444
}
4545

46-
private static CreateIndexModel<TEntity> CreateIndexModel(IEntityIndex indexDefinition)
46+
private static CreateIndexModel<TEntity> CreateIndexModel(IEntityIndexDefinition indexDefinition)
4747
{
4848
var builder = Builders<TEntity>.IndexKeys;
4949
IndexKeysDefinition<TEntity> keyModel;
5050

5151
if (indexDefinition.IndexType == IndexType.Text)
5252
{
53-
keyModel = builder.Text(indexDefinition.Property.FullPath);
53+
keyModel = builder.Text(indexDefinition.Path);
5454
}
5555
else if (indexDefinition.IndexType == IndexType.Geo2dSphere)
5656
{
57-
keyModel = builder.Geo2DSphere(indexDefinition.Property.FullPath);
57+
keyModel = builder.Geo2DSphere(indexDefinition.Path);
5858
}
5959
else
6060
{
6161
keyModel = indexDefinition.SortOrder == IndexSortOrder.Ascending ?
62-
builder.Ascending(indexDefinition.Property.FullPath) : builder.Descending(indexDefinition.Property.FullPath);
62+
builder.Ascending(indexDefinition.Path) : builder.Descending(indexDefinition.Path);
6363
}
6464

6565
if (indexDefinition.IsTenantExclusive && typeof(IHaveTenantId).IsAssignableFrom(typeof(TEntity)))
Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,45 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Linq;
43

5-
namespace MongoFramework.Infrastructure.Internal
4+
namespace MongoFramework.Infrastructure.Internal;
5+
6+
internal static class TypeExtensions
67
{
7-
internal static class TypeExtensions
8+
private static readonly HashSet<Type> CommonGenericEnumerables = new()
9+
{
10+
typeof(IEnumerable<>),
11+
typeof(IList<>),
12+
typeof(ICollection<>),
13+
typeof(IReadOnlyList<>),
14+
typeof(IReadOnlyCollection<>)
15+
};
16+
17+
public static Type GetEnumerableItemTypeOrDefault(this Type type)
818
{
9-
public static Type GetEnumerableItemTypeOrDefault(this Type type)
19+
if (type.IsArray)
20+
{
21+
return type.GetElementType();
22+
}
23+
else if (type.IsGenericType)
1024
{
11-
if (type.IsArray)
25+
if (CommonGenericEnumerables.Contains(type.GetGenericTypeDefinition()))
1226
{
13-
return type.GetElementType();
27+
return type.GetGenericArguments()[0];
1428
}
15-
else if (type.IsGenericType)
29+
else
1630
{
17-
if (type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
31+
//Unlike when the type is directly a known generic enumerable interface, if we start making assumptions
32+
//like that on the interfaces of the type, we can hit edge cases where a type implements multiple interfaces.
33+
foreach (var interfaceType in type.GetInterfaces())
1834
{
19-
return type.GetGenericArguments()[0];
20-
}
21-
else
22-
{
23-
var compatibleInterfaces = type.GetInterfaces().Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>));
24-
var targetInterface = compatibleInterfaces.FirstOrDefault();
25-
if (targetInterface != null)
35+
if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
2636
{
2737
return type.GetGenericArguments()[0];
2838
}
2939
}
3040
}
31-
32-
return type;
3341
}
42+
43+
return type;
3444
}
3545
}

src/MongoFramework/Infrastructure/Mapping/DefaultMappingPack.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ public static class DefaultProcessors
1313
new EntityIdProcessor(),
1414
new NestedTypeProcessor(),
1515
new ExtraElementsProcessor(),
16-
new DecimalSerializationProcessor(),
17-
new TypeDiscoveryProcessor(),
1816
new BsonKnownTypesProcessor(),
1917
new IndexProcessor(),
2018
new MappingAdapterProcessor()
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using MongoDB.Bson.Serialization;
2+
3+
namespace MongoFramework.Infrastructure.Mapping;
4+
5+
/// <summary>
6+
/// Maps the MongoFramework definition into something the MongoDB Driver will understand
7+
/// </summary>
8+
internal static class DriverMappingInterop
9+
{
10+
/// <summary>
11+
/// Registers the <paramref name="definition"/> as a <see cref="BsonClassMap"/> with all appropriate properties configured.
12+
/// </summary>
13+
/// <param name="definition"></param>
14+
public static void RegisterDefinition(IEntityDefinition definition)
15+
{
16+
var classMap = new BsonClassMap(definition.EntityType);
17+
18+
// Hierarchy
19+
if (!EntityMapping.IsValidTypeToMap(definition.EntityType.BaseType))
20+
{
21+
classMap.SetIsRootClass(true);
22+
}
23+
24+
// Properties
25+
foreach (var property in definition.Properties)
26+
{
27+
var memberMap = classMap.MapMember(property.PropertyInfo);
28+
memberMap.SetElementName(property.ElementName);
29+
}
30+
31+
// Key / ID
32+
if (definition.Key is not null)
33+
{
34+
var idMemberMap = classMap.MapIdMember(definition.Key.Property.PropertyInfo);
35+
idMemberMap.SetIdGenerator(new DriverKeyGeneratorWrapper(definition.Key.KeyGenerator));
36+
}
37+
38+
// Extra Elements
39+
if (definition.ExtraElements is not null)
40+
{
41+
if (definition.ExtraElements.IgnoreExtraElements)
42+
{
43+
classMap.SetIgnoreExtraElements(true);
44+
classMap.SetIgnoreExtraElementsIsInherited(definition.ExtraElements.IgnoreInherited);
45+
}
46+
else
47+
{
48+
classMap.SetIgnoreExtraElements(false);
49+
50+
var extraElementsProperty = definition.ExtraElements.Property;
51+
foreach (var memberMap in classMap.DeclaredMemberMaps)
52+
{
53+
if (memberMap.ElementName == extraElementsProperty.ElementName)
54+
{
55+
classMap.SetExtraElementsMember(memberMap);
56+
break;
57+
}
58+
}
59+
}
60+
}
61+
62+
BsonClassMap.RegisterClassMap(classMap);
63+
}
64+
}
Lines changed: 99 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,107 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4+
using System.Reflection;
45

5-
namespace MongoFramework.Infrastructure.Mapping
6+
namespace MongoFramework.Infrastructure.Mapping;
7+
8+
public interface IEntityDefinition
9+
{
10+
public Type EntityType { get; set; }
11+
public string CollectionName { get; set; }
12+
public IEntityKeyDefinition Key { get; set; }
13+
public IEnumerable<IEntityPropertyDefinition> Properties { get; set; }
14+
public IEnumerable<IEntityIndexDefinition> Indexes { get; set; }
15+
public IEntityExtraElementsDefinition ExtraElements { get; set; }
16+
}
17+
18+
public interface IEntityPropertyDefinition
19+
{
20+
public IEntityDefinition EntityDefinition { get; }
21+
public string ElementName { get; }
22+
public PropertyInfo PropertyInfo { get; }
23+
24+
public object GetValue(object entity);
25+
public void SetValue(object entity, object value);
26+
}
27+
28+
public interface IEntityIndexDefinition
29+
{
30+
public IReadOnlyCollection<IEntityPropertyDefinition> Properties { get; }
31+
[Obsolete("Index definition can point to multiple properties directly")]
32+
public IEntityPropertyDefinition Property { get; }
33+
//TODO: This will be made redundant when the broader change to support fluent comes in
34+
public string Path { get; }
35+
public string IndexName { get; }
36+
public bool IsUnique { get; }
37+
public IndexSortOrder SortOrder { get; }
38+
public int IndexPriority { get; }
39+
public IndexType IndexType { get; }
40+
public bool IsTenantExclusive { get; }
41+
}
42+
43+
public interface IEntityExtraElementsDefinition
44+
{
45+
public IEntityPropertyDefinition Property { get; }
46+
public bool IgnoreExtraElements { get; }
47+
public bool IgnoreInherited { get; }
48+
}
49+
50+
public interface IEntityKeyDefinition
51+
{
52+
public IEntityPropertyDefinition Property { get; }
53+
public IEntityKeyGenerator KeyGenerator { get; }
54+
}
55+
56+
public class EntityDefinition : IEntityDefinition
57+
{
58+
public Type EntityType { get; set; }
59+
public string CollectionName { get; set; }
60+
public IEntityKeyDefinition Key { get; set; }
61+
public IEnumerable<IEntityPropertyDefinition> Properties { get; set; } = Enumerable.Empty<IEntityPropertyDefinition>();
62+
public IEnumerable<IEntityIndexDefinition> Indexes { get; set; } = Enumerable.Empty<IEntityIndexDefinition>();
63+
public IEntityExtraElementsDefinition ExtraElements { get; set; }
64+
}
65+
66+
public class EntityPropertyDefinition : IEntityPropertyDefinition
667
{
7-
public class EntityDefinition : IEntityDefinition
68+
public IEntityDefinition EntityDefinition { get; set; }
69+
public string ElementName { get; set; }
70+
public PropertyInfo PropertyInfo { get; set; }
71+
72+
public object GetValue(object entity)
73+
{
74+
return PropertyInfo.GetValue(entity);
75+
}
76+
77+
public void SetValue(object entity, object value)
878
{
9-
public Type EntityType { get; set; }
10-
public string CollectionName { get; set; }
11-
public IEntityKeyGenerator KeyGenerator { get; set; }
12-
public IEnumerable<IEntityProperty> Properties { get; set; } = Enumerable.Empty<IEntityProperty>();
13-
public IEnumerable<IEntityIndex> Indexes { get; set; } = Enumerable.Empty<IEntityIndex>();
79+
PropertyInfo.SetValue(entity, value);
1480
}
1581
}
82+
83+
public class EntityIndexDefinition : IEntityIndexDefinition
84+
{
85+
public IReadOnlyCollection<IEntityPropertyDefinition> Properties { get; set; }
86+
public IEntityPropertyDefinition Property { get; set; }
87+
public string Path { get; set; }
88+
public string IndexName { get; set; }
89+
public bool IsUnique { get; set; }
90+
public IndexSortOrder SortOrder { get; set; }
91+
public int IndexPriority { get; set; }
92+
public IndexType IndexType { get; set; }
93+
public bool IsTenantExclusive { get; set; }
94+
}
95+
96+
public sealed record EntityKeyDefinition : IEntityKeyDefinition
97+
{
98+
public IEntityPropertyDefinition Property { get; init; }
99+
public IEntityKeyGenerator KeyGenerator { get; init; }
100+
}
101+
102+
public sealed record EntityExtraElementsDefinition : IEntityExtraElementsDefinition
103+
{
104+
public IEntityPropertyDefinition Property { get; init; }
105+
public bool IgnoreExtraElements { get; init; }
106+
public bool IgnoreInherited { get; init; }
107+
}

0 commit comments

Comments
 (0)