diff --git a/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DependencyInjection/EfCoreRepositoryRegistrar.cs b/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DependencyInjection/EfCoreRepositoryRegistrar.cs index f75c4a0132..e6ed221c9b 100644 --- a/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DependencyInjection/EfCoreRepositoryRegistrar.cs +++ b/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DependencyInjection/EfCoreRepositoryRegistrar.cs @@ -10,6 +10,7 @@ namespace Volo.Abp.EntityFrameworkCore.DependencyInjection public EfCoreRepositoryRegistrar(AbpDbContextRegistrationOptions options) : base(options) { + } protected override IEnumerable GetEntityTypes(Type dbContextType) diff --git a/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepository.cs b/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepository.cs index 31a39eb3a2..991db17cef 100644 --- a/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepository.cs +++ b/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepository.cs @@ -9,8 +9,6 @@ namespace Volo.Abp.Domain.Repositories.MongoDB IMongoDatabase Database { get; } IMongoCollection Collection { get; } - - string CollectionName { get; } } public interface IMongoDbRepository : IMongoDbRepository, IRepository diff --git a/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs b/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs index db4ae76df7..8509d5ec49 100644 --- a/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs +++ b/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs @@ -15,8 +15,6 @@ namespace Volo.Abp.Domain.Repositories.MongoDB where TMongoDbContext : IAbpMongoDbContext where TEntity : class, IEntity { - public virtual string CollectionName => DbContext.GetCollectionName(); - public virtual IMongoCollection Collection => DbContext.Collection(); public virtual IMongoDatabase Database => DbContext.Database; diff --git a/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs b/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs index 02a80cfa3a..b33a8d8444 100644 --- a/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs +++ b/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs @@ -19,12 +19,6 @@ namespace Volo.Abp.Domain.Repositories return repository.ToMongoDbRepository().Collection; } - public static string GetCollectionName(this IBasicRepository repository) - where TEntity : class, IEntity - { - return repository.ToMongoDbRepository().CollectionName; - } - public static IMongoDbRepository ToMongoDbRepository(this IBasicRepository repository) where TEntity : class, IEntity { diff --git a/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbContext.cs b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbContext.cs index 5b829c95c9..e3adb5ce95 100644 --- a/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbContext.cs +++ b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbContext.cs @@ -1,31 +1,22 @@ -using System; using System.Collections.Generic; -using System.Linq; using MongoDB.Driver; namespace Volo.Abp.MongoDB { public abstract class AbpMongoDbContext : IAbpMongoDbContext { - private static readonly MongoEntityMapping[] EmptyTypeList = new MongoEntityMapping[0]; + public IMongoModelSource ModelSource { get; set; } public IMongoDatabase Database { get; private set; } - private readonly Lazy> _mappingsByType; - - protected AbpMongoDbContext() + protected internal virtual void CreateModel(IMongoModelBuilder modelBuilder) { - //TODO: Cache model/mappings - _mappingsByType = new Lazy>(() => - { - return GetMappings().ToDictionary(m => m.EntityType); - }, true); } - public virtual IReadOnlyList GetMappings() + public virtual void InitializeDatabase(IMongoDatabase database) { - return EmptyTypeList; + Database = database; } public virtual IMongoCollection Collection() @@ -33,25 +24,21 @@ namespace Volo.Abp.MongoDB return Database.GetCollection(GetCollectionName()); } - public virtual string GetCollectionName() + protected virtual string GetCollectionName() { - return GetMapping().CollectionName; + return GetEntityModel().CollectionName; } - protected virtual MongoEntityMapping GetMapping() + protected virtual IMongoEntityModel GetEntityModel() { - var mapping = _mappingsByType.Value.GetOrDefault(typeof(T)); - if (mapping == null) + var model = ModelSource.GetModel(this).Entities.GetOrDefault(typeof(TEntity)); + + if (model == null) { - throw new AbpException("Unmapped entity type: " + typeof(T).AssemblyQualifiedName); + throw new AbpException("Could not find a model for given entity type: " + typeof(TEntity).AssemblyQualifiedName); } - return mapping; - } - - public void InitializeDatabase(IMongoDatabase database) - { - Database = database; + return model; } } } \ No newline at end of file diff --git a/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DependencyInjection/MongoDbRepositoryRegistrar.cs b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DependencyInjection/MongoDbRepositoryRegistrar.cs index e3ec7abef2..8642401829 100644 --- a/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DependencyInjection/MongoDbRepositoryRegistrar.cs +++ b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/DependencyInjection/MongoDbRepositoryRegistrar.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Repositories.MongoDB; @@ -11,13 +10,12 @@ namespace Volo.Abp.MongoDB.DependencyInjection public MongoDbRepositoryRegistrar(MongoDbContextRegistrationOptions options) : base(options) { + } protected override IEnumerable GetEntityTypes(Type dbContextType) { - //TODO: Instead of getting from Options.OriginalDbContextType, we may consider to add entities as properties to the dbcontext, just like EF Core! - var mongoDbContext = (IAbpMongoDbContext)Activator.CreateInstance(Options.OriginalDbContextType); - return mongoDbContext.GetMappings().Select(m => m.EntityType); + return MongoDbContextHelper.GetEntityTypes(dbContextType); } protected override Type GetRepositoryType(Type dbContextType, Type entityType) diff --git a/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/IAbpMongoDbContext.cs b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/IAbpMongoDbContext.cs index 13851e40a6..a2f48fd323 100644 --- a/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/IAbpMongoDbContext.cs +++ b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/IAbpMongoDbContext.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using MongoDB.Driver; namespace Volo.Abp.MongoDB @@ -8,9 +7,5 @@ namespace Volo.Abp.MongoDB IMongoDatabase Database { get; } IMongoCollection Collection(); - - string GetCollectionName(); - - IReadOnlyList GetMappings(); //TODO: Consider to remove from the interface } } \ No newline at end of file diff --git a/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/IMongoEntityModel.cs b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/IMongoEntityModel.cs new file mode 100644 index 0000000000..ca9edfe869 --- /dev/null +++ b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/IMongoEntityModel.cs @@ -0,0 +1,11 @@ +using System; + +namespace Volo.Abp.MongoDB +{ + public interface IMongoEntityModel + { + Type EntityType { get; } + + string CollectionName { get; } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/IMongoModelBuilder.cs b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/IMongoModelBuilder.cs new file mode 100644 index 0000000000..f10bb26f58 --- /dev/null +++ b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/IMongoModelBuilder.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using JetBrains.Annotations; + +namespace Volo.Abp.MongoDB +{ + public interface IMongoModelBuilder + { + void Entity([NotNull] Action buildAction); + + void Entity([NotNull] Type entityType, [NotNull] Action buildAction); + + IReadOnlyList GetEntities(); + } +} \ No newline at end of file diff --git a/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/IMongoModelSource.cs b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/IMongoModelSource.cs new file mode 100644 index 0000000000..55c2ae6fc9 --- /dev/null +++ b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/IMongoModelSource.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.MongoDB +{ + public interface IMongoModelSource + { + MongoDbContextModel GetModel(AbpMongoDbContext dbContext); + } +} \ No newline at end of file diff --git a/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoCollectionAttribute.cs b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoCollectionAttribute.cs new file mode 100644 index 0000000000..ec2a64a659 --- /dev/null +++ b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoCollectionAttribute.cs @@ -0,0 +1,19 @@ +using System; + +namespace Volo.Abp.MongoDB +{ + public class MongoCollectionAttribute : Attribute + { + public string CollectionName { get; set; } + + public MongoCollectionAttribute() + { + + } + + public MongoCollectionAttribute(string collectionName) + { + CollectionName = collectionName; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoDbContextHelper.cs b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoDbContextHelper.cs new file mode 100644 index 0000000000..72bcf33768 --- /dev/null +++ b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoDbContextHelper.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using MongoDB.Driver; +using Volo.Abp.Domain.Entities; +using Volo.Abp.Reflection; + +namespace Volo.Abp.MongoDB +{ + internal static class MongoDbContextHelper + { + public static IEnumerable GetEntityTypes(Type dbContextType) + { + return + from property in dbContextType.GetTypeInfo().GetProperties(BindingFlags.Public | BindingFlags.Instance) + where + ReflectionHelper.IsAssignableToGenericType(property.PropertyType, typeof(IMongoCollection<>)) && + typeof(IEntity).IsAssignableFrom(property.PropertyType.GenericTypeArguments[0]) + select property.PropertyType.GenericTypeArguments[0]; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoDbContextModel.cs b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoDbContextModel.cs new file mode 100644 index 0000000000..cc4d25f26c --- /dev/null +++ b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoDbContextModel.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; + +namespace Volo.Abp.MongoDB +{ + public class MongoDbContextModel + { + public IReadOnlyDictionary Entities { get; } + + public MongoDbContextModel(IReadOnlyDictionary entities) + { + Entities = entities; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoEntityMapping.cs b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoEntityMapping.cs deleted file mode 100644 index 8b7c421ae9..0000000000 --- a/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoEntityMapping.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; - -namespace Volo.Abp.MongoDB -{ - public class MongoEntityMapping - { - public Type EntityType { get; set; } - - public string CollectionName { get; set; } - - public MongoEntityMapping(Type entityType, string collectionName = null) - { - EntityType = entityType; - CollectionName = collectionName ?? entityType.Name; - } - } -} \ No newline at end of file diff --git a/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoEntityModelBuilder.cs b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoEntityModelBuilder.cs new file mode 100644 index 0000000000..028ce84cde --- /dev/null +++ b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoEntityModelBuilder.cs @@ -0,0 +1,18 @@ +using System; + +namespace Volo.Abp.MongoDB +{ + public class MongoEntityModelBuilder : IMongoEntityModel + { + public Type EntityType { get; } + + public string CollectionName { get; set; } + + public MongoEntityModelBuilder(Type entityType) + { + Check.NotNull(entityType, nameof(entityType)); + + EntityType = entityType; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoModelBuilder.cs b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoModelBuilder.cs new file mode 100644 index 0000000000..7550cf7c6b --- /dev/null +++ b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoModelBuilder.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using JetBrains.Annotations; + +namespace Volo.Abp.MongoDB +{ + public class MongoModelBuilder : IMongoModelBuilder + { + private readonly Dictionary _entityModelBuilders; + + public MongoModelBuilder() + { + _entityModelBuilders = new Dictionary(); + } + + public MongoDbContextModel Build() + { + var entityModels = _entityModelBuilders + .Select(x => x.Value) + .ToImmutableDictionary(x => x.EntityType, x => (IMongoEntityModel) x); + + return new MongoDbContextModel(entityModels); + } + + public virtual void Entity([NotNull] Action buildAction) + { + Entity(typeof(TEntity), buildAction); + } + + public virtual void Entity([NotNull] Type entityType, [NotNull] Action buildAction) + { + Check.NotNull(entityType, nameof(entityType)); + Check.NotNull(buildAction, nameof(buildAction)); + + var model = _entityModelBuilders.GetOrAdd(entityType, () => new MongoEntityModelBuilder(entityType)); + buildAction(model); + } + + public virtual IReadOnlyList GetEntities() + { + return _entityModelBuilders.Values.ToImmutableList(); + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoModelBuilderConfigurationOptions.cs b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoModelBuilderConfigurationOptions.cs new file mode 100644 index 0000000000..85fd1c8802 --- /dev/null +++ b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoModelBuilderConfigurationOptions.cs @@ -0,0 +1,26 @@ +using JetBrains.Annotations; + +namespace Volo.Abp.MongoDB +{ + public class MongoModelBuilderConfigurationOptions + { + [NotNull] + public string CollectionPrefix + { + get => _collectionPrefix; + set + { + Check.NotNull(value, nameof(value), $"{nameof(CollectionPrefix)} can not be null! Set to empty string if you don't want a collection prefix."); + _collectionPrefix = value; + } + } + private string _collectionPrefix; + + public MongoModelBuilderConfigurationOptions([NotNull] string collectionPrefix = "") + { + Check.NotNull(collectionPrefix, nameof(collectionPrefix)); + + CollectionPrefix = collectionPrefix; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoModelSource.cs b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoModelSource.cs new file mode 100644 index 0000000000..c5cb0da1c8 --- /dev/null +++ b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoModelSource.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Concurrent; +using System.Linq; +using System.Reflection; +using MongoDB.Driver; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Entities; +using Volo.Abp.Reflection; + +namespace Volo.Abp.MongoDB +{ + public class MongoModelSource : IMongoModelSource, ISingletonDependency + { + protected readonly ConcurrentDictionary ModelCache = new ConcurrentDictionary(); + + public virtual MongoDbContextModel GetModel(AbpMongoDbContext dbContext) + { + return ModelCache.GetOrAdd( + dbContext.GetType(), + _ => CreateModel(dbContext) + ); + } + + protected virtual MongoDbContextModel CreateModel(AbpMongoDbContext dbContext) + { + var modelBuilder = CreateModelBuilder(); + BuildModelFromDbContextType(modelBuilder, dbContext.GetType()); + BuildModelFromDbContextInstance(modelBuilder, dbContext); + return modelBuilder.Build(); + } + + protected virtual MongoModelBuilder CreateModelBuilder() + { + return new MongoModelBuilder(); + } + + protected virtual void BuildModelFromDbContextType(IMongoModelBuilder modelBuilder, Type dbContextType) + { + var collectionProperties = + from property in dbContextType.GetTypeInfo().GetProperties(BindingFlags.Public | BindingFlags.Instance) + where + ReflectionHelper.IsAssignableToGenericType(property.PropertyType, typeof(IMongoCollection<>)) && + typeof(IEntity).IsAssignableFrom(property.PropertyType.GenericTypeArguments[0]) + select property; + + foreach (var collectionProperty in collectionProperties) + { + BuildModelFromDbContextCollectionProperty(modelBuilder, collectionProperty); + } + } + + protected virtual void BuildModelFromDbContextCollectionProperty(IMongoModelBuilder modelBuilder, PropertyInfo collectionProperty) + { + var entityType = collectionProperty.PropertyType.GenericTypeArguments[0]; + var collectionAttribute = collectionProperty.GetCustomAttributes().OfType().FirstOrDefault(); + + modelBuilder.Entity(entityType, b => + { + b.CollectionName = collectionAttribute?.CollectionName ?? collectionProperty.Name; + }); + } + + protected virtual void BuildModelFromDbContextInstance(IMongoModelBuilder modelBuilder, AbpMongoDbContext dbContext) + { + dbContext.CreateModel(modelBuilder); + } + } +} \ No newline at end of file diff --git a/test/Volo.Abp.MongoDB.Tests/Volo/Abp/TestApp/MongoDb/TestAppMongoDbContext.cs b/test/Volo.Abp.MongoDB.Tests/Volo/Abp/TestApp/MongoDb/TestAppMongoDbContext.cs index 8245676f24..8f18aec5ed 100644 --- a/test/Volo.Abp.MongoDB.Tests/Volo/Abp/TestApp/MongoDb/TestAppMongoDbContext.cs +++ b/test/Volo.Abp.MongoDB.Tests/Volo/Abp/TestApp/MongoDb/TestAppMongoDbContext.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using MongoDB.Driver; +using MongoDB.Driver; using Volo.Abp.Data; using Volo.Abp.MongoDB; using Volo.Abp.TestApp.Domain; @@ -9,23 +8,19 @@ namespace Volo.Abp.TestApp.MongoDb [ConnectionStringName("TestApp")] public class TestAppMongoDbContext : AbpMongoDbContext, ITestAppMongoDbContext { - //TODO: We can set collections automatically, lik EF Core - //TODO: We can get collection names conventionally, or by an attribute - + [MongoCollection("Persons")] //Intentially changed the collection name to test it public IMongoCollection People => Collection(); public IMongoCollection Cities => Collection(); - //TODO: Default implementation should read IMongoCollections from the context! - //GetMappings should send a context and we add to it. Rename to ConfigureMappings. - - public override IReadOnlyList GetMappings() + protected override void CreateModel(IMongoModelBuilder modelBuilder) { - return new[] + base.CreateModel(modelBuilder); + + modelBuilder.Entity(b => { - new MongoEntityMapping(typeof(Person), "People"), - new MongoEntityMapping(typeof(City), "Cities") - }; + b.CollectionName = "MyCities"; + }); } } }