From 80680a5cb6f5cd8318607c15b3ed9f922d5d36f8 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Wed, 22 May 2019 22:02:50 +0300 Subject: [PATCH] Try to map base classes for MongoDb --- .../Volo.Abp.Core/System/AbpTypeExtensions.cs | 36 +++++++++++++- .../Abp/MongoDB/AbpBsonClassMapExtensions.cs | 47 +++++++++++++------ .../Volo/Abp/MongoDB/MongoModelBuilder.cs | 37 +++++++++++---- .../System/AbpTypeExtensions_Tests.cs | 32 +++++++++++++ 4 files changed, 126 insertions(+), 26 deletions(-) create mode 100644 framework/test/Volo.Abp.Core.Tests/System/AbpTypeExtensions_Tests.cs diff --git a/framework/src/Volo.Abp.Core/System/AbpTypeExtensions.cs b/framework/src/Volo.Abp.Core/System/AbpTypeExtensions.cs index a90e4212e1..a930096404 100644 --- a/framework/src/Volo.Abp.Core/System/AbpTypeExtensions.cs +++ b/framework/src/Volo.Abp.Core/System/AbpTypeExtensions.cs @@ -1,4 +1,7 @@ -namespace System +using System.Collections.Generic; +using JetBrains.Annotations; + +namespace System { public static class AbpTypeExtensions { @@ -31,5 +34,36 @@ { return targetType.IsAssignableFrom(type); } + + /// + /// Gets all base classes of this type. + /// + /// The type to get its base classes. + /// True, to include the standard type in the returned array. + public static Type[] GetBaseClasses(this Type type, bool includeObject = true) + { + var types = new List(); + AddTypeAndBaseTypesRecursively(types, type.BaseType, includeObject); + return types.ToArray(); + } + + private static void AddTypeAndBaseTypesRecursively( + [NotNull] List types, + [CanBeNull] Type type, + bool includeObject) + { + if (type == null) + { + return; + } + + if (!includeObject && type == typeof(object)) + { + return; + } + + AddTypeAndBaseTypesRecursively(types, type.BaseType, includeObject); + types.Add(type); + } } } diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpBsonClassMapExtensions.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpBsonClassMapExtensions.cs index f53e524914..a450a4bc43 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpBsonClassMapExtensions.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpBsonClassMapExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Reflection; using MongoDB.Bson.Serialization; using Volo.Abp.Data; @@ -9,34 +10,50 @@ namespace Volo.Abp.MongoDB public static void ConfigureAbpConventions(this BsonClassMap map) { map.AutoMap(); - map.ConfigureExtraProperties(); + map.TryConfigureExtraProperties(); } - public static void ConfigureExtraProperties(this BsonClassMap map) + public static bool TryConfigureExtraProperties(this BsonClassMap map) where TEntity : class, IHasExtraProperties { + var property = map.ClassType.GetProperty( + nameof(IHasExtraProperties.ExtraProperties), + BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty + ); + + if (property?.DeclaringType != map.ClassType) + { + return false; + } + map.SetExtraElementsMember(new BsonMemberMap( map, typeof(TEntity).GetMember(nameof(IHasExtraProperties.ExtraProperties))[0]) ); + + return true; } - /// - /// Configures SetExtraElementsMember if the - /// implements the interface. - /// Otherwise, does nothing - /// - public static void ConfigureExtraProperties(this BsonClassMap map) + public static bool TryConfigureExtraProperties(this BsonClassMap map) { - if (map.ClassType.IsAssignableTo()) + if (!map.ClassType.IsAssignableTo()) { - map.SetExtraElementsMember( - new BsonMemberMap( - map, - map.ClassType.GetMember(nameof(IHasExtraProperties.ExtraProperties))[0] - ) - ); + return false; } + + var property = map.ClassType.GetProperty( + nameof(IHasExtraProperties.ExtraProperties), + BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty + ); + + if (property?.DeclaringType != map.ClassType) + { + return false; + } + + map.MapExtraElementsMember(property); + + return true; } } } diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoModelBuilder.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoModelBuilder.cs index 788d0bdbea..43ffbabf4a 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoModelBuilder.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoModelBuilder.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using Volo.Abp.Domain.Entities; namespace Volo.Abp.MongoDB { @@ -19,24 +20,40 @@ namespace Volo.Abp.MongoDB public MongoDbContextModel Build() { - var entityModels = _entityModelBuilders - .Select(x => x.Value) - .Cast() - .ToImmutableDictionary(x => x.EntityType, x => x); - - foreach (var entityModel in entityModels.Values) + lock (SyncObj) { - var map = entityModel.As().GetMap(); - lock (SyncObj) + var entityModels = _entityModelBuilders + .Select(x => x.Value) + .Cast() + .ToImmutableDictionary(x => x.EntityType, x => x); + + var baseClasses = new List(); + + foreach (var entityModel in entityModels.Values) { + var map = entityModel.As().GetMap(); if (!BsonClassMap.IsClassMapRegistered(map.ClassType)) { BsonClassMap.RegisterClassMap(map); } + + baseClasses.AddRange(entityModel.EntityType.GetBaseClasses(includeObject: false)); } - } - return new MongoDbContextModel(entityModels); + baseClasses = baseClasses.Distinct().ToList(); + + foreach (var baseClass in baseClasses) + { + if (!BsonClassMap.IsClassMapRegistered(baseClass)) + { + var map = new BsonClassMap(baseClass); + map.ConfigureAbpConventions(); + BsonClassMap.RegisterClassMap(map); + } + } + + return new MongoDbContextModel(entityModels); + } } public virtual void Entity(Action> buildAction = null) diff --git a/framework/test/Volo.Abp.Core.Tests/System/AbpTypeExtensions_Tests.cs b/framework/test/Volo.Abp.Core.Tests/System/AbpTypeExtensions_Tests.cs new file mode 100644 index 0000000000..382440a3f4 --- /dev/null +++ b/framework/test/Volo.Abp.Core.Tests/System/AbpTypeExtensions_Tests.cs @@ -0,0 +1,32 @@ +using Shouldly; +using Xunit; + +namespace System +{ + public class AbpTypeExtensions_Tests + { + [Fact] + public void GetBaseClasses() + { + var baseClasses = typeof(MyClass).GetBaseClasses(includeObject: false); + baseClasses.Length.ShouldBe(2); + baseClasses[0].ShouldBe(typeof(MyBaseClass1)); + baseClasses[1].ShouldBe(typeof(MyBaseClass2)); + } + + public abstract class MyBaseClass1 + { + + } + + public class MyBaseClass2 : MyBaseClass1 + { + + } + + public class MyClass : MyBaseClass2 + { + + } + } +}