├── .dockerignore ├── .gitattributes ├── .gitignore ├── .travis.yml ├── Blueshift.EntityFrameworkCore.sln ├── Blueshift.EntityFrameworkCore.sln.DotSettings ├── Directory.Build.props ├── Directory.Build.targets ├── LICENSE.txt ├── NuGet.config ├── README.md ├── appveyor.yml ├── build.cmd ├── build.sh ├── build ├── dependencies.props ├── repo.beforecommon.props ├── repo.props ├── repo.targets └── sources.props ├── docker-compose.dcproj ├── docker-compose.yml ├── korebuild-lock.txt ├── korebuild.json ├── run.cmd ├── run.ps1 ├── run.sh ├── src ├── Blueshift.EntityFrameworkCore.MongoDB.SampleDomain │ ├── Blueshift.EntityFrameworkCore.MongoDB.SampleDomain.csproj │ ├── ZooDbContext.cs │ ├── ZooDbDependencyInjection.cs │ ├── ZooEntityFixture.cs │ └── _Comparers.cs ├── Blueshift.EntityFrameworkCore.MongoDB │ ├── Adapter │ │ ├── Conventions │ │ │ ├── AbstractBaseClassConvention.cs │ │ │ ├── BsonClassMapAttributeConvention.cs │ │ │ ├── BsonMemberMapAttributeConvention.cs │ │ │ ├── IgnoreEmptyEnumerablesConvention.cs │ │ │ ├── IgnoreNullOrEmptyStringsConvention.cs │ │ │ ├── KeyAttributeConvention.cs │ │ │ ├── NavigationSrializationMemberMapConvention.cs │ │ │ └── NotMappedAttributeConvention.cs │ │ ├── EntityFrameworkConventionPack.cs │ │ ├── Serialization │ │ │ ├── BsonSerializerExtensions.cs │ │ │ ├── DenormalizingBsonClassMapSerializer.cs │ │ │ └── NavigationBsonMemberMapSerializer.cs │ │ └── Update │ │ │ ├── DeleteOneModelFactory.cs │ │ │ ├── IMongoDbWriteModelFactory.cs │ │ │ ├── IMongoDbWriteModelFactoryCache.cs │ │ │ ├── IMongoDbWriteModelFactorySelector.cs │ │ │ ├── InsertOneModelFactory.cs │ │ │ ├── MongoDbWriteModelFactory.cs │ │ │ ├── MongoDbWriteModelFactoryCache.cs │ │ │ ├── MongoDbWriteModelFactorySelector.cs │ │ │ └── ReplaceOneModelFactory.cs │ ├── Annotations │ │ ├── DenormalizeAttribute.cs │ │ ├── MongoCollectionAttribute.cs │ │ └── MongoDatabaseAttribute.cs │ ├── Blueshift.EntityFrameworkCore.MongoDB.csproj │ ├── ChangeTracking │ │ └── MongoDbInternalEntityEntryFactory.cs │ ├── DbContextOptionsExtensions.cs │ ├── DependencyInjection │ │ └── MongoDbEfServiceCollectionExtensions.cs │ ├── Infrastructure │ │ ├── EntityFrameworkMongoDbServicesBuilder.cs │ │ ├── MongoDbContextOptionsBuilder.cs │ │ ├── MongoDbContextOptionsBuilderExtensions.cs │ │ ├── MongoDbModelValidator.cs │ │ └── MongoDbOptionsExtension.cs │ ├── ListExtensions.cs │ ├── Metadata │ │ ├── Builders │ │ │ ├── DocumentDbInternalMetadataBuilderExtensions.cs │ │ │ ├── DocumentEntityTypeBuilderExtensions.cs │ │ │ ├── DocumentInternalKeyBuilderExtensions.cs │ │ │ ├── MongoDbConventionSetBuilder.cs │ │ │ ├── MongoDbConventionSetBuilderDependencies.cs │ │ │ ├── MongoDbEntityTypeBuilderExtensions.cs │ │ │ ├── MongoDbInternalMetadataBuilderExtensions.cs │ │ │ └── MongoDbModelBuilderExtensions.cs │ │ ├── Conventions │ │ │ ├── BsonDiscriminatorAttributeConvention.cs │ │ │ ├── BsonIgnoreAttributeConvention.cs │ │ │ ├── BsonKnownTypesAttributeConvention.cs │ │ │ ├── BsonRequiredAttributeConvention.cs │ │ │ ├── DocumentPropertyMappingValidationConvention.cs │ │ │ ├── MongoCollectionAttributeConvention.cs │ │ │ ├── MongoDatabaseConvention.cs │ │ │ ├── MongoDbDatabaseGeneratedAttributeConvention.cs │ │ │ ├── MongoDbKeyAttributeConvention.cs │ │ │ ├── MongoDbRelationshipDiscoveryConvention.cs │ │ │ └── OwnedDocumentConvention.cs │ │ ├── DocumentAnnotationNames.cs │ │ ├── DocumentAnnotations.cs │ │ ├── DocumentEntityTypeAnnotations.cs │ │ ├── DocumentKeyAnnotations.cs │ │ ├── MongoDbAnnotationNames.cs │ │ ├── MongoDbEntityTypeAnnotations.cs │ │ └── MongoDbModelAnnotations.cs │ ├── MethodHelper.cs │ ├── MongoDbUtilities.cs │ ├── ObjectIdTypeConverter.cs │ ├── Properties │ │ ├── AssemblyInfo.cs │ │ ├── Blueshift.EntityFrameworkCore.MongoDB.rd.xml │ │ ├── DocumentDbStrings.Designer.cs │ │ ├── DocumentDbStrings.cs │ │ ├── DocumentDbStrings.resx │ │ └── DocumentDbStrings.tt │ ├── Query │ │ ├── EntityLoadInfoFactory.cs │ │ ├── ExpressionVisitors │ │ │ ├── DocumentNavigationRewritingExpressionVisitor.cs │ │ │ ├── DocumentNavigationRewritingExpressionVisitorFactory.cs │ │ │ ├── IMongoDbDenormalizedCollectionCompensatingVisitorFactory.cs │ │ │ ├── MongoDbDenormalizedCollectionCompensatingVisitor.cs │ │ │ ├── MongoDbDenormalizedCollectionCompensatingVisitorFactory.cs │ │ │ ├── MongoDbEntityQueryableExpressionVisitor.cs │ │ │ ├── MongoDbEntityQueryableExpressionVisitorFactory.cs │ │ │ ├── MongoDbMemberAccessBindingExpressionVisitor.cs │ │ │ └── MongoDbMemberAccessBindingExpressionVisitorFactory.cs │ │ ├── Expressions │ │ │ ├── DocumentQueryExpression.cs │ │ │ ├── IDocumentQueryExpressionFactory.cs │ │ │ └── MongoDbDocumentQueryExpressionFactory.cs │ │ ├── IEntityLoadInfoFactory.cs │ │ ├── IValueBufferFactory.cs │ │ ├── LinqQueryCompilationContextFactory.cs │ │ ├── MongoDbEntityQueryModelVisitor.cs │ │ ├── MongoDbEntityQueryModelVisitorDependencies.cs │ │ ├── MongoDbEntityQueryModelVisitorFactory.cs │ │ ├── MongoDbQueryBuffer.cs │ │ ├── MongoDbQueryContext.cs │ │ ├── MongoDbQueryContextFactory.cs │ │ ├── QueryableLinqOperatorProvider.cs │ │ └── ValueBufferFactory.cs │ ├── Storage │ │ ├── IMongoDbConnection.cs │ │ ├── IMongoDbTypeMappingSource.cs │ │ ├── MongoDbConnection.cs │ │ ├── MongoDbDatabase.cs │ │ ├── MongoDbDatabaseCreator.cs │ │ └── MongoDbTypeMappingSource.cs │ └── ValueGeneration │ │ ├── HashCodeValueGenerator.cs │ │ ├── IntegerValueGenerator.cs │ │ ├── MongoDbValueGeneratorSelector.cs │ │ └── ObjectIdValueGenerator.cs ├── Blueshift.Identity.MongoDB │ ├── Blueshift.Identity.MongoDB.csproj │ ├── DependencyInjection │ │ └── IdentityEntityFrameworkMongoDbBuilderExtensions.cs │ ├── IdentityMongoDbContext.cs │ ├── MongoDbIdentityClaim.cs │ ├── MongoDbIdentityRole.cs │ ├── MongoDbIdentityUser.cs │ ├── MongoDbIdentityUserLogin.cs │ ├── MongoDbIdentityUserRole.cs │ ├── MongoDbIdentityUserToken.cs │ ├── MongoDbRoleStore.cs │ └── MongoDbUserStore.cs ├── Directory.Build.props └── Shared │ ├── Check.cs │ ├── CodeAnnotations.cs │ ├── MemberInfoExtensions.cs │ ├── PropertyInfoExtensions.cs │ ├── SharedTypeExtensions.cs │ └── StringBuilderExtensions.cs ├── test ├── Blueshift.EntityFrameworkCore.MongoDB.Tests │ ├── Adapter │ │ ├── Conventions │ │ │ ├── AbstractClassConventionTest.cs │ │ │ ├── IgnoreEmptyEnumerablesConventionTests.cs │ │ │ ├── IgnoreNullOrEmptyStringsConventionTests.cs │ │ │ └── KeyAttributeConventionTests.cs │ │ ├── EntityFrameworkConventionPackTests.cs │ │ ├── Serialization │ │ │ ├── BsonSerializerExtensionsTests.cs │ │ │ └── DenormalizingBsonClassMapSerializerTests.cs │ │ └── Update │ │ │ └── MongoDbWriteModelFactoryTests.cs │ ├── ApiConsistencyTest.cs │ ├── Blueshift.EntityFrameworkCore.MongoDB.Tests.csproj │ ├── Metadata │ │ ├── Conventions │ │ │ └── MongoDatabaseConventionTests.cs │ │ ├── MongoDbEntityTypeAnnotationsTests.cs │ │ └── MongoDbModelAnnotationsTests.cs │ ├── MongoDbContextTestBase.cs │ ├── MongoDbContextTests.cs │ ├── MongoDbUtilitiesTests.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Storage │ │ ├── MongoDbConnectionTests.cs │ │ ├── MongoDbDatabaseCreatorTests.cs │ │ ├── MongoDbDatabaseTests.cs │ │ └── MongoDbTypeMappingSourceTests.cs │ ├── ValueGeneration │ │ ├── MongoDbValueGeneratorSelectorTests.cs │ │ └── ObjectIdValueGeneratorTests.cs │ └── xunit.runner.json ├── Blueshift.Identity.MongoDB.Tests │ ├── Blueshift.Identity.MongoDB.Tests.csproj │ ├── MongoDbIdentityFixture.cs │ ├── MongoDbIdentityTestBase.cs │ ├── MongoDbIdentityTestCollection.cs │ ├── MongoDbRoleClaimStoreTests.cs │ ├── MongoDbRoleStoreTests.cs │ ├── MongoDbUserAuthenticationTokenStoreTests.cs │ ├── MongoDbUserAuthenticatorKeyStoreTests.cs │ ├── MongoDbUserClaimStoreTests.cs │ ├── MongoDbUserEmailStoreTests.cs │ ├── MongoDbUserLockoutStoreTests.cs │ ├── MongoDbUserLoginStoreTests.cs │ ├── MongoDbUserPasswordStoreTests.cs │ ├── MongoDbUserPhoneNumberStoreTests.cs │ ├── MongoDbUserRoleStoreTests.cs │ ├── MongoDbUserSecurityStampStoreTests.cs │ ├── MongoDbUserStoreTests.cs │ ├── MongoDbUserTwoFactorRecoveryCodeStoreTests.cs │ ├── MongoDbUserTwoFactorStoreTests.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── _Comparers.cs │ └── xunit.runner.json └── Directory.Build.props └── version.props /.dockerignore: -------------------------------------------------------------------------------- 1 | .dockerignore 2 | .env 3 | .git 4 | .gitignore 5 | .vs 6 | .vscode 7 | docker-compose.yml 8 | docker-compose.*.yml 9 | */bin 10 | */obj 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.sh text eol=lf 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: csharp 2 | sudo: false 3 | dotnet: 2.1.2 4 | dist: trusty 5 | env: 6 | global: 7 | - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true 8 | - DOTNET_CLI_TELEMETRY_OPTOUT: 1 9 | mono: none 10 | services: 11 | - mongodb 12 | os: 13 | - linux 14 | # - osx 15 | osx_image: xcode8.1 16 | addons: 17 | apt: 18 | packages: 19 | - libunwind8 20 | branches: 21 | only: 22 | - master 23 | - release 24 | - develop 25 | - /^rel\/.*/ 26 | - /^(.*\/)?ci-.*$/ 27 | script: 28 | - ./build.sh -------------------------------------------------------------------------------- /Blueshift.EntityFrameworkCore.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | $([System.DateTime]::Now.ToString('yyyy')) 8 | git 9 | $(MSBuildThisFileDirectory) 10 | https://github.com/crhairr/EntityFrameworkCore.MongoDb.git 11 | 14 | True 15 | Blueshift Software, LLC 16 | $(Company) 17 | © $(Company) @ $(BuildYear) - all rights reserved. 18 | Blueshift;MongoDB;Entity Framework Core;entity-framework-core;EF;Data;O/RM 19 | Blueshift Software MongoDB Provider for Entity Framework Core 20 | full 21 | 7.2 22 | $(NoWarn);CS8032; 23 | 24 | $(MSBuildProjectName) 25 | $(MSBuildProjectName) 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Directory.Build.targets: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(MicrosoftNETCoreApp20PackageVersion) 5 | $(NETStandardLibrary20PackageVersion) 6 | 7 | 99.9 8 | 9 | 10 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Blueshift Software, LLC. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /NuGet.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repository has been defunct for some time. Due to a lack of both public interest and general support from the Microsoft Entity Framework team, I have decided to formally end support for the solution and archive the repository. 2 | 3 | 4 | # Document Database Providers for Entity Framework Core 5 | 6 | Welcome to the home of Document Database (NoSQL) Providers for EntityFrameworkCore! 7 | 8 | [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/iip86emc94ncp0ao?svg=true&style=flat-square)](https://ci.appveyor.com/project/crhairr/entityframeworkcore-mongodb/) [![Travis CI Build Status](https://travis-ci.org/BlueshiftSoftware/EntityFrameworkCore.svg?branch=develop&label=travis-ci&style=flat-square)](https://travis-ci.org/BlueshiftSoftware/EntityFrameworkCore) 9 | 10 | This repository currently only contains a MongoDB provider for EF Core. However, there are plans in the current roadmap to expand this with further NoSQL provider offerings. 11 | 12 | MongoDb is a highly popular No-SQL database solution for storing structured, non-relational document data. This provider enables applications built with EntityFrameworkCore to use MongoDb instances as a backing data store. 13 | 14 | Find out how to get started by visiting the [Wiki pages](https://github.com/crhairr/EntityFrameworkCore.MongoDb/wiki). Feel free to contribute to this repository with code, comments, wiki entries, and/or issues. 15 | 16 | The latest release and CI previews builds are available at the EntityFrameworkCore.MongoDb [MyGet Feed](https://www.myget.org/gallery/efcore-mongodb/). 17 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | init: 2 | - git config --global core.autocrlf true 3 | clone_depth: 1 4 | test: off 5 | services: 6 | - mongodb 7 | environment: 8 | global: 9 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true 10 | DOTNET_CLI_TELEMETRY_OPTOUT: 1 11 | matrix: 12 | fast_finish: true 13 | for: 14 | - 15 | matrix: 16 | only: 17 | - image: Ubuntu 18 | build_script: 19 | - sh: chmod +x ./run.sh 20 | - sh: ./run.sh default-build 21 | - 22 | matrix: 23 | only: 24 | - image: Visual Studio 2017 25 | build_script: 26 | - ps: .\run.ps1 default-build 27 | artifacts: 28 | - path: 'artifacts\build\*.nupkg' 29 | name: MyGet 30 | deploy: 31 | - provider: Environment 32 | name: MyGet 33 | artifact: 'artifacts\build\*.nupkg' 34 | on: 35 | APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 36 | image: 37 | - Ubuntu 38 | - Visual Studio 2017 39 | -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | PowerShell -NoProfile -NoLogo -ExecutionPolicy unrestricted -Command "[System.Threading.Thread]::CurrentThread.CurrentCulture = ''; [System.Threading.Thread]::CurrentThread.CurrentUICulture = '';& '%~dp0run.ps1' default-build %*; exit $LASTEXITCODE" 3 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 5 | 6 | # Call "sync" between "chmod" and execution to prevent "text file busy" error in Docker (aufs) 7 | chmod +x "$DIR/run.sh"; sync 8 | "$DIR/run.sh" default-build "$@" 9 | -------------------------------------------------------------------------------- /build/dependencies.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 5 | 6 | 7 | 8 | 2.1.1 9 | 2.0.3 10 | 11 | 12 | 13 | 2.1.3 14 | 2.1.2 15 | 2.1.2 16 | 2.1.1 17 | 18 | 19 | 20 | 2.7.3 21 | 22 | 23 | 24 | 15.9.0 25 | 4.10.1 26 | 2.4.1 27 | 2.4.1 28 | 2.4.1 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /build/repo.beforecommon.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | true 5 | 6 | -------------------------------------------------------------------------------- /build/repo.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | False 5 | 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /build/repo.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /build/sources.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $(DotNetRestoreSources) 6 | 7 | $(RestoreSources); 8 | https://dotnet.myget.org/F/dotnet-core/api/v3/index.json; 9 | https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json; 10 | https://dotnet.myget.org/F/aspnetcore-tools/api/v3/index.json; 11 | https://www.myget.org/F/efcore-mongodb/api/v3/index.json; 12 | 13 | 14 | $(RestoreSources); 15 | https://api.nuget.org/v3/index.json; 16 | 17 | 18 | -------------------------------------------------------------------------------- /docker-compose.dcproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2.1 4 | Linux 5 | 04ceb217-71e2-4dbd-b35a-737ee7d258b8 6 | LaunchBrowser 7 | {Scheme}://localhost:{ServicePort} 8 | blueshift.authoring 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.4' 2 | 3 | services: 4 | mongo-efcore: 5 | image: mongo:4.1.2-xenial 6 | restart: always 7 | ports: 8 | - 27017:27017 9 | mongo-efcore-express: 10 | image: mongo-express 11 | restart: always 12 | ports: 13 | - 27027:8081 14 | environment: 15 | ME_CONFIG_MONGODB_SERVER: mongo-efcore 16 | -------------------------------------------------------------------------------- /korebuild-lock.txt: -------------------------------------------------------------------------------- 1 | version:2.1.3-rtm-15802 2 | commithash:a7c08b45b440a7d2058a0aa1eaa3eb6ba811976a -------------------------------------------------------------------------------- /korebuild.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/aspnet/BuildTools/release/2.1/tools/korebuild.schema.json", 3 | "channel": "release/2.1" 4 | } -------------------------------------------------------------------------------- /run.cmd: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | PowerShell -NoProfile -NoLogo -ExecutionPolicy unrestricted -Command "[System.Threading.Thread]::CurrentThread.CurrentCulture = ''; [System.Threading.Thread]::CurrentThread.CurrentUICulture = '';& '%~dp0run.ps1' %*; exit $LASTEXITCODE" 3 | -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB.SampleDomain/Blueshift.EntityFrameworkCore.MongoDB.SampleDomain.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(BuildFrameworks) 5 | Blueshift MongoDb Provider for EntityFrameworkCore 6 | Sample Domain Library for EntityFramework Core MongoDb Provider 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB.SampleDomain/ZooDbDependencyInjection.cs: -------------------------------------------------------------------------------- 1 | using Blueshift.EntityFrameworkCore.MongoDB.Infrastructure; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace Blueshift.EntityFrameworkCore.MongoDB.SampleDomain 5 | { 6 | public static class ZooDbDependencyInjection 7 | { 8 | public static IServiceCollection AddZooDbContext(this IServiceCollection serviceCollection) 9 | { 10 | return serviceCollection 11 | .AddDbContext(options => options.UseMongoDb("mongodb://localhost")); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Adapter/Conventions/AbstractBaseClassConvention.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Utilities; 2 | using MongoDB.Bson.Serialization; 3 | using MongoDB.Bson.Serialization.Attributes; 4 | using MongoDB.Bson.Serialization.Conventions; 5 | 6 | namespace Blueshift.EntityFrameworkCore.MongoDB.Adapter.Conventions 7 | { 8 | /// 9 | /// 10 | /// 11 | /// A convention that specifies that a discriminator is required when the given type is abstract. 12 | /// 13 | public class AbstractBaseClassConvention : BsonClassMapAttributeConvention 14 | { 15 | /// 16 | /// 17 | /// Process the conventions on according to the given . 18 | /// 19 | /// The to which the conventions will be assigned. 20 | /// The that defines the convention. 21 | protected override void Apply(BsonClassMap classMap, BsonKnownTypesAttribute attribute) 22 | { 23 | Check.NotNull(classMap, nameof(classMap)); 24 | if (!classMap.DiscriminatorIsRequired) 25 | { 26 | classMap.SetDiscriminatorIsRequired(classMap.ClassType.IsAbstract); 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Adapter/Conventions/BsonClassMapAttributeConvention.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Text.RegularExpressions; 5 | using JetBrains.Annotations; 6 | using Microsoft.EntityFrameworkCore.Utilities; 7 | using MongoDB.Bson.Serialization; 8 | using MongoDB.Bson.Serialization.Conventions; 9 | 10 | namespace Blueshift.EntityFrameworkCore.MongoDB.Adapter.Conventions 11 | { 12 | /// 13 | /// 14 | /// 15 | /// Base class for attribute-based convention processing. 16 | /// 17 | /// The type of attribute to process. 18 | public abstract class BsonClassMapAttributeConvention : ConventionBase, IClassMapConvention 19 | where TAttribute : Attribute 20 | { 21 | /// 22 | /// 23 | /// Initializes a new instance of the . 24 | /// 25 | protected BsonClassMapAttributeConvention() 26 | : base(Regex.Replace(typeof(TAttribute).Name, "Attribute$", "")) 27 | { 28 | } 29 | 30 | 31 | /// 32 | /// 33 | /// Processes each defined on the given 34 | /// member info and 35 | /// 36 | /// The to 37 | public virtual void Apply(BsonClassMap classMap) 38 | { 39 | Check.NotNull(classMap, nameof(classMap)); 40 | IEnumerable memberMapAttributes = classMap 41 | .ClassType 42 | .GetCustomAttributes(); 43 | foreach (TAttribute attribute in memberMapAttributes) 44 | { 45 | Apply(classMap, attribute); 46 | } 47 | } 48 | 49 | /// 50 | /// Process the conventions on according to the given . 51 | /// 52 | /// The to which the conventions will be assigned. 53 | /// The that defines the convention. 54 | protected abstract void Apply([NotNull] BsonClassMap classMap, [NotNull] TAttribute attribute); 55 | } 56 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Adapter/Conventions/BsonMemberMapAttributeConvention.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Text.RegularExpressions; 5 | using JetBrains.Annotations; 6 | using Microsoft.EntityFrameworkCore.Utilities; 7 | using MongoDB.Bson.Serialization; 8 | using MongoDB.Bson.Serialization.Conventions; 9 | 10 | namespace Blueshift.EntityFrameworkCore.MongoDB.Adapter.Conventions 11 | { 12 | /// 13 | /// Base class for attribute-based convention processing. 14 | /// 15 | /// The type of attribute to process. 16 | public abstract class BsonMemberMapAttributeConvention : ConventionBase, IMemberMapConvention 17 | where TAttribute : Attribute 18 | { 19 | /// 20 | /// Initializes a new instance of the . 21 | /// 22 | protected BsonMemberMapAttributeConvention() 23 | : base(Regex.Replace(typeof(TAttribute).Name, "Attribute$", "")) 24 | { 25 | } 26 | 27 | /// 28 | /// Processes each defined on the given 29 | /// member info and 30 | /// 31 | /// The to 32 | public virtual void Apply([NotNull] BsonMemberMap memberMap) 33 | { 34 | Check.NotNull(memberMap, nameof(memberMap)); 35 | IEnumerable memberMapAttributes = memberMap.MemberInfo 36 | .GetCustomAttributes(); 37 | foreach (TAttribute attribute in memberMapAttributes) 38 | { 39 | Apply(memberMap, attribute); 40 | } 41 | } 42 | 43 | /// 44 | /// Process the conventions on according to the given . 45 | /// 46 | /// The to which the conventions will be assigned. 47 | /// The that defines the convention. 48 | protected abstract void Apply([NotNull] BsonMemberMap memberMap, [NotNull] TAttribute attribute); 49 | } 50 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Adapter/Conventions/IgnoreEmptyEnumerablesConvention.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Text.RegularExpressions; 4 | using JetBrains.Annotations; 5 | using Microsoft.EntityFrameworkCore.Utilities; 6 | using MongoDB.Bson.Serialization; 7 | using MongoDB.Bson.Serialization.Conventions; 8 | 9 | namespace Blueshift.EntityFrameworkCore.MongoDB.Adapter.Conventions 10 | { 11 | /// 12 | /// A convention that ignores empty instances when serializing Bson documents. 13 | /// 14 | public class IgnoreEmptyEnumerablesConvention : ConventionBase, IMemberMapConvention 15 | { 16 | /// 17 | /// Initializes a new instance of the class. 18 | /// 19 | public IgnoreEmptyEnumerablesConvention() 20 | : base(Regex.Replace(nameof(IgnoreEmptyEnumerablesConvention), "Convention$", "")) 21 | { 22 | } 23 | 24 | /// 25 | /// Applies the Ignore Empty Enumerables convention to the given . 26 | /// 27 | /// The to which the convention will be applied. 28 | public virtual void Apply([NotNull] BsonMemberMap memberMap) 29 | { 30 | Check.NotNull(memberMap, nameof(memberMap)); 31 | if (memberMap.MemberType.TryGetSequenceType() != null) 32 | { 33 | memberMap.SetShouldSerializeMethod(@object => 34 | { 35 | object value = memberMap.Getter(@object); 36 | return (value as IEnumerable)?.GetEnumerator().MoveNext() ?? false; 37 | }); 38 | } 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Adapter/Conventions/IgnoreNullOrEmptyStringsConvention.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using JetBrains.Annotations; 3 | using Microsoft.EntityFrameworkCore.Utilities; 4 | using MongoDB.Bson.Serialization; 5 | using MongoDB.Bson.Serialization.Conventions; 6 | 7 | namespace Blueshift.EntityFrameworkCore.MongoDB.Adapter.Conventions 8 | { 9 | /// 10 | /// Instructs the MongoDb C# driver to ignore null, empty, or default values of properties. 11 | /// 12 | public class IgnoreNullOrEmptyStringsConvention : ConventionBase, IMemberMapConvention 13 | { 14 | /// 15 | /// Initializes a new instance of the class. 16 | /// 17 | public IgnoreNullOrEmptyStringsConvention() 18 | : base(Regex.Replace(nameof(IgnoreNullOrEmptyStringsConvention), "Convention$", "")) 19 | { 20 | } 21 | 22 | /// 23 | /// Applies the Ignore Null or Empty Strings convention to the given . 24 | /// 25 | /// The to which the convention will be applied. 26 | public virtual void Apply([NotNull] BsonMemberMap memberMap) 27 | { 28 | Check.NotNull(memberMap, nameof(memberMap)); 29 | if (memberMap.MemberType == typeof(string)) 30 | { 31 | SetShouldSerializeMethod(memberMap); 32 | } 33 | } 34 | 35 | private static void SetShouldSerializeMethod(BsonMemberMap memberMap) 36 | { 37 | var defaultString = memberMap.DefaultValue as string; 38 | if (!string.IsNullOrEmpty(defaultString)) 39 | { 40 | ShouldSerializeIfNotDefault(memberMap, defaultString); 41 | } 42 | else 43 | { 44 | ShouldSerializeIfNotEmpty(memberMap); 45 | } 46 | } 47 | 48 | private static void ShouldSerializeIfNotEmpty(BsonMemberMap memberMap) 49 | => memberMap.SetShouldSerializeMethod(@object => !string.IsNullOrEmpty(memberMap.Getter(@object) as string)); 50 | 51 | private static void ShouldSerializeIfNotDefault(BsonMemberMap memberMap, string defaultString) 52 | => memberMap.SetShouldSerializeMethod(@object => !string.Equals(defaultString, memberMap.Getter(@object) as string)); 53 | } 54 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Adapter/Conventions/KeyAttributeConvention.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using Microsoft.EntityFrameworkCore.Utilities; 3 | using MongoDB.Bson.Serialization; 4 | 5 | namespace Blueshift.EntityFrameworkCore.MongoDB.Adapter.Conventions 6 | { 7 | /// 8 | /// A convention that sets the of a 9 | /// if that property has been decorated with a . 10 | /// 11 | public class KeyAttributeConvention : BsonMemberMapAttributeConvention 12 | { 13 | /// 14 | /// Applies the Key Attribute convention to the given . 15 | /// 16 | /// The to which the convention will be applied. 17 | /// The to apply. 18 | protected override void Apply(BsonMemberMap memberMap, KeyAttribute attribute) 19 | => Check.NotNull(memberMap, nameof(memberMap)) 20 | .ClassMap 21 | .SetIdMember(memberMap); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Adapter/Conventions/NavigationSrializationMemberMapConvention.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.Linq; 4 | using System.Reflection; 5 | using Blueshift.EntityFrameworkCore.MongoDB.Adapter.Serialization; 6 | using Microsoft.EntityFrameworkCore.Utilities; 7 | using MongoDB.Bson.Serialization; 8 | using MongoDB.Bson.Serialization.Attributes; 9 | using MongoDB.Bson.Serialization.Conventions; 10 | 11 | namespace Blueshift.EntityFrameworkCore.MongoDB.Adapter.Conventions 12 | { 13 | /// 14 | /// 15 | /// 16 | /// A convention for specifying how to serialize navigation properties. 17 | /// 18 | public class NavigationSrializationMemberMapConvention : ConventionBase, IMemberMapConvention 19 | { 20 | /// 21 | /// 22 | /// Checks whether the member map represents a navigation, and sets the member map's serializer. 23 | /// 24 | /// The member map. 25 | public void Apply(BsonMemberMap memberMap) 26 | { 27 | Type memberTargetType = Check.NotNull(memberMap, nameof(memberMap)).MemberType.TryGetSequenceType() 28 | ?? memberMap.MemberType; 29 | if (!memberTargetType.IsPrimitive && HasIdMember(memberTargetType)) 30 | { 31 | IBsonSerializer memberMapSerializer = (IBsonSerializer) Activator.CreateInstance( 32 | typeof(NavigationBsonMemberMapSerializer<>).MakeGenericType(memberTargetType), 33 | memberMap); 34 | memberMap.SetSerializer(memberMapSerializer); 35 | } 36 | } 37 | 38 | private bool HasIdMember(Type type) 39 | => !type.IsPrimitive 40 | && type 41 | .GetMembers(BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance) 42 | .Any(memberInfo => memberInfo.IsDefined(typeof(BsonIdAttribute)) 43 | || memberInfo.IsDefined(typeof(KeyAttribute))); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Adapter/Conventions/NotMappedAttributeConvention.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations.Schema; 2 | using Microsoft.EntityFrameworkCore.Utilities; 3 | using MongoDB.Bson.Serialization; 4 | 5 | namespace Blueshift.EntityFrameworkCore.MongoDB.Adapter.Conventions 6 | { 7 | /// 8 | /// Marks a as ignored during serialization. 9 | /// 10 | public class NotMappedAttributeConvention : BsonMemberMapAttributeConvention 11 | { 12 | /// 13 | /// Applies the Not Mapped convention to the given . 14 | /// 15 | /// The to which the convention will be applied. 16 | /// The to apply. 17 | protected override void Apply(BsonMemberMap memberMap, NotMappedAttribute attribute) 18 | => Check.NotNull(memberMap, nameof(memberMap)) 19 | .ClassMap 20 | .UnmapMember(memberMap.MemberInfo); 21 | } 22 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Adapter/EntityFrameworkConventionPack.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Blueshift.EntityFrameworkCore.MongoDB.Adapter.Conventions; 3 | using MongoDB.Bson.Serialization.Conventions; 4 | 5 | namespace Blueshift.EntityFrameworkCore.MongoDB.Adapter 6 | { 7 | /// 8 | /// 9 | /// Provides a set of conventions that configures the MongoDb C# Driver to work appropriately with the EntityFrameworkCore. 10 | /// 11 | public class EntityFrameworkConventionPack : ConventionPack 12 | { 13 | /// 14 | /// Registers the . 15 | /// 16 | /// 17 | public static void Register(Func typeFilter) 18 | { 19 | ConventionRegistry.Register( 20 | "Blueshift.EntityFrameworkCore.MongoDb.Conventions", 21 | Instance, 22 | typeFilter); 23 | } 24 | 25 | /// 26 | /// The singleton instance of . 27 | /// 28 | public static EntityFrameworkConventionPack Instance { get; } = new EntityFrameworkConventionPack(); 29 | 30 | private EntityFrameworkConventionPack() 31 | { 32 | AddRange(new IConvention[] 33 | { 34 | new AbstractBaseClassConvention(), 35 | new KeyAttributeConvention(), 36 | new NavigationSrializationMemberMapConvention(), 37 | new NotMappedAttributeConvention() 38 | }); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Adapter/Serialization/BsonSerializerExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using JetBrains.Annotations; 5 | using Microsoft.EntityFrameworkCore.Utilities; 6 | using MongoDB.Bson.Serialization; 7 | using MongoDB.Bson.Serialization.Serializers; 8 | 9 | // ReSharper disable once CheckNamespace 10 | namespace Blueshift.EntityFrameworkCore.MongoDB.Adapter.Serialization 11 | { 12 | /// 13 | /// Provides extended functionality to . 14 | /// 15 | public static class BsonSerializerExtensions 16 | { 17 | /// 18 | /// Modifies an instance of to only use the supplied members when serializing instances. 19 | /// 20 | /// The to modify. 21 | /// An of that lists the members 22 | /// required for serialization. 23 | /// A new instance of that serializes the information in . 24 | public static IBsonSerializer AsDenormalizingBsonClassMapSerializer( 25 | [NotNull] this IBsonSerializer bsonSerializer, 26 | [CanBeNull] IEnumerable denormalizedMemberNames = null) 27 | { 28 | TypeInfo typeInfo = Check.NotNull(bsonSerializer, nameof(bsonSerializer)).GetType().GetTypeInfo(); 29 | 30 | if (bsonSerializer is IChildSerializerConfigurable childSerializerConfigurable) 31 | { 32 | bsonSerializer = childSerializerConfigurable.WithChildSerializer( 33 | childSerializerConfigurable.ChildSerializer.AsDenormalizingBsonClassMapSerializer(denormalizedMemberNames)); 34 | } 35 | else if (typeInfo.TryGetImplementationType(typeof(ReadOnlyCollectionSerializer<>), 36 | out Type readOnlyCollectionSerializerType) 37 | || typeInfo.TryGetImplementationType(typeof(ReadOnlyCollectionSubclassSerializer<,>), 38 | out readOnlyCollectionSerializerType)) 39 | { 40 | bsonSerializer = (IBsonSerializer) Activator.CreateInstance(readOnlyCollectionSerializerType, 41 | ((IBsonSerializer) readOnlyCollectionSerializerType 42 | .GetProperty(nameof(EnumerableSerializerBase.ItemSerializer)) 43 | .GetValue(bsonSerializer)) 44 | .AsDenormalizingBsonClassMapSerializer(denormalizedMemberNames)); 45 | } 46 | else 47 | { 48 | BsonClassMap bsonClassMap = BsonClassMap.LookupClassMap(bsonSerializer.ValueType); 49 | bsonSerializer = (IBsonSerializer) Activator.CreateInstance( 50 | typeof(DenormalizingBsonClassMapSerializer<>).MakeGenericType(bsonSerializer.ValueType), 51 | bsonClassMap, 52 | denormalizedMemberNames); 53 | } 54 | 55 | return bsonSerializer; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Adapter/Serialization/NavigationBsonMemberMapSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using Blueshift.EntityFrameworkCore.MongoDB.Annotations; 5 | using Microsoft.EntityFrameworkCore.Utilities; 6 | using MongoDB.Bson.Serialization; 7 | 8 | namespace Blueshift.EntityFrameworkCore.MongoDB.Adapter.Serialization 9 | { 10 | /// 11 | /// 12 | /// A serializer for writing navigation properties used by MongoDB. 13 | /// 14 | /// The type of the member to be serialized by this . 15 | public class NavigationBsonMemberMapSerializer : IBsonSerializer 16 | { 17 | private readonly Lazy _lazyBsonSerializer; 18 | 19 | /// 20 | /// Initializes a new instance of the class. 21 | /// 22 | /// The representing the member to serialize. 23 | public NavigationBsonMemberMapSerializer(BsonMemberMap bsonMemberMap) 24 | { 25 | MemberMap = Check.NotNull(bsonMemberMap, nameof(bsonMemberMap)); 26 | 27 | _lazyBsonSerializer = new Lazy(() => 28 | { 29 | IEnumerable denormalizedMemberNames 30 | = bsonMemberMap.MemberInfo 31 | .GetCustomAttribute() 32 | ?.MemberNames 33 | ?? new string[0]; 34 | return BsonSerializer.LookupSerializer(bsonMemberMap.MemberType) 35 | .AsDenormalizingBsonClassMapSerializer(denormalizedMemberNames); 36 | }); 37 | } 38 | 39 | /// 40 | /// The representing the member to serialize. 41 | /// 42 | public BsonMemberMap MemberMap { get; } 43 | 44 | /// 45 | TClass IBsonSerializer.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) 46 | => (TClass) Deserialize(context, args); 47 | 48 | /// 49 | public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, TClass value) 50 | => _lazyBsonSerializer.Value.Serialize(context, args, value); 51 | 52 | /// 53 | public object Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) 54 | => _lazyBsonSerializer.Value.Deserialize(context, args); 55 | 56 | /// 57 | public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value) 58 | => _lazyBsonSerializer.Value.Serialize(context, args, value); 59 | 60 | /// 61 | public Type ValueType => MemberMap.MemberType; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Adapter/Update/DeleteOneModelFactory.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using Microsoft.EntityFrameworkCore.Metadata; 3 | using Microsoft.EntityFrameworkCore.Update; 4 | using Microsoft.EntityFrameworkCore.Utilities; 5 | using Microsoft.EntityFrameworkCore.ValueGeneration; 6 | using MongoDB.Driver; 7 | 8 | namespace Blueshift.EntityFrameworkCore.MongoDB.Adapter.Update 9 | { 10 | /// 11 | /// Creates from a given . 12 | /// 13 | /// The type of entity being added. 14 | public class DeleteOneModelFactory : MongoDbWriteModelFactory 15 | { 16 | /// 17 | /// Initializes a new instance of the class. 18 | /// 19 | /// The to use for populating concurrency tokens. 20 | /// The for which this will be used. 21 | public DeleteOneModelFactory( 22 | [NotNull] IValueGeneratorSelector valueGeneratorSelector, 23 | [NotNull] IEntityType entityType) 24 | : base( 25 | Check.NotNull(valueGeneratorSelector, nameof(valueGeneratorSelector)), 26 | Check.NotNull(entityType, nameof(entityType))) 27 | { 28 | } 29 | 30 | /// 31 | /// Creates an that maps the given . 32 | /// 33 | /// The to map. 34 | /// A new containing the inserted values represented 35 | /// by . 36 | public override WriteModel CreateWriteModel(IUpdateEntry updateEntry) 37 | => new DeleteOneModel(GetLookupFilter(updateEntry)); 38 | } 39 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Adapter/Update/IMongoDbWriteModelFactory.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using Microsoft.EntityFrameworkCore.Update; 3 | using MongoDB.Driver; 4 | 5 | namespace Blueshift.EntityFrameworkCore.MongoDB.Adapter.Update 6 | { 7 | /// 8 | /// Interface for generating instances from instances. 9 | /// 10 | /// The type of entity being updated 11 | public interface IMongoDbWriteModelFactory 12 | { 13 | /// 14 | /// Converts an instance to a instance. 15 | /// 16 | /// The entry to convert. 17 | /// A new that contains the updates in . 18 | WriteModel CreateWriteModel([NotNull] IUpdateEntry updateEntry); 19 | } 20 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Adapter/Update/IMongoDbWriteModelFactoryCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.EntityFrameworkCore.Metadata; 4 | using MongoDB.Driver; 5 | 6 | namespace Blueshift.EntityFrameworkCore.MongoDB.Adapter.Update 7 | { 8 | /// 9 | /// Caches instances. 10 | /// 11 | public interface IMongoDbWriteModelFactoryCache 12 | { 13 | /// 14 | /// Returns a cached or newly created instance of for the given 15 | /// and . 16 | /// 17 | /// The type of entity being written. 18 | /// The that contains the entity metadata. 19 | /// The describing the type of 20 | /// that returned the factory will produce. 21 | /// A that can 22 | /// be used to create a new factory instance if one has not previously been cached. 23 | /// A new or cached instance of . 24 | IMongoDbWriteModelFactory GetOrAdd( 25 | IEntityType entityType, 26 | EntityState entityState, 27 | Func> factoryFunc); 28 | } 29 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Adapter/Update/IMongoDbWriteModelFactorySelector.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using Microsoft.EntityFrameworkCore.Update; 3 | using MongoDB.Driver; 4 | 5 | namespace Blueshift.EntityFrameworkCore.MongoDB.Adapter.Update 6 | { 7 | /// 8 | /// Interface for selecting an instance of . 9 | /// 10 | public interface IMongoDbWriteModelFactorySelector 11 | { 12 | /// 13 | /// Select an instance for the given . 14 | /// 15 | /// The that the write model factory will be used to translate. 16 | /// The type of entity for which to create a instance. 17 | /// An instance of that can be used to convert 18 | /// instances to instances. 19 | IMongoDbWriteModelFactory Select([NotNull] IUpdateEntry updateEntry); 20 | } 21 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Adapter/Update/InsertOneModelFactory.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using Microsoft.EntityFrameworkCore.ChangeTracking.Internal; 3 | using Microsoft.EntityFrameworkCore.Metadata; 4 | using Microsoft.EntityFrameworkCore.Update; 5 | using Microsoft.EntityFrameworkCore.Utilities; 6 | using Microsoft.EntityFrameworkCore.ValueGeneration; 7 | using MongoDB.Driver; 8 | 9 | namespace Blueshift.EntityFrameworkCore.MongoDB.Adapter.Update 10 | { 11 | /// 12 | /// 13 | /// Creates from a given . 14 | /// 15 | /// The type of entity being added. 16 | public class InsertOneModelFactory : MongoDbWriteModelFactory 17 | { 18 | /// 19 | /// 20 | /// Initializes a new instance of the class. 21 | /// 22 | /// The to use for populating concurrency tokens. 23 | /// The for which this will be used. 24 | public InsertOneModelFactory( 25 | [NotNull] IValueGeneratorSelector valueGeneratorSelector, 26 | [NotNull] IEntityType entityType) 27 | : base( 28 | Check.NotNull(valueGeneratorSelector, nameof(valueGeneratorSelector)), 29 | Check.NotNull(entityType, nameof(entityType))) 30 | { 31 | } 32 | 33 | /// 34 | /// 35 | /// Creates an that maps the given . 36 | /// 37 | /// The to map. 38 | /// A new containing the inserted values represented 39 | /// by . 40 | public override WriteModel CreateWriteModel(IUpdateEntry updateEntry) 41 | { 42 | InternalEntityEntry internalEntityEntry = Check.Is(updateEntry, nameof(updateEntry)); 43 | UpdateDbGeneratedProperties(internalEntityEntry); 44 | return new InsertOneModel((TEntity)internalEntityEntry.Entity); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Adapter/Update/MongoDbWriteModelFactoryCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Metadata; 5 | using Microsoft.EntityFrameworkCore.Utilities; 6 | using MongoDB.Driver; 7 | 8 | namespace Blueshift.EntityFrameworkCore.MongoDB.Adapter.Update 9 | { 10 | /// 11 | /// Caches instances. 12 | /// 13 | public class MongoDbWriteModelFactoryCache : IMongoDbWriteModelFactoryCache 14 | { 15 | private readonly ConcurrentDictionary _cache 16 | = new ConcurrentDictionary(); 17 | 18 | /// 19 | /// Returns a cached or newly created instance of for the given 20 | /// and . 21 | /// 22 | /// The type of entity being written. 23 | /// The that contains the entity metadata. 24 | /// The describing the type of 25 | /// that returned the factory will produce. 26 | /// A that can 27 | /// be used to create a new factory instance if one has not previously been cached. 28 | /// A new or cached instance of . 29 | public IMongoDbWriteModelFactory GetOrAdd( 30 | IEntityType entityType, 31 | EntityState entityState, 32 | Func> factoryFunc) 33 | => _cache.GetOrAdd( 34 | new CacheKey( 35 | Check.NotNull(entityType, nameof(entityType)), 36 | entityState, 37 | Check.NotNull(factoryFunc, nameof(factoryFunc))), 38 | cacheKey => cacheKey.FactoryFunc(entityType, entityState)) 39 | as IMongoDbWriteModelFactory; 40 | 41 | private struct CacheKey : IEquatable 42 | { 43 | private readonly IEntityType _entityType; 44 | private readonly EntityState _entityState; 45 | 46 | public CacheKey( 47 | IEntityType entityType, 48 | EntityState entityState, 49 | Func factoryFunc) 50 | { 51 | _entityType = entityType; 52 | _entityState = entityState; 53 | FactoryFunc = factoryFunc; 54 | } 55 | 56 | public Func FactoryFunc { get; } 57 | 58 | public override bool Equals(object obj) 59 | => Equals((CacheKey)obj); 60 | 61 | public bool Equals(CacheKey other) 62 | => Equals(_entityType, other._entityType) 63 | && Equals(_entityState, other._entityState); 64 | 65 | public override int GetHashCode() 66 | { 67 | unchecked 68 | { 69 | return (_entityType.GetHashCode() * 492) ^ _entityState.GetHashCode(); 70 | } 71 | } 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Adapter/Update/ReplaceOneModelFactory.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using Microsoft.EntityFrameworkCore.ChangeTracking.Internal; 3 | using Microsoft.EntityFrameworkCore.Metadata; 4 | using Microsoft.EntityFrameworkCore.Update; 5 | using Microsoft.EntityFrameworkCore.Utilities; 6 | using Microsoft.EntityFrameworkCore.ValueGeneration; 7 | using MongoDB.Driver; 8 | 9 | namespace Blueshift.EntityFrameworkCore.MongoDB.Adapter.Update 10 | { 11 | /// 12 | /// 13 | /// Creates from a given . 14 | /// 15 | /// The type of entity being added. 16 | public class ReplaceOneModelFactory : MongoDbWriteModelFactory 17 | { 18 | /// 19 | /// 20 | /// Initializes a new instance of the class. 21 | /// 22 | /// The to use for populating 23 | /// concurrency tokens. 24 | /// The for which this 25 | /// will be used. 26 | public ReplaceOneModelFactory( 27 | [NotNull] IValueGeneratorSelector valueGeneratorSelector, 28 | [NotNull] IEntityType entityType) 29 | : base( 30 | Check.NotNull(valueGeneratorSelector, nameof(valueGeneratorSelector)), 31 | Check.NotNull(entityType, nameof(entityType))) 32 | { 33 | } 34 | 35 | /// 36 | /// 37 | /// Creates an that maps the given . 38 | /// 39 | /// The to map. 40 | /// A new containing the inserted values represented 41 | /// by . 42 | public override WriteModel CreateWriteModel(IUpdateEntry updateEntry) 43 | { 44 | InternalEntityEntry internalEntityEntry = Check.Is(updateEntry, nameof(updateEntry)); 45 | UpdateDbGeneratedProperties(internalEntityEntry); 46 | 47 | return new ReplaceOneModel( 48 | GetLookupFilter(updateEntry), 49 | (TEntity)internalEntityEntry.Entity); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Annotations/DenormalizeAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Blueshift.EntityFrameworkCore.MongoDB.Annotations 4 | { 5 | /// 6 | /// Declares that a member of a navigation property should be denormalized when serializing the property. 7 | /// 8 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] 9 | public class DenormalizeAttribute : Attribute 10 | { 11 | /// 12 | /// Initializes a new instance of the class. 13 | /// 14 | /// The names of sub-document members to denormalize when serializing the parent document. 15 | public DenormalizeAttribute(params string[] memberNames) 16 | { 17 | MemberNames = memberNames ?? new string[0]; 18 | } 19 | 20 | /// 21 | /// The name of the member to denormalize. 22 | /// 23 | public string[] MemberNames { get; } 24 | } 25 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Annotations/MongoCollectionAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using JetBrains.Annotations; 3 | using Microsoft.EntityFrameworkCore.Metadata.Internal; 4 | using Microsoft.EntityFrameworkCore.Utilities; 5 | 6 | namespace Blueshift.EntityFrameworkCore.MongoDB.Annotations 7 | { 8 | /// 9 | /// When applied to an entity class, sets the name of MongoDB collection name used to store instances of the entity. 10 | /// 11 | [AttributeUsage(AttributeTargets.Class)] 12 | public class MongoCollectionAttribute : Attribute 13 | { 14 | /// 15 | /// Initializes a new instance of the class. 16 | /// 17 | /// The MongoDb database name to use with the . 18 | public MongoCollectionAttribute([NotNull] string collectionName) 19 | { 20 | CollectionName = Check.NotEmpty(collectionName, nameof(collectionName)); 21 | } 22 | 23 | /// 24 | /// The MongoDb database name to use with the . 25 | /// 26 | public virtual string CollectionName { get; } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Annotations/MongoDatabaseAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using JetBrains.Annotations; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Metadata.Internal; 5 | using Microsoft.EntityFrameworkCore.Utilities; 6 | 7 | namespace Blueshift.EntityFrameworkCore.MongoDB.Annotations 8 | { 9 | /// 10 | /// When applied to a , sets the database name to use with the context's . 11 | /// 12 | [AttributeUsage(AttributeTargets.Class)] 13 | public class MongoDatabaseAttribute : Attribute 14 | { 15 | /// 16 | /// Initializes a new instance of the class. 17 | /// 18 | /// The MongoDb database name to use with the . 19 | public MongoDatabaseAttribute([NotNull] string database) 20 | { 21 | Database = Check.NotEmpty(database, nameof(database)); 22 | } 23 | 24 | /// 25 | /// The MongoDb database name to use with the . 26 | /// 27 | public virtual string Database { get; } 28 | } 29 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Blueshift.EntityFrameworkCore.MongoDB.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(BuildFrameworks) 5 | Blueshift MongoDb Provider for EntityFrameworkCore 6 | Blueshift Software MongoDb Provider for Microsoft EntityFramework Core 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | True 28 | True 29 | DocumentDbStrings.tt 30 | 31 | 32 | True 33 | True 34 | DocumentDbStrings.resx 35 | 36 | 37 | 38 | 39 | 40 | ResXFileCodeGenerator 41 | DocumentDbStrings.Designer.cs 42 | 43 | 44 | 45 | 46 | 47 | TextTemplatingFileGenerator 48 | DocumentDbStrings.cs 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/ChangeTracking/MongoDbInternalEntityEntryFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.EntityFrameworkCore.ChangeTracking.Internal; 4 | using Microsoft.EntityFrameworkCore.Metadata; 5 | using Microsoft.EntityFrameworkCore.Metadata.Internal; 6 | using Microsoft.EntityFrameworkCore.Storage; 7 | 8 | namespace Blueshift.EntityFrameworkCore.MongoDB.ChangeTracking 9 | { 10 | /// 11 | public class MongoDbInternalEntityEntryFactory : InternalEntityEntryFactory 12 | { 13 | /// 14 | public override InternalEntityEntry Create( 15 | IStateManager stateManager, 16 | IEntityType entityType, 17 | object entity, 18 | in ValueBuffer valueBuffer) 19 | => base.Create( 20 | stateManager, 21 | entityType, 22 | entity, 23 | valueBuffer.IsEmpty 24 | ? CreateValueBuffer(entityType, entity) 25 | : valueBuffer); 26 | 27 | /// 28 | public override InternalEntityEntry Create( 29 | IStateManager stateManager, 30 | IEntityType entityType, 31 | object entity) 32 | => Create(stateManager, entityType, entity, ValueBuffer.Empty); 33 | 34 | private ValueBuffer CreateValueBuffer(IEntityType entityType, object entity) 35 | { 36 | object[] values = new object[entityType.PropertyCount()]; 37 | 38 | foreach (IProperty property in entityType.GetProperties()) 39 | { 40 | values[property.GetIndex()] = GetPropertyValue(entity, property); 41 | } 42 | 43 | return new ValueBuffer(values); 44 | } 45 | 46 | private object GetPropertyValue(object entity, IProperty property) 47 | { 48 | if (property.IsShadowProperty && property.IsForeignKey()) 49 | { 50 | IForeignKey foreignKey = property.AsProperty().ForeignKeys.First(); 51 | INavigation navigationProperty = property.DeclaringEntityType == foreignKey.PrincipalEntityType 52 | && !foreignKey.IsSelfPrimaryKeyReferencing() 53 | ? foreignKey.PrincipalToDependent 54 | : foreignKey.DependentToPrincipal; 55 | 56 | if (navigationProperty != null) 57 | { 58 | entity = navigationProperty.GetGetter().GetClrValue(entity); 59 | 60 | IEntityType targetEntityType = navigationProperty.GetTargetType(); 61 | property = targetEntityType.FindPrimaryKey().Properties.Single(); 62 | } 63 | else 64 | { 65 | entity = null; 66 | } 67 | } 68 | 69 | return entity == null 70 | ? null 71 | : property.IsShadowProperty 72 | ? entity.GetHashCode() 73 | : property.GetGetter().GetClrValue(entity); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/DbContextOptionsExtensions.cs: -------------------------------------------------------------------------------- 1 | //using System; 2 | //using System.Collections.Generic; 3 | //using System.Linq; 4 | //using JetBrains.Annotations; 5 | //using Microsoft.EntityFrameworkCore.Infrastructure; 6 | //using Microsoft.EntityFrameworkCore.Utilities; 7 | 8 | //// ReSharper disable once CheckNamespace 9 | //namespace Blueshift.EntityFrameworkCore.MongoDB 10 | //{ 11 | // /// 12 | // /// Provides a set of extension methods for instances of the interface. 13 | // /// 14 | // public static class DbContextOptionsExtensions 15 | // { 16 | // /// 17 | // /// Extracts a single instance of the from this . 18 | // /// 19 | // /// The type of to be extracted. 20 | // /// An instance of to search for the given extension. 21 | // /// The single instance of that was found. 22 | // /// 23 | // /// Thrown if no instance of the could be found, or if more than one was found. 24 | // /// 25 | // public static TExtension Extract([NotNull] this IDbContextOptions dbContextOptions) 26 | // where TExtension : IDbContextOptionsExtension 27 | // { 28 | // Check.NotNull(dbContextOptions, nameof(dbContextOptions)); 29 | 30 | // IList extensions = dbContextOptions.Extensions 31 | // .OfType() 32 | // .ToList(); 33 | 34 | // if (extensions.Count == 0) 35 | // { 36 | // throw new InvalidOperationException($"No provider has been configured with a {nameof(TExtension)} extension."); 37 | // } 38 | // if (extensions.Count > 1) 39 | // { 40 | // throw new InvalidOperationException($"Multiple providers have been configured with a {nameof(TExtension)} extension."); 41 | // } 42 | // return extensions[index: 0]; 43 | // } 44 | // } 45 | //} -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/DependencyInjection/MongoDbEfServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Reflection; 3 | using Blueshift.EntityFrameworkCore.MongoDB.Adapter; 4 | using Blueshift.EntityFrameworkCore.MongoDB.Infrastructure; 5 | using JetBrains.Annotations; 6 | using Microsoft.EntityFrameworkCore.Utilities; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using MongoDB.Bson; 9 | 10 | // ReSharper disable once CheckNamespace 11 | namespace Blueshift.EntityFrameworkCore.MongoDB.DependencyInjection 12 | { 13 | /// 14 | /// Extends with methods for use with the MongoDb EntityFrameworkCore provider. 15 | /// 16 | public static class MongoDbEfServiceCollectionExtensions 17 | { 18 | static MongoDbEfServiceCollectionExtensions() 19 | { 20 | if (!typeof(ObjectId).GetTypeInfo().IsDefined(typeof(TypeConverterAttribute))) 21 | { 22 | TypeDescriptor.AddAttributes(typeof(ObjectId), new TypeConverterAttribute(typeof(ObjectIdTypeConverter))); 23 | } 24 | 25 | EntityFrameworkConventionPack.Register(type => true); 26 | } 27 | 28 | /// 29 | /// Populates the given instance with the service dependencies for 30 | /// the MongoDb provider for EntityFrameworkCore. 31 | /// 32 | /// The instance of populate. 33 | /// The populated with the MongoDb EntityFrameworkCore dependencies. 34 | public static IServiceCollection AddEntityFrameworkMongoDb([NotNull] this IServiceCollection serviceCollection) 35 | { 36 | Check.NotNull(serviceCollection, nameof(serviceCollection)); 37 | 38 | var entityFrameworkServicesBuilder = new EntityFrameworkMongoDbServicesBuilder(serviceCollection); 39 | entityFrameworkServicesBuilder.TryAddCoreServices(); 40 | return serviceCollection; 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Infrastructure/MongoDbContextOptionsBuilder.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.EntityFrameworkCore.Infrastructure; 4 | using Microsoft.EntityFrameworkCore.Utilities; 5 | 6 | // ReSharper disable once CheckNamespace 7 | namespace Blueshift.EntityFrameworkCore.MongoDB.Infrastructure 8 | { 9 | /// 10 | /// 11 | /// Allows MongoDb-specific configuration to be performed on . 12 | /// 13 | /// 14 | /// Instances of this class are returned from a call to 15 | /// 16 | /// and it is not designed to be directly constructed in your application code. 17 | /// 18 | /// 19 | public class MongoDbContextOptionsBuilder 20 | { 21 | /// 22 | /// Initializes a new instance of the class. 23 | /// 24 | /// The core class. 25 | public MongoDbContextOptionsBuilder([NotNull] DbContextOptionsBuilder optionsBuilder) 26 | { 27 | OptionsBuilder = Check.NotNull(optionsBuilder, nameof(optionsBuilder)); 28 | } 29 | 30 | /// 31 | /// Gets the core that supplied to the constructor. 32 | /// 33 | protected virtual DbContextOptionsBuilder OptionsBuilder { get; } 34 | 35 | /// 36 | /// Sets the name of the MongoDB database to use with the being configured. 37 | /// 38 | /// The name of the MongoDB database instance to use with the current . 39 | /// This , so that calls can be chained. 40 | public MongoDbContextOptionsBuilder UseDatabase([NotNull] string databaseName) 41 | { 42 | Check.NotEmpty(databaseName, nameof(databaseName)); 43 | MongoDbOptionsExtension extension = CloneExtension(); 44 | extension.DatabaseName = databaseName; 45 | ((IDbContextOptionsBuilderInfrastructure)OptionsBuilder).AddOrUpdateExtension(extension); 46 | return this; 47 | } 48 | 49 | /// 50 | /// Clones the used to configure this builder. 51 | /// 52 | /// A cloned instance of this builder's . 53 | protected virtual MongoDbOptionsExtension CloneExtension() 54 | => new MongoDbOptionsExtension(OptionsBuilder.Options.GetExtension()); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Metadata/Builders/DocumentEntityTypeBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.EntityFrameworkCore.Metadata; 4 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 5 | using Microsoft.EntityFrameworkCore.Utilities; 6 | 7 | namespace Blueshift.EntityFrameworkCore.MongoDB.Metadata.Builders 8 | { 9 | /// 10 | /// Provides a set of MongoDB-specific extension methods for . 11 | /// 12 | public static class DocumentEntityTypeBuilderExtensions 13 | { 14 | /// 15 | /// Sets whether the is a complex type (i.e.: not a valid queryable root document entity). 16 | /// 17 | /// The to annotate. 18 | /// 19 | /// true if the is a complex type; 20 | /// otherwise false. 21 | /// 22 | /// The , such that calls be chained. 23 | public static EntityTypeBuilder IsDocumentComplexType( 24 | [NotNull] this EntityTypeBuilder entityTypeBuilder, 25 | bool isDocumentComplexType) 26 | { 27 | Check.NotNull(entityTypeBuilder, nameof(entityTypeBuilder)); 28 | 29 | entityTypeBuilder.Document().IsComplexType = isDocumentComplexType; 30 | return entityTypeBuilder; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Metadata/Builders/DocumentInternalKeyBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using Microsoft.EntityFrameworkCore.Metadata.Internal; 3 | using Microsoft.EntityFrameworkCore.Utilities; 4 | 5 | namespace Blueshift.EntityFrameworkCore.MongoDB.Metadata.Builders 6 | { 7 | /// 8 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 9 | /// directly from your code. This API may change or be removed in future releases. 10 | /// 11 | public static class DocumentInternalKeyBuilderExtensions 12 | { 13 | /// 14 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 15 | /// directly from your code. This API may change or be removed in future releases. 16 | /// 17 | public static InternalKeyBuilder IsDocumentOwnershipKey( 18 | [NotNull] this InternalKeyBuilder internalKeyBuilder, 19 | bool isDocumentOwnershipKey) 20 | { 21 | DocumentKeyAnnotations documentKeyAnnotations = 22 | Check.NotNull(internalKeyBuilder, nameof(internalKeyBuilder)).Document(); 23 | 24 | documentKeyAnnotations.IsOwnershipKey = isDocumentOwnershipKey; 25 | 26 | return internalKeyBuilder; 27 | } 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Metadata/Builders/MongoDbModelBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Utilities; 3 | 4 | // ReSharper disable once CheckNamespace 5 | namespace Blueshift.EntityFrameworkCore.MongoDB.Metadata.Builders 6 | { 7 | /// 8 | /// MongoDb-specific extension methods for . 9 | /// 10 | public static class MongoDbModelBuilderExtensions 11 | { 12 | /// 13 | /// Configures the database to use when connecting to MongoDb. 14 | /// 15 | /// The to configure. 16 | /// The name of the database. 17 | /// This , such that calls can be chained. 18 | public static ModelBuilder ForMongoDbFromDatabase(this ModelBuilder modelBuilder, string database) 19 | { 20 | Check.NotNull(modelBuilder, nameof(modelBuilder)); 21 | modelBuilder.Model.MongoDb().Database = database; 22 | return modelBuilder; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Metadata/Conventions/BsonDiscriminatorAttributeConvention.cs: -------------------------------------------------------------------------------- 1 | using Blueshift.EntityFrameworkCore.MongoDB.Metadata.Builders; 2 | using Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal; 3 | using Microsoft.EntityFrameworkCore.Metadata.Internal; 4 | using Microsoft.EntityFrameworkCore.Utilities; 5 | using MongoDB.Bson.Serialization.Attributes; 6 | 7 | namespace Blueshift.EntityFrameworkCore.MongoDB.Metadata.Conventions 8 | { 9 | /// 10 | public class BsonDiscriminatorAttributeConvention : EntityTypeAttributeConvention 11 | { 12 | /// 13 | public override InternalEntityTypeBuilder Apply(InternalEntityTypeBuilder entityTypeBuilder, 14 | BsonDiscriminatorAttribute attribute) 15 | { 16 | Check.NotNull(entityTypeBuilder, nameof(entityTypeBuilder)); 17 | Check.NotNull(attribute, nameof(attribute)); 18 | MongoDbEntityTypeAnnotations annotations = entityTypeBuilder.MongoDb(); 19 | if (!string.IsNullOrWhiteSpace(attribute.Discriminator)) 20 | { 21 | annotations.Discriminator = attribute.Discriminator; 22 | } 23 | 24 | if (!annotations.DiscriminatorIsRequired) 25 | { 26 | annotations.DiscriminatorIsRequired = attribute.Required; 27 | } 28 | 29 | annotations.IsRootType = attribute.RootClass; 30 | return entityTypeBuilder; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Metadata/Conventions/BsonIgnoreAttributeConvention.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal; 6 | using Microsoft.EntityFrameworkCore.Metadata.Internal; 7 | using Microsoft.EntityFrameworkCore.Utilities; 8 | using MongoDB.Bson.Serialization.Attributes; 9 | 10 | namespace Blueshift.EntityFrameworkCore.MongoDB.Metadata.Conventions 11 | { 12 | /// 13 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 14 | /// directly from your code. This API may change or be removed in future releases. 15 | /// 16 | public class BsonIgnoreAttributeConvention : IEntityTypeAddedConvention 17 | { 18 | /// 19 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 20 | /// directly from your code. This API may change or be removed in future releases. 21 | /// 22 | public virtual InternalEntityTypeBuilder Apply(InternalEntityTypeBuilder entityTypeBuilder) 23 | { 24 | Check.NotNull(entityTypeBuilder, nameof(entityTypeBuilder)); 25 | 26 | Type clrType = entityTypeBuilder.Metadata.ClrType; 27 | if (clrType == null) 28 | { 29 | return entityTypeBuilder; 30 | } 31 | 32 | IEnumerable members = clrType.GetRuntimeProperties() 33 | .Cast() 34 | .Concat(clrType.GetRuntimeFields()) 35 | .Where(memberInfo => memberInfo.IsDefined(typeof(BsonIgnoreAttribute), true)); 36 | 37 | foreach (MemberInfo member in members) 38 | { 39 | entityTypeBuilder.Ignore(member.Name, ConfigurationSource.DataAnnotation); 40 | } 41 | 42 | return entityTypeBuilder; 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Metadata/Conventions/BsonKnownTypesAttributeConvention.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using Blueshift.EntityFrameworkCore.MongoDB.Metadata.Builders; 4 | using Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal; 5 | using Microsoft.EntityFrameworkCore.Metadata.Internal; 6 | using Microsoft.EntityFrameworkCore.Utilities; 7 | using MongoDB.Bson.Serialization.Attributes; 8 | 9 | namespace Blueshift.EntityFrameworkCore.MongoDB.Metadata.Conventions 10 | { 11 | /// 12 | public class BsonKnownTypesAttributeConvention : EntityTypeAttributeConvention 13 | { 14 | /// 15 | public override InternalEntityTypeBuilder Apply(InternalEntityTypeBuilder entityTypeBuilder) 16 | { 17 | Check.NotNull(entityTypeBuilder, nameof(entityTypeBuilder)); 18 | 19 | var type = entityTypeBuilder.Metadata.ClrType; 20 | if (type == null || !Attribute.IsDefined(type, typeof(BsonKnownTypesAttribute), inherit: false)) 21 | { 22 | return entityTypeBuilder; 23 | } 24 | 25 | var attributes = type.GetTypeInfo().GetCustomAttributes(false); 26 | 27 | foreach (var attribute in attributes) 28 | { 29 | entityTypeBuilder = Apply(entityTypeBuilder, attribute); 30 | if (entityTypeBuilder == null) 31 | { 32 | break; 33 | } 34 | } 35 | 36 | return entityTypeBuilder; 37 | } 38 | 39 | /// 40 | public override InternalEntityTypeBuilder Apply( 41 | InternalEntityTypeBuilder entityTypeBuilder, 42 | BsonKnownTypesAttribute bsonKnownTypesAttribute) 43 | { 44 | MongoDbEntityTypeAnnotations annotations = entityTypeBuilder.MongoDb(); 45 | if (!annotations.DiscriminatorIsRequired) 46 | { 47 | annotations.DiscriminatorIsRequired = entityTypeBuilder.Metadata.IsAbstract(); 48 | } 49 | 50 | if (bsonKnownTypesAttribute.KnownTypes != null) 51 | { 52 | InternalModelBuilder modelBuilder = entityTypeBuilder.ModelBuilder; 53 | Type baseType = entityTypeBuilder.Metadata.ClrType; 54 | 55 | foreach (Type derivedType in bsonKnownTypesAttribute.KnownTypes) 56 | { 57 | if (!baseType.IsAssignableFrom(derivedType)) 58 | { 59 | throw new InvalidOperationException($"Known type {derivedType} declared on base type {baseType} does not inherit from base type."); 60 | } 61 | 62 | modelBuilder 63 | .Entity(derivedType, ConfigurationSource.DataAnnotation) 64 | .MongoDb() 65 | .IsDerivedType = true; 66 | } 67 | } 68 | 69 | return entityTypeBuilder; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Metadata/Conventions/BsonRequiredAttributeConvention.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal; 3 | using Microsoft.EntityFrameworkCore.Metadata.Internal; 4 | using Microsoft.EntityFrameworkCore.Utilities; 5 | using MongoDB.Bson.Serialization.Attributes; 6 | 7 | namespace Blueshift.EntityFrameworkCore.MongoDB.Metadata.Conventions 8 | { 9 | /// 10 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 11 | /// directly from your code. This API may change or be removed in future releases. 12 | /// 13 | public class BsonRequiredAttributeConvention : PropertyAttributeConvention 14 | { 15 | /// 16 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 17 | /// directly from your code. This API may change or be removed in future releases. 18 | /// 19 | public override InternalPropertyBuilder Apply(InternalPropertyBuilder propertyBuilder, 20 | BsonRequiredAttribute attribute, 21 | MemberInfo clrMember) 22 | { 23 | Check.NotNull(propertyBuilder, nameof(propertyBuilder)); 24 | Check.NotNull(attribute, nameof(attribute)); 25 | Check.NotNull(clrMember, nameof(clrMember)); 26 | 27 | propertyBuilder.IsRequired(true, ConfigurationSource.DataAnnotation); 28 | 29 | return propertyBuilder; 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Metadata/Conventions/MongoCollectionAttributeConvention.cs: -------------------------------------------------------------------------------- 1 | using Blueshift.EntityFrameworkCore.MongoDB.Annotations; 2 | using Blueshift.EntityFrameworkCore.MongoDB.Metadata.Builders; 3 | using Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal; 4 | using Microsoft.EntityFrameworkCore.Metadata.Internal; 5 | using Microsoft.EntityFrameworkCore.Utilities; 6 | 7 | namespace Blueshift.EntityFrameworkCore.MongoDB.Metadata.Conventions 8 | { 9 | /// 10 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 11 | /// directly from your code. This API may change or be removed in future releases. 12 | /// 13 | public class MongoCollectionAttributeConvention : EntityTypeAttributeConvention 14 | { 15 | /// 16 | public override InternalEntityTypeBuilder Apply( 17 | InternalEntityTypeBuilder entityTypeBuilder, 18 | MongoCollectionAttribute mongoCollectionAttribute) 19 | { 20 | Check.NotNull(entityTypeBuilder, nameof(entityTypeBuilder)); 21 | Check.NotNull(mongoCollectionAttribute, nameof(mongoCollectionAttribute)); 22 | entityTypeBuilder.MongoDb().CollectionName = mongoCollectionAttribute.CollectionName; 23 | return entityTypeBuilder; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Metadata/Conventions/MongoDatabaseConvention.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Text.RegularExpressions; 3 | using Blueshift.EntityFrameworkCore.MongoDB.Annotations; 4 | using Blueshift.EntityFrameworkCore.MongoDB.Infrastructure; 5 | using Blueshift.EntityFrameworkCore.MongoDB.Metadata.Builders; 6 | using JetBrains.Annotations; 7 | using Microsoft.EntityFrameworkCore; 8 | using Microsoft.EntityFrameworkCore.Infrastructure; 9 | using Microsoft.EntityFrameworkCore.Internal; 10 | using Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal; 11 | using Microsoft.EntityFrameworkCore.Metadata.Internal; 12 | using Microsoft.EntityFrameworkCore.Utilities; 13 | 14 | namespace Blueshift.EntityFrameworkCore.MongoDB.Metadata.Conventions 15 | { 16 | /// 17 | /// 18 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 19 | /// directly from your code. This API may change or be removed in future releases. 20 | /// 21 | public class MongoDatabaseConvention : IModelInitializedConvention 22 | { 23 | private readonly DbContext _dbContext; 24 | private readonly MongoDbOptionsExtension _mongoDbOptionsExtension; 25 | 26 | /// 27 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 28 | /// directly from your code. This API may change or be removed in future releases. 29 | /// 30 | public MongoDatabaseConvention([NotNull] DbContext dbContext) 31 | { 32 | _dbContext = Check.NotNull(dbContext, nameof(dbContext)); 33 | _mongoDbOptionsExtension = _dbContext 34 | .GetService() 35 | .ContextOptions 36 | .FindExtension(); 37 | } 38 | 39 | /// 40 | /// 41 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 42 | /// directly from your code. This API may change or be removed in future releases. 43 | /// 44 | public InternalModelBuilder Apply(InternalModelBuilder modelBuilder) 45 | { 46 | Check.NotNull(modelBuilder, nameof(modelBuilder)); 47 | 48 | MongoDatabaseAttribute mongoDatabaseAttribute = _dbContext.GetType() 49 | .GetTypeInfo() 50 | .GetCustomAttribute(); 51 | 52 | string databaseName = _mongoDbOptionsExtension.DatabaseName 53 | ?? mongoDatabaseAttribute?.Database 54 | ?? GetDefaultDatabaseName(); 55 | 56 | Check.NotNull(modelBuilder, nameof(modelBuilder)) 57 | .MongoDb() 58 | .Database = databaseName; 59 | return modelBuilder; 60 | } 61 | 62 | private string GetDefaultDatabaseName() 63 | => MongoDbUtilities.ToLowerCamelCase(Regex.Replace(_dbContext.GetType().Name, "(?:Mongo)?(?:Db)?(?:Context)?$", "")); 64 | } 65 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Metadata/Conventions/MongoDbDatabaseGeneratedAttributeConvention.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations.Schema; 2 | using System.Reflection; 3 | using Blueshift.EntityFrameworkCore.MongoDB.Metadata.Builders; 4 | using Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal; 5 | using Microsoft.EntityFrameworkCore.Metadata.Internal; 6 | 7 | namespace Blueshift.EntityFrameworkCore.MongoDB.Metadata.Conventions 8 | { 9 | /// 10 | public class MongoDbDatabaseGeneratedAttributeConvention : DatabaseGeneratedAttributeConvention 11 | { 12 | /// 13 | public override InternalPropertyBuilder Apply( 14 | InternalPropertyBuilder propertyBuilder, 15 | DatabaseGeneratedAttribute attribute, 16 | MemberInfo clrMember) 17 | { 18 | if (attribute.DatabaseGeneratedOption == DatabaseGeneratedOption.Identity) 19 | { 20 | propertyBuilder.Metadata 21 | .DeclaringEntityType 22 | .MongoDb() 23 | .AssignIdOnInsert = true; 24 | } 25 | return base.Apply(propertyBuilder, attribute, clrMember); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Metadata/Conventions/MongoDbKeyAttributeConvention.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Reflection; 6 | using Blueshift.EntityFrameworkCore.MongoDB.Metadata.Builders; 7 | using Microsoft.EntityFrameworkCore.Internal; 8 | using Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal; 9 | using Microsoft.EntityFrameworkCore.Metadata.Internal; 10 | using Microsoft.EntityFrameworkCore.Utilities; 11 | using MongoDB.Bson.Serialization.Attributes; 12 | 13 | namespace Blueshift.EntityFrameworkCore.MongoDB.Metadata.Conventions 14 | { 15 | /// 16 | public class MongoDbKeyAttributeConvention : KeyAttributeConvention 17 | { 18 | private static readonly KeyAttribute KeyAttribute = new KeyAttribute(); 19 | 20 | /// 21 | public override InternalPropertyBuilder Apply(InternalPropertyBuilder propertyBuilder) 22 | { 23 | Check.NotNull(propertyBuilder, nameof(propertyBuilder)); 24 | 25 | MemberInfo memberInfo = propertyBuilder.Metadata.GetIdentifyingMemberInfo(); 26 | return (memberInfo?.IsDefined(typeof(BsonIdAttribute), true) ?? false) 27 | ? Apply(propertyBuilder, KeyAttribute, memberInfo) 28 | : base.Apply(propertyBuilder); 29 | } 30 | 31 | /// 32 | public override InternalModelBuilder Apply(InternalModelBuilder modelBuilder) 33 | { 34 | IEnumerable entityTypes = modelBuilder.Metadata 35 | .GetEntityTypes() 36 | .Where(entityType => entityType.MongoDb().IsDerivedType); 37 | 38 | foreach (EntityType entityType in entityTypes) 39 | { 40 | foreach (Property declaredProperty in entityType.GetDeclaredProperties()) 41 | { 42 | if (declaredProperty.GetIdentifyingMemberInfo()?.IsDefined(typeof(BsonIdAttribute), true) ?? false) 43 | { 44 | throw new InvalidOperationException( 45 | CoreStrings.KeyAttributeOnDerivedEntity(entityType.DisplayName(), declaredProperty.Name)); 46 | } 47 | } 48 | } 49 | return base.Apply(modelBuilder); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Metadata/Conventions/MongoDbRelationshipDiscoveryConvention.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.EntityFrameworkCore.Diagnostics; 4 | using Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal; 5 | using Microsoft.EntityFrameworkCore.Metadata.Internal; 6 | 7 | namespace Blueshift.EntityFrameworkCore.MongoDB.Metadata.Conventions 8 | { 9 | /// 10 | /// 11 | public class MongoDbRelationshipDiscoveryConvention : RelationshipDiscoveryConvention, IForeignKeyOwnershipChangedConvention 12 | { 13 | /// 14 | public MongoDbRelationshipDiscoveryConvention( 15 | [NotNull] IMemberClassifier memberClassifier, 16 | [NotNull] IDiagnosticsLogger logger) 17 | : base(memberClassifier, logger) 18 | { 19 | } 20 | 21 | /// 22 | InternalRelationshipBuilder IForeignKeyOwnershipChangedConvention.Apply(InternalRelationshipBuilder relationshipBuilder) 23 | { 24 | Apply(relationshipBuilder.Metadata.DeclaringEntityType.Builder); 25 | return relationshipBuilder; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Metadata/DocumentAnnotationNames.cs: -------------------------------------------------------------------------------- 1 | namespace Blueshift.EntityFrameworkCore.MongoDB.Metadata 2 | { 3 | /// 4 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 5 | /// directly from your code. This API may change or be removed in future releases. 6 | /// 7 | public class DocumentAnnotationNames 8 | { 9 | private const string Prefix = "Document:"; 10 | 11 | /// 12 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 13 | /// directly from your code. This API may change or be removed in future releases. 14 | /// 15 | public const string IsComplexType = Prefix + nameof(IsComplexType); 16 | 17 | /// 18 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 19 | /// directly from your code. This API may change or be removed in future releases. 20 | /// 21 | public const string IsOwnershipKey = Prefix + nameof(IsOwnershipKey); 22 | } 23 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Metadata/DocumentAnnotations.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using Microsoft.EntityFrameworkCore.Infrastructure; 3 | using Microsoft.EntityFrameworkCore.Metadata; 4 | using Microsoft.EntityFrameworkCore.Utilities; 5 | 6 | namespace Blueshift.EntityFrameworkCore.MongoDB.Metadata 7 | { 8 | /// 9 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 10 | /// directly from your code. This API may change or be removed in future releases. 11 | /// 12 | public class DocumentAnnotations 13 | where TAnnotatable : IAnnotatable 14 | { 15 | /// 16 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 17 | /// directly from your code. This API may change or be removed in future releases. 18 | /// 19 | protected DocumentAnnotations([NotNull] TAnnotatable metadata) 20 | { 21 | Annotatable = Check.Is(metadata, nameof(metadata)); 22 | Metadata = Check.NotNull(metadata, nameof(metadata)); 23 | } 24 | 25 | /// 26 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 27 | /// directly from your code. This API may change or be removed in future releases. 28 | /// 29 | public virtual TAnnotatable Metadata { get; } 30 | 31 | /// 32 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 33 | /// directly from your code. This API may change or be removed in future releases. 34 | /// 35 | protected virtual IMutableAnnotatable Annotatable { get; } 36 | 37 | /// 38 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 39 | /// directly from your code. This API may change or be removed in future releases. 40 | /// 41 | public virtual T GetAnnotation([CanBeNull] string annotationName) 42 | => (T)Annotatable[Check.NullButNotEmpty(annotationName, nameof(annotationName))]; 43 | 44 | /// 45 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 46 | /// directly from your code. This API may change or be removed in future releases. 47 | /// 48 | public virtual bool SetAnnotation([NotNull] string annotationName, [CanBeNull] T value) 49 | { 50 | Check.NotEmpty(annotationName, nameof(annotationName)); 51 | Annotatable[annotationName] = value; 52 | return true; 53 | } 54 | 55 | /// 56 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 57 | /// directly from your code. This API may change or be removed in future releases. 58 | /// 59 | public virtual bool CanSetAnnotation([NotNull] string annotationName, [CanBeNull] object value) 60 | => true; 61 | } 62 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Metadata/DocumentEntityTypeAnnotations.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using Microsoft.EntityFrameworkCore.Metadata; 3 | using Microsoft.EntityFrameworkCore.Utilities; 4 | using MongoDB.Driver; 5 | 6 | namespace Blueshift.EntityFrameworkCore.MongoDB.Metadata 7 | { 8 | /// 9 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 10 | /// directly from your code. This API may change or be removed in future releases. 11 | /// 12 | public class DocumentEntityTypeAnnotations : DocumentAnnotations 13 | { 14 | /// 15 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 16 | /// directly from your code. This API may change or be removed in future releases. 17 | /// 18 | public DocumentEntityTypeAnnotations([NotNull] IEntityType entityType) 19 | : base(entityType) 20 | { 21 | } 22 | 23 | /// 24 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 25 | /// directly from your code. This API may change or be removed in future releases. 26 | /// 27 | public virtual bool IsComplexType 28 | { 29 | get => GetAnnotation(DocumentAnnotationNames.IsComplexType) ?? false; 30 | set => SetAnnotation(DocumentAnnotationNames.IsComplexType, value); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Metadata/DocumentKeyAnnotations.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using Microsoft.EntityFrameworkCore.Metadata; 3 | 4 | namespace Blueshift.EntityFrameworkCore.MongoDB.Metadata 5 | { 6 | /// 7 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 8 | /// directly from your code. This API may change or be removed in future releases. 9 | /// 10 | public class DocumentKeyAnnotations : DocumentAnnotations 11 | { 12 | /// 13 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 14 | /// directly from your code. This API may change or be removed in future releases. 15 | /// 16 | public DocumentKeyAnnotations([NotNull] IKey key) 17 | : base(key) 18 | { 19 | } 20 | 21 | /// 22 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 23 | /// directly from your code. This API may change or be removed in future releases. 24 | /// 25 | public virtual bool IsOwnershipKey 26 | { 27 | get => GetAnnotation(DocumentAnnotationNames.IsOwnershipKey) ?? false; 28 | set => SetAnnotation(DocumentAnnotationNames.IsOwnershipKey, value); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Metadata/MongoDbModelAnnotations.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using Microsoft.EntityFrameworkCore.Metadata; 3 | using Microsoft.EntityFrameworkCore.Utilities; 4 | using MongoDB.Driver; 5 | 6 | namespace Blueshift.EntityFrameworkCore.MongoDB.Metadata 7 | { 8 | /// 9 | /// 10 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 11 | /// directly from your code. This API may change or be removed in future releases. 12 | /// 13 | public class MongoDbModelAnnotations : DocumentAnnotations 14 | { 15 | /// 16 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 17 | /// directly from your code. This API may change or be removed in future releases. 18 | /// 19 | public MongoDbModelAnnotations([NotNull] IModel model) 20 | : base(model) 21 | { 22 | } 23 | 24 | /// 25 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 26 | /// directly from your code. This API may change or be removed in future releases. 27 | /// 28 | public virtual string Database 29 | { 30 | get => GetAnnotation(MongoDbAnnotationNames.Database); 31 | 32 | [param: NotNull] 33 | set => SetAnnotation(MongoDbAnnotationNames.Database, Check.NotEmpty(value, nameof(Database))); 34 | } 35 | 36 | /// 37 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 38 | /// directly from your code. This API may change or be removed in future releases. 39 | /// 40 | public virtual MongoDatabaseSettings DatabaseSettings 41 | { 42 | get => GetAnnotation(MongoDbAnnotationNames.DatabaseSettings); 43 | 44 | [param: NotNull] 45 | set => SetAnnotation(MongoDbAnnotationNames.DatabaseSettings, Check.NotNull(value, nameof(DatabaseSettings))); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Blueshift Software, LLC. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.Reflection; 5 | using System.Resources; 6 | 7 | [assembly: NeutralResourcesLanguage("en-US")] 8 | [assembly: AssemblyMetadata("Serviceable", "True")] -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Properties/Blueshift.EntityFrameworkCore.MongoDB.rd.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Properties/DocumentDbStrings.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | 3 | using System.Reflection; 4 | using System.Resources; 5 | using JetBrains.Annotations; 6 | 7 | namespace Microsoft.EntityFrameworkCore.Cosmos.Internal 8 | { 9 | /// 10 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 11 | /// directly from your code. This API may change or be removed in future releases. 12 | /// 13 | public static class DocumentDbStrings 14 | { 15 | private static readonly ResourceManager _resourceManager 16 | = new ResourceManager( 17 | "Blueshift.EntityFrameworkCore.MongoDb.Properties.CosmosStrings", 18 | typeof(DocumentDbStrings).GetTypeInfo().Assembly); 19 | 20 | /// 21 | /// The entity of type '{entityType}' is mapped as a part of the document mapped to '{missingEntityType}', but there is no tracked entity of this type with the corresponding key value. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the key values. 22 | /// 23 | public static string OrphanedNestedDocument( 24 | [CanBeNull] object entityType, 25 | [CanBeNull] object missingEntityType) 26 | => string.Format( 27 | GetString("OrphanedNestedDocument", nameof(entityType), nameof(missingEntityType)), 28 | entityType, missingEntityType); 29 | 30 | /// 31 | /// The entity of type '{entityType}' is mapped as a part of the document mapped to '{missingEntityType}', but there is no tracked entity of this type with the key value '{keyValue}'. 32 | /// 33 | public static string OrphanedNestedDocumentSensitive( 34 | [CanBeNull] object entityType, 35 | [CanBeNull] object missingEntityType, 36 | [CanBeNull] object keyValue) 37 | => string.Format( 38 | GetString("OrphanedNestedDocumentSensitive", nameof(entityType), nameof(missingEntityType), nameof(keyValue)), 39 | entityType, missingEntityType, keyValue); 40 | 41 | /// 42 | /// The entity of type '{entityType}' cannot be queried directly because it is mapped as a part of the document mapped to '{principalEntityType}'. Rewrite the query to start with '{principalEntityType}'. 43 | /// 44 | public static string QueryRootNestedEntityType( 45 | [CanBeNull] object entityType, 46 | [CanBeNull] object principalEntityType) 47 | => string.Format( 48 | GetString("QueryRootNestedEntityType", nameof(entityType), nameof(principalEntityType)), 49 | entityType, principalEntityType); 50 | 51 | /// 52 | /// No matching discriminator values where found for this instance of '{entityType}'. 53 | /// 54 | public static string UnableToDiscriminate([CanBeNull] object entityType) 55 | => string.Format( 56 | GetString("UnableToDiscriminate", nameof(entityType)), 57 | entityType); 58 | 59 | private static string GetString(string name, params string[] formatterNames) 60 | { 61 | var value = _resourceManager.GetString(name); 62 | for (var i = 0; i < formatterNames.Length; i++) 63 | { 64 | value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); 65 | } 66 | 67 | return value; 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Properties/DocumentDbStrings.cs: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Properties/DocumentDbStrings.tt: -------------------------------------------------------------------------------- 1 | <# 2 | Session["ResourceFile"] = "DocumentDbStrings.resx"; 3 | Session["NoDiagnostics"] = true; 4 | #> 5 | -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Query/EntityLoadInfoFactory.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.EntityFrameworkCore.Internal; 4 | using Microsoft.EntityFrameworkCore.Metadata; 5 | using Microsoft.EntityFrameworkCore.Query; 6 | using Microsoft.EntityFrameworkCore.Storage; 7 | using Microsoft.EntityFrameworkCore.Utilities; 8 | 9 | namespace Blueshift.EntityFrameworkCore.MongoDB.Query 10 | { 11 | /// 12 | public class EntityLoadInfoFactory : IEntityLoadInfoFactory 13 | { 14 | [NotNull] private readonly ICurrentDbContext _currentDbContext; 15 | [NotNull] private readonly IValueBufferFactory _valueBufferFactory; 16 | 17 | /// 18 | /// Initializes a new instance of . 19 | /// 20 | /// Used to get the current instance. 21 | /// An that can be used to create 22 | /// for loading documents. 23 | public EntityLoadInfoFactory( 24 | [NotNull] ICurrentDbContext currentDbContext, 25 | [NotNull] IValueBufferFactory valueBufferFactory) 26 | { 27 | _currentDbContext = Check.NotNull(currentDbContext, nameof(currentDbContext)); 28 | _valueBufferFactory = Check.NotNull(valueBufferFactory, nameof(valueBufferFactory)); 29 | } 30 | 31 | /// 32 | public EntityLoadInfo Create(object document, IEntityType entityType, object owner, INavigation owningNavigation) 33 | { 34 | Check.NotNull(document, nameof(document)); 35 | Check.NotNull(entityType, nameof(entityType)); 36 | 37 | if (document.GetType() != entityType.ClrType) 38 | { 39 | Check.IsInstanceOfType(document, entityType.ClrType, nameof(document)); 40 | 41 | entityType = entityType.Model.FindEntityType(document.GetType()); 42 | } 43 | 44 | return new EntityLoadInfo( 45 | new MaterializationContext( 46 | _valueBufferFactory.CreateFromInstance( 47 | document, 48 | entityType, 49 | owner, 50 | owningNavigation), 51 | _currentDbContext.Context), 52 | materializationContext => document, 53 | null); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Query/ExpressionVisitors/DocumentNavigationRewritingExpressionVisitorFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Query; 2 | using Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal; 3 | 4 | namespace Blueshift.EntityFrameworkCore.MongoDB.Query.ExpressionVisitors 5 | { 6 | /// 7 | public class DocumentNavigationRewritingExpressionVisitorFactory : NavigationRewritingExpressionVisitorFactory 8 | { 9 | /// 10 | public override NavigationRewritingExpressionVisitor Create(EntityQueryModelVisitor queryModelVisitor) 11 | => new DocumentNavigationRewritingExpressionVisitor(queryModelVisitor); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Query/ExpressionVisitors/IMongoDbDenormalizedCollectionCompensatingVisitorFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Blueshift.EntityFrameworkCore.MongoDB.Query.ExpressionVisitors 2 | { 3 | /// 4 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 5 | /// directly from your code. This API may change or be removed in future releases. 6 | /// 7 | public interface IMongoDbDenormalizedCollectionCompensatingVisitorFactory 8 | { 9 | /// 10 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 11 | /// directly from your code. This API may change or be removed in future releases. 12 | /// 13 | MongoDbDenormalizedCollectionCompensatingVisitor Create(); 14 | } 15 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Query/ExpressionVisitors/MongoDbDenormalizedCollectionCompensatingVisitorFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Blueshift.EntityFrameworkCore.MongoDB.Query.ExpressionVisitors 2 | { 3 | /// 4 | public class MongoDbDenormalizedCollectionCompensatingVisitorFactory : IMongoDbDenormalizedCollectionCompensatingVisitorFactory 5 | { 6 | /// 7 | public MongoDbDenormalizedCollectionCompensatingVisitor Create() 8 | => new MongoDbDenormalizedCollectionCompensatingVisitor(); 9 | } 10 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Query/ExpressionVisitors/MongoDbEntityQueryableExpressionVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Linq.Expressions; 4 | using System.Reflection; 5 | using Blueshift.EntityFrameworkCore.MongoDB.Query.Expressions; 6 | using JetBrains.Annotations; 7 | using Microsoft.EntityFrameworkCore; 8 | using Microsoft.EntityFrameworkCore.Metadata; 9 | using Microsoft.EntityFrameworkCore.Query.ExpressionVisitors; 10 | using Microsoft.EntityFrameworkCore.Utilities; 11 | using Remotion.Linq.Clauses; 12 | 13 | namespace Blueshift.EntityFrameworkCore.MongoDB.Query.ExpressionVisitors 14 | { 15 | /// 16 | public class MongoDbEntityQueryableExpressionVisitor : EntityQueryableExpressionVisitor 17 | { 18 | private readonly IModel _model; 19 | private readonly IQuerySource _querySource; 20 | private readonly IDocumentQueryExpressionFactory _documentQueryExpressionFactory; 21 | 22 | /// 23 | public MongoDbEntityQueryableExpressionVisitor( 24 | // ReSharper disable once SuggestBaseTypeForParameter 25 | [NotNull] MongoDbEntityQueryModelVisitor entityQueryModelVisitor, 26 | [NotNull] IModel model, 27 | [CanBeNull] IQuerySource querySource, 28 | [NotNull] IDocumentQueryExpressionFactory documentQueryExpressionFactory) 29 | : base(entityQueryModelVisitor) 30 | { 31 | _model = Check.NotNull(model, nameof(model)); 32 | _querySource = querySource; 33 | _documentQueryExpressionFactory = Check.NotNull(documentQueryExpressionFactory, nameof(documentQueryExpressionFactory)); 34 | } 35 | 36 | private new MongoDbEntityQueryModelVisitor QueryModelVisitor => (MongoDbEntityQueryModelVisitor)base.QueryModelVisitor; 37 | 38 | /// 39 | protected override Expression VisitEntityQueryable(Type elementType) 40 | { 41 | Check.NotNull(elementType, nameof(elementType)); 42 | 43 | IEntityType entityType = QueryModelVisitor.QueryCompilationContext.FindEntityType(_querySource) 44 | ?? _model.FindEntityType(elementType); 45 | 46 | entityType = entityType.GetMongoDbCollectionEntityType(); 47 | 48 | var documentQueryExpression = _documentQueryExpressionFactory 49 | .CreateDocumentQueryExpression(entityType); 50 | 51 | if (entityType.ClrType != elementType) 52 | { 53 | MethodInfo ofTypeMethodInfo = MethodHelper 54 | .GetGenericMethodDefinition(() => Enumerable.OfType(null)) 55 | .MakeGenericMethod(elementType); 56 | 57 | documentQueryExpression = Expression.Call( 58 | null, 59 | ofTypeMethodInfo, 60 | documentQueryExpression); 61 | } 62 | 63 | return documentQueryExpression; 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Query/ExpressionVisitors/MongoDbEntityQueryableExpressionVisitorFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Linq.Expressions; 2 | using Blueshift.EntityFrameworkCore.MongoDB.Query.Expressions; 3 | using JetBrains.Annotations; 4 | using Microsoft.EntityFrameworkCore.Metadata; 5 | using Microsoft.EntityFrameworkCore.Query; 6 | using Microsoft.EntityFrameworkCore.Query.ExpressionVisitors; 7 | using Microsoft.EntityFrameworkCore.Utilities; 8 | using Remotion.Linq.Clauses; 9 | 10 | namespace Blueshift.EntityFrameworkCore.MongoDB.Query.ExpressionVisitors 11 | { 12 | /// 13 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 14 | /// directly from your code. This API may change or be removed in future releases. 15 | /// 16 | public class MongoDbEntityQueryableExpressionVisitorFactory : IEntityQueryableExpressionVisitorFactory 17 | { 18 | private readonly IModel _model; 19 | private readonly IDocumentQueryExpressionFactory _documentQueryExpressionFactory; 20 | 21 | /// 22 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 23 | /// directly from your code. This API may change or be removed in future releases. 24 | /// 25 | public MongoDbEntityQueryableExpressionVisitorFactory( 26 | [NotNull] IModel model, 27 | [NotNull] IDocumentQueryExpressionFactory documentQueryExpressionFactory) 28 | { 29 | _model = Check.NotNull(model, nameof(model)); 30 | _documentQueryExpressionFactory = Check.NotNull(documentQueryExpressionFactory, nameof(documentQueryExpressionFactory)); 31 | } 32 | 33 | /// 34 | /// This API supports the Entity Framework Core infrastructure and is not intended to be used 35 | /// directly from your code. This API may change or be removed in future releases. 36 | /// 37 | public virtual ExpressionVisitor Create( 38 | EntityQueryModelVisitor entityQueryModelVisitor, 39 | IQuerySource querySource) 40 | => new MongoDbEntityQueryableExpressionVisitor( 41 | Check.Is(entityQueryModelVisitor, nameof(entityQueryModelVisitor)), 42 | _model, 43 | querySource, 44 | _documentQueryExpressionFactory); 45 | } 46 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Query/ExpressionVisitors/MongoDbMemberAccessBindingExpressionVisitorFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using Microsoft.EntityFrameworkCore.Query; 4 | using Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal; 5 | using Remotion.Linq.Clauses; 6 | 7 | namespace Blueshift.EntityFrameworkCore.MongoDB.Query.ExpressionVisitors 8 | { 9 | /// 10 | public class MongoDbMemberAccessBindingExpressionVisitorFactory : MemberAccessBindingExpressionVisitorFactory 11 | { 12 | /// 13 | public override ExpressionVisitor Create( 14 | QuerySourceMapping querySourceMapping, 15 | EntityQueryModelVisitor queryModelVisitor, 16 | bool inProjection) 17 | => new MongoDbMemberAccessBindingExpressionVisitor( 18 | querySourceMapping, 19 | queryModelVisitor is MongoDbEntityQueryModelVisitor mongoDbEntityQueryModelVisitor 20 | ? mongoDbEntityQueryModelVisitor 21 | : throw new ArgumentException( 22 | @"EntityQueryModelVisitor must be an instance of MongoDbEntityQueryModelVisitor.", 23 | nameof(queryModelVisitor)), 24 | inProjection); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Query/Expressions/DocumentQueryExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using JetBrains.Annotations; 6 | using Microsoft.EntityFrameworkCore.Metadata; 7 | using Microsoft.EntityFrameworkCore.Utilities; 8 | 9 | namespace Blueshift.EntityFrameworkCore.MongoDB.Query.Expressions 10 | { 11 | /// 12 | /// 13 | /// Represents a query for a set of documents from a document database. 14 | /// 15 | public class DocumentQueryExpression : Expression 16 | { 17 | private readonly IDocumentQueryExpressionFactory _documentQueryExpressionFactory; 18 | private readonly IEntityType _entityType; 19 | 20 | /// 21 | /// 22 | /// Creates a new instance of the class. 23 | /// 24 | /// The to use to create 25 | /// the root document query expression. 26 | /// The representing the type of entities to query. 27 | public DocumentQueryExpression( 28 | [NotNull] IDocumentQueryExpressionFactory documentQueryExpressionFactory, 29 | [NotNull] IEntityType entityType) 30 | { 31 | _documentQueryExpressionFactory = Check.NotNull(documentQueryExpressionFactory, nameof(documentQueryExpressionFactory)); 32 | _entityType = Check.NotNull(entityType, nameof(entityType)); 33 | } 34 | 35 | /// 36 | public override bool CanReduce => true; 37 | 38 | /// 39 | public override Expression Reduce() 40 | => _documentQueryExpressionFactory 41 | .CreateDocumentQueryExpression(_entityType); 42 | 43 | /// 44 | public override Type Type 45 | => typeof(IQueryable<>).MakeGenericType(_entityType.ClrType); 46 | 47 | /// 48 | protected override Expression VisitChildren(ExpressionVisitor visitor) => this; 49 | 50 | /// 51 | public override ExpressionType NodeType => ExpressionType.Extension; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Query/Expressions/IDocumentQueryExpressionFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Linq.Expressions; 2 | using Microsoft.EntityFrameworkCore.Metadata; 3 | 4 | namespace Blueshift.EntityFrameworkCore.MongoDB.Query.Expressions 5 | { 6 | /// 7 | /// Interface for a service that can be used to generate a document query expression. 8 | /// 9 | public interface IDocumentQueryExpressionFactory 10 | { 11 | /// 12 | /// Creates an that represents a query for documents of a given entity type. 13 | /// 14 | /// The that represents that documents to query. 15 | /// An that represents a query for documents of a given entity type. 16 | Expression CreateDocumentQueryExpression(IEntityType entityType); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Query/IEntityLoadInfoFactory.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using Microsoft.EntityFrameworkCore.Metadata; 3 | using Microsoft.EntityFrameworkCore.Query; 4 | 5 | namespace Blueshift.EntityFrameworkCore.MongoDB.Query 6 | { 7 | /// 8 | /// Interface for a service that can create instances of . 9 | /// 10 | public interface IEntityLoadInfoFactory 11 | { 12 | /// 13 | /// Creates a new instance of for the given instance. 14 | /// 15 | /// The object for which the will be created. 16 | /// The representing the type of . 17 | /// The entity instance that owns , if any. 18 | /// The that describes the ownership, if any. 19 | /// A new instance of that can be used to load the given . 20 | EntityLoadInfo Create( 21 | [NotNull] object document, 22 | [NotNull] IEntityType entityType, 23 | [CanBeNull] object owner, 24 | [CanBeNull] INavigation owningNavigation); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Query/IValueBufferFactory.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using Microsoft.EntityFrameworkCore.Metadata; 3 | using Microsoft.EntityFrameworkCore.Storage; 4 | 5 | namespace Blueshift.EntityFrameworkCore.MongoDB.Query 6 | { 7 | /// 8 | /// Interface for a service that can create instances. 9 | /// 10 | public interface IValueBufferFactory 11 | { 12 | /// 13 | /// Creates an instance of from the given . 14 | /// 15 | /// An existing object instance to use to build the value buffer. 16 | /// The containing the metadata for . 17 | /// The entity that owns , if any. 18 | /// The that describes the ownership, if any. 19 | /// A new that reflects the properties of the given . 20 | ValueBuffer CreateFromInstance( 21 | [NotNull] object instance, 22 | [NotNull] IEntityType entityType, 23 | [CanBeNull] object owner, 24 | [CanBeNull] INavigation owningNavigation); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Query/LinqQueryCompilationContextFactory.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using Microsoft.EntityFrameworkCore.Query; 3 | using Microsoft.EntityFrameworkCore.Query.Internal; 4 | 5 | namespace Blueshift.EntityFrameworkCore.MongoDB.Query 6 | { 7 | /// 8 | public class LinqQueryCompilationContextFactory : QueryCompilationContextFactory 9 | { 10 | /// 11 | public LinqQueryCompilationContextFactory( 12 | [NotNull] QueryCompilationContextDependencies dependencies) : base(dependencies) 13 | { 14 | } 15 | 16 | /// 17 | public override QueryCompilationContext Create(bool async) 18 | => new QueryCompilationContext( 19 | Dependencies, 20 | new QueryableLinqOperatorProvider(), 21 | TrackQueryResults); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Query/MongoDbEntityQueryModelVisitorFactory.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using Microsoft.EntityFrameworkCore.Query; 3 | using Microsoft.EntityFrameworkCore.Utilities; 4 | 5 | namespace Blueshift.EntityFrameworkCore.MongoDB.Query 6 | { 7 | /// 8 | public class MongoDbEntityQueryModelVisitorFactory : EntityQueryModelVisitorFactory 9 | { 10 | /// 11 | public MongoDbEntityQueryModelVisitorFactory( 12 | [NotNull] EntityQueryModelVisitorDependencies entityQueryModelVisitorDependencies, 13 | [NotNull] MongoDbEntityQueryModelVisitorDependencies mongoDbEntityQueryModelVisitorDependencies) 14 | : base(Check.NotNull(entityQueryModelVisitorDependencies, nameof(entityQueryModelVisitorDependencies))) 15 | { 16 | MongoDbDependencies 17 | = Check.NotNull(mongoDbEntityQueryModelVisitorDependencies, nameof(mongoDbEntityQueryModelVisitorDependencies)); 18 | } 19 | 20 | /// 21 | /// Dependencies used to create a . 22 | /// 23 | public MongoDbEntityQueryModelVisitorDependencies MongoDbDependencies { get; } 24 | 25 | /// 26 | public override EntityQueryModelVisitor Create( 27 | QueryCompilationContext queryCompilationContext, 28 | EntityQueryModelVisitor parentEntityQueryModelVisitor) 29 | => new MongoDbEntityQueryModelVisitor( 30 | Dependencies, 31 | Check.NotNull(queryCompilationContext, nameof(queryCompilationContext)), 32 | MongoDbDependencies); 33 | } 34 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Query/MongoDbQueryContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using JetBrains.Annotations; 3 | using Microsoft.EntityFrameworkCore.Query; 4 | using Microsoft.EntityFrameworkCore.Query.Internal; 5 | using Microsoft.EntityFrameworkCore.Utilities; 6 | 7 | namespace Blueshift.EntityFrameworkCore.MongoDB.Query 8 | { 9 | /// 10 | public class MongoDbQueryContext : QueryContext 11 | { 12 | /// 13 | public MongoDbQueryContext( 14 | [NotNull] QueryContextDependencies queryContextDependencies, 15 | [NotNull] Func queryBufferFactory) 16 | : base( 17 | Check.NotNull(queryContextDependencies, nameof(queryContextDependencies)), 18 | Check.NotNull(queryBufferFactory, nameof(queryBufferFactory)) 19 | ) 20 | { 21 | } 22 | 23 | /// 24 | public override void BeginTrackingQuery() 25 | { 26 | Check.NotNull(QueryBuffer, nameof(QueryBuffer)); 27 | base.BeginTrackingQuery(); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Query/MongoDbQueryContextFactory.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | using Microsoft.EntityFrameworkCore.Query; 3 | using Microsoft.EntityFrameworkCore.Query.Internal; 4 | using Microsoft.EntityFrameworkCore.Utilities; 5 | 6 | namespace Blueshift.EntityFrameworkCore.MongoDB.Query 7 | { 8 | /// 9 | public class MongoDbQueryContextFactory : QueryContextFactory 10 | { 11 | [NotNull] private readonly IEntityLoadInfoFactory _entityLoadInfoFactory; 12 | 13 | /// 14 | public MongoDbQueryContextFactory( 15 | [NotNull] QueryContextDependencies queryContextDependencies, 16 | [NotNull] IEntityLoadInfoFactory entityLoadInfoFactory) 17 | : base( 18 | Check.NotNull(queryContextDependencies, nameof(queryContextDependencies))) 19 | { 20 | _entityLoadInfoFactory = Check.NotNull(entityLoadInfoFactory, nameof(entityLoadInfoFactory)); 21 | } 22 | 23 | /// 24 | public override QueryContext Create() 25 | => new MongoDbQueryContext( 26 | Dependencies, 27 | CreateQueryBuffer); 28 | 29 | /// 30 | protected override IQueryBuffer CreateQueryBuffer() 31 | => new MongoDbQueryBuffer( 32 | Dependencies, 33 | _entityLoadInfoFactory); 34 | } 35 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Storage/IMongoDbConnection.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using MongoDB.Driver; 4 | using MongoDB.Driver.Linq; 5 | 6 | namespace Blueshift.EntityFrameworkCore.MongoDB.Storage 7 | { 8 | /// 9 | /// An interface for a service that can be used to interact with a MongoDB instance. 10 | /// 11 | public interface IMongoDbConnection 12 | { 13 | /// 14 | /// Gets the used by the current model. 15 | /// 16 | /// The used by the MongoDB C# driver to communicate with the MongoDB instance. 17 | IMongoDatabase GetDatabase(); 18 | 19 | /// 20 | /// Asynchronously gets the used by the current model. 21 | /// 22 | /// A to observe while waiting for the task to complete. 23 | /// 24 | /// A representing the state of the operation. The result contains The 25 | /// used by the MongoDB C# driver to communicate with the MongoDB instance. 26 | /// 27 | Task GetDatabaseAsync(CancellationToken cancellationToken = default(CancellationToken)); 28 | 29 | /// 30 | /// Drops the database used by this model from the MongoDB instance. 31 | /// 32 | void DropDatabase(); 33 | 34 | /// 35 | /// Asynchronously drops the database used by this model from the MongoDB instance. 36 | /// 37 | /// A to observe while waiting for the task to complete. 38 | /// A representing the state of the operation. 39 | Task DropDatabaseAsync(CancellationToken cancellationToken = default(CancellationToken)); 40 | 41 | /// 42 | /// Gets a instance that can be used to store instances of . 43 | /// 44 | /// The type of entity stored in the collection. 45 | /// The instance that can store . 46 | IMongoCollection GetCollection(); 47 | } 48 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Storage/IMongoDbTypeMappingSource.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Storage; 2 | 3 | namespace Blueshift.EntityFrameworkCore.MongoDB.Storage 4 | { 5 | /// 6 | public interface IMongoDbTypeMappingSource : ITypeMappingSource 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Storage/MongoDbConnection.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using Blueshift.EntityFrameworkCore.MongoDB.Metadata; 4 | using Blueshift.EntityFrameworkCore.MongoDB.Metadata.Builders; 5 | using JetBrains.Annotations; 6 | using Microsoft.EntityFrameworkCore; 7 | using Microsoft.EntityFrameworkCore.Metadata; 8 | using Microsoft.EntityFrameworkCore.Utilities; 9 | using MongoDB.Driver; 10 | 11 | namespace Blueshift.EntityFrameworkCore.MongoDB.Storage 12 | { 13 | /// 14 | /// A service that can be used to interact with a MongoDB instance. 15 | /// 16 | public class MongoDbConnection : IMongoDbConnection 17 | { 18 | private readonly IMongoClient _mongoClient; 19 | private readonly IMongoDatabase _mongoDatabase; 20 | private readonly IModel _model; 21 | 22 | /// 23 | /// Initializes a new instance of the class. 24 | /// 25 | /// The used to communicate with the MongoDB instance. 26 | /// The used by this connection. 27 | public MongoDbConnection( 28 | [NotNull] IMongoClient mongoClient, 29 | [NotNull] IModel model) 30 | { 31 | _model = Check.NotNull(model, nameof(model)); 32 | 33 | _mongoClient = Check.NotNull(mongoClient, nameof(mongoClient)); 34 | _mongoDatabase = _mongoClient.GetDatabase(new MongoDbModelAnnotations(model).Database); 35 | } 36 | 37 | /// 38 | public virtual IMongoDatabase GetDatabase() 39 | => _mongoDatabase; 40 | 41 | /// 42 | public virtual Task GetDatabaseAsync(CancellationToken cancellationToken = default(CancellationToken)) 43 | => Task.FromResult(_mongoDatabase); 44 | 45 | /// 46 | public virtual void DropDatabase() 47 | => _mongoClient.DropDatabase(new MongoDbModelAnnotations(_model).Database); 48 | 49 | /// 50 | public virtual Task DropDatabaseAsync(CancellationToken cancellationToken = default(CancellationToken)) 51 | => _mongoClient.DropDatabaseAsync(new MongoDbModelAnnotations(_model).Database, cancellationToken); 52 | 53 | /// 54 | public virtual IMongoCollection GetCollection() 55 | { 56 | IEntityType collectionEntityType = _model 57 | .FindEntityType(typeof(TEntity)) 58 | .GetMongoDbCollectionEntityType(); 59 | 60 | MongoDbEntityTypeAnnotations annotations = collectionEntityType.MongoDb(); 61 | 62 | return _mongoDatabase.GetCollection(annotations.CollectionName, annotations.CollectionSettings); 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/Storage/MongoDbTypeMappingSource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Reflection; 5 | using JetBrains.Annotations; 6 | using Microsoft.EntityFrameworkCore.Storage; 7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 8 | using MongoDB.Bson; 9 | 10 | namespace Blueshift.EntityFrameworkCore.MongoDB.Storage 11 | { 12 | /// 13 | /// 14 | /// 15 | /// Determines whether a .NET type can be mapped to a MongoDB database type. 16 | /// 17 | public class MongoDbTypeMappingSource : TypeMappingSource, IMongoDbTypeMappingSource 18 | { 19 | private readonly ConcurrentDictionary _typeCache = new ConcurrentDictionary 20 | { 21 | [typeof(string)] = new PassThruTypeMapping(typeof(string)), 22 | [typeof(IEnumerable)] = new PassThruTypeMapping(typeof(IEnumerable)), 23 | [typeof(ObjectId)] = new PassThruTypeMapping(typeof(ObjectId)), 24 | [typeof(IEnumerable)] = new PassThruTypeMapping(typeof(IEnumerable)), 25 | [typeof(byte[])] = new PassThruTypeMapping(typeof(byte[])), 26 | [typeof(IEnumerable)] = new PassThruTypeMapping(typeof(IEnumerable)) 27 | }; 28 | 29 | /// 30 | public MongoDbTypeMappingSource([NotNull] TypeMappingSourceDependencies dependencies) 31 | : base(dependencies) 32 | { 33 | } 34 | 35 | /// 36 | protected override CoreTypeMapping FindMapping(in TypeMappingInfo mappingInfo) 37 | => _typeCache.GetOrAdd( 38 | mappingInfo.ClrType, 39 | clrType => 40 | { 41 | TypeInfo typeInfo = (clrType.TryGetSequenceType() ?? clrType).UnwrapNullableType().GetTypeInfo(); 42 | 43 | return typeInfo.IsPrimitive 44 | || typeInfo.IsValueType 45 | ? new PassThruTypeMapping(clrType) 46 | : null; 47 | }); 48 | 49 | private class PassThruTypeMapping : CoreTypeMapping 50 | { 51 | private PassThruTypeMapping(CoreTypeMappingParameters parameters) 52 | : base(parameters) 53 | { 54 | } 55 | 56 | public PassThruTypeMapping([NotNull] Type clrType) 57 | : base(new CoreTypeMappingParameters(clrType)) 58 | { 59 | } 60 | 61 | public override CoreTypeMapping Clone(ValueConverter converter) 62 | => new PassThruTypeMapping(Parameters.WithComposedConverter(converter)); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/ValueGeneration/HashCodeValueGenerator.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.ChangeTracking; 2 | using Microsoft.EntityFrameworkCore.ValueGeneration; 3 | using MongoDB.Bson; 4 | 5 | namespace Blueshift.EntityFrameworkCore.MongoDB.ValueGeneration 6 | { 7 | /// 8 | public class HashCodeValueGenerator : ValueGenerator 9 | { 10 | /// 11 | /// 12 | /// Generates a new value. 13 | /// 14 | /// The whose value is to be generated. 15 | /// A new for . 16 | public override int? Next(EntityEntry entry) 17 | => entry.Entity?.GetHashCode(); 18 | 19 | /// 20 | /// 21 | /// true if this generates temporary values; 22 | /// otherwise false. 23 | /// 24 | /// Always returns true. 25 | public override bool GeneratesTemporaryValues => false; 26 | } 27 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/ValueGeneration/IntegerValueGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Threading; 4 | using Microsoft.EntityFrameworkCore.ChangeTracking; 5 | using Microsoft.EntityFrameworkCore.ValueGeneration; 6 | using MongoDB.Bson; 7 | 8 | namespace Blueshift.EntityFrameworkCore.MongoDB.ValueGeneration 9 | { 10 | /// 11 | public class IntegerValueGenerator : ValueGenerator 12 | { 13 | private long _currentValue; 14 | 15 | /// 16 | /// 17 | /// Generates a new value. 18 | /// 19 | /// The whose value is to be generated. 20 | /// A new for . 21 | public override TValue Next(EntityEntry entry) 22 | => (TValue) Convert.ChangeType( 23 | Interlocked.Increment(ref _currentValue), 24 | typeof(TValue), 25 | CultureInfo.InvariantCulture); 26 | 27 | /// 28 | /// 29 | /// true if this generates temporary values; 30 | /// otherwise false. 31 | /// 32 | /// Always returns false. 33 | public override bool GeneratesTemporaryValues => false; 34 | } 35 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/ValueGeneration/MongoDbValueGeneratorSelector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using JetBrains.Annotations; 4 | using Microsoft.EntityFrameworkCore.Metadata; 5 | using Microsoft.EntityFrameworkCore.Utilities; 6 | using Microsoft.EntityFrameworkCore.ValueGeneration; 7 | using MongoDB.Bson; 8 | 9 | namespace Blueshift.EntityFrameworkCore.MongoDB.ValueGeneration 10 | { 11 | /// 12 | public class MongoDbValueGeneratorSelector : ValueGeneratorSelector 13 | { 14 | private readonly IDictionary> _valueGeneratorMap = 15 | new Dictionary> 16 | { 17 | [typeof(ObjectId)] = () => new ObjectIdValueGenerator(), 18 | [typeof(short)] = () => new IntegerValueGenerator(), 19 | [typeof(int)] = () => new IntegerValueGenerator(), 20 | [typeof(long)] = () => new IntegerValueGenerator(), 21 | }; 22 | 23 | private readonly IDictionary> _shadowKeyGeneratorMap = 24 | new Dictionary> 25 | { 26 | [typeof(int)] = () => new HashCodeValueGenerator() 27 | }; 28 | 29 | /// 30 | /// 31 | /// Initializes a new instance of the class. 32 | /// 33 | /// Parameter object containing dependencies for this service. 34 | public MongoDbValueGeneratorSelector([NotNull] ValueGeneratorSelectorDependencies dependencies) 35 | : base(dependencies) 36 | { 37 | } 38 | 39 | /// 40 | /// 41 | /// Creates a new value generator for the given property. 42 | /// 43 | /// The property to get the value generator for. 44 | /// 45 | /// The entity type that the value generator will be used for. When called on inherited 46 | /// properties on derived entity types, this entity type may be different from the 47 | /// declared entity type on property 48 | /// 49 | /// The newly created value generator. 50 | public override ValueGenerator Create( 51 | IProperty property, 52 | IEntityType entityType) 53 | { 54 | Check.NotNull(property, nameof(property)); 55 | Check.NotNull(entityType, nameof(entityType)); 56 | 57 | IDictionary> valueGeneratorCreator = property.IsShadowProperty 58 | ? _shadowKeyGeneratorMap 59 | : _valueGeneratorMap; 60 | 61 | return valueGeneratorCreator 62 | .TryGetValue( 63 | property.ClrType.UnwrapNullableType(), 64 | out Func valueGeneratorFactory) 65 | ? valueGeneratorFactory() 66 | : base.Create(property, entityType); 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /src/Blueshift.EntityFrameworkCore.MongoDB/ValueGeneration/ObjectIdValueGenerator.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.ChangeTracking; 2 | using Microsoft.EntityFrameworkCore.ValueGeneration; 3 | using MongoDB.Bson; 4 | 5 | namespace Blueshift.EntityFrameworkCore.MongoDB.ValueGeneration 6 | { 7 | /// 8 | /// Generates values for properties when an entity is added to a context. 9 | /// 10 | public class ObjectIdValueGenerator : ValueGenerator 11 | { 12 | /// 13 | /// Generates a new value. 14 | /// 15 | /// The whose value is to be generated. 16 | /// A new for . 17 | public override ObjectId Next(EntityEntry entry) 18 | => ObjectId.GenerateNewId(); 19 | 20 | /// 21 | /// true if this generates temporary values; 22 | /// otherwise false. 23 | /// 24 | /// Always returns false. 25 | public override bool GeneratesTemporaryValues => false; 26 | } 27 | } -------------------------------------------------------------------------------- /src/Blueshift.Identity.MongoDB/Blueshift.Identity.MongoDB.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(BuildFrameworks) 5 | Blueshift MongoDB EntityFrameworkCore Identity Provider for Microsoft ASP.NET Core 6 | Blueshift Software MongoDB EntityFrameworkCore Identity Provider for Microsoft ASP.NET Core 7 | true 8 | $(PackageTags);AspNetCore;Identity;Membership 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/Blueshift.Identity.MongoDB/MongoDbIdentityClaim.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Claims; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.EntityFrameworkCore.Utilities; 4 | 5 | namespace Blueshift.Identity.MongoDB 6 | { 7 | /// 8 | /// A representation of an authorization claim for use with a MongoDB EntityFramework provider. 9 | /// 10 | [Owned] 11 | public class MongoDbIdentityClaim 12 | { 13 | /// 14 | /// Initializes a new instance of the class. 15 | /// 16 | public MongoDbIdentityClaim() { } 17 | 18 | /// 19 | /// Initializes a new instance of the class. 20 | /// 21 | /// The security to use to initialize this role claim. 22 | public MongoDbIdentityClaim(Claim claim) 23 | { 24 | InitializeFromClaim(claim); 25 | } 26 | 27 | /// 28 | /// Gets or sets the claim type for this claim. 29 | /// 30 | public virtual string ClaimType { get; set; } 31 | 32 | /// 33 | /// Gets or sets the claim value for this claim. 34 | /// 35 | public virtual string ClaimValue { get; set; } 36 | 37 | /// 38 | /// Constructs a new claim with the type and value. 39 | /// 40 | /// A that represents this . 41 | public virtual Claim ToClaim() 42 | => new Claim(ClaimType, ClaimValue); 43 | 44 | /// 45 | /// Initializes this with values from the given . 46 | /// 47 | /// The source to use for initialization. 48 | public virtual void InitializeFromClaim(Claim claim) 49 | { 50 | Check.NotNull(claim, nameof(claim)); 51 | ClaimType = claim.Type; 52 | ClaimValue = claim.Value; 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/Blueshift.Identity.MongoDB/MongoDbIdentityUserRole.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | 3 | namespace Blueshift.Identity.MongoDB 4 | { 5 | /// 6 | /// A representation of a user's security authorization role for use with a MongoDB EntityFramework provider. 7 | /// 8 | [Owned] 9 | public class MongoDbIdentityUserRole 10 | { 11 | /// 12 | /// Gets or sets the name of the role that the user is in. 13 | /// 14 | public string RoleName { get; set; } 15 | 16 | /// 17 | /// Gets or sets the normalized name of the role that the user is in. 18 | /// 19 | public string NormalizedRoleName { get; set; } 20 | } 21 | } -------------------------------------------------------------------------------- /src/Blueshift.Identity.MongoDB/MongoDbIdentityUserToken.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | 3 | namespace Blueshift.Identity.MongoDB 4 | { 5 | /// 6 | /// A representation of an external user login provider token for use with a MongoDB EntityFramework provider. 7 | /// 8 | [Owned] 9 | public class MongoDbIdentityUserToken 10 | { 11 | /// 12 | /// Gets or sets the name of the token. 13 | /// 14 | public virtual string Name { get; set; } 15 | 16 | /// 17 | /// Gets or sets the token value. 18 | /// 19 | public virtual string Value { get; set; } 20 | } 21 | } -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | netstandard2.0 6 | $(DeveloperBuildFrameworks) 7 | netstandard2.0 8 | net461;$(BuildFrameworks) 9 | 10 | 11 | 12 | $(NoWarn);CA1822 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Shared/MemberInfoExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | namespace System.Reflection 5 | { 6 | internal static class MemberInfoExtensions 7 | { 8 | public static Type GetMemberType(this MemberInfo memberInfo) 9 | => (memberInfo as PropertyInfo)?.PropertyType ?? ((FieldInfo)memberInfo)?.FieldType; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Shared/PropertyInfoExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using JetBrains.Annotations; 7 | 8 | // ReSharper disable once CheckNamespace 9 | namespace System.Reflection 10 | { 11 | [DebuggerStepThrough] 12 | internal static class PropertyInfoExtensions 13 | { 14 | public static bool IsStatic(this PropertyInfo property) 15 | => (property.GetMethod ?? property.SetMethod).IsStatic; 16 | 17 | public static bool IsCandidateProperty(this PropertyInfo propertyInfo, bool needsWrite = true) 18 | => !propertyInfo.IsStatic() 19 | && propertyInfo.GetIndexParameters().Length == 0 20 | && propertyInfo.CanRead 21 | && (!needsWrite || propertyInfo.CanWrite) 22 | && propertyInfo.GetMethod != null && propertyInfo.GetMethod.IsPublic; 23 | 24 | public static Type FindCandidateNavigationPropertyType(this PropertyInfo propertyInfo, Func isPrimitiveProperty) 25 | { 26 | var targetType = propertyInfo.PropertyType; 27 | var targetSequenceType = targetType.TryGetSequenceType(); 28 | if (!propertyInfo.IsCandidateProperty(targetSequenceType == null)) 29 | { 30 | return null; 31 | } 32 | 33 | targetType = targetSequenceType ?? targetType; 34 | targetType = targetType.UnwrapNullableType(); 35 | 36 | if (isPrimitiveProperty(targetType) 37 | || targetType.GetTypeInfo().IsInterface 38 | || targetType.GetTypeInfo().IsValueType 39 | || targetType == typeof(object)) 40 | { 41 | return null; 42 | } 43 | 44 | return targetType; 45 | } 46 | 47 | public static PropertyInfo FindGetterProperty([NotNull] this PropertyInfo propertyInfo) 48 | => propertyInfo.DeclaringType 49 | .GetPropertiesInHierarchy(propertyInfo.Name) 50 | .FirstOrDefault(p => p.GetMethod != null); 51 | 52 | public static PropertyInfo FindSetterProperty([NotNull] this PropertyInfo propertyInfo) 53 | => propertyInfo.DeclaringType 54 | .GetPropertiesInHierarchy(propertyInfo.Name) 55 | .FirstOrDefault(p => p.SetMethod != null); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Shared/StringBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.Collections.Generic; 5 | 6 | namespace System.Text 7 | { 8 | internal static class StringBuilderExtensions 9 | { 10 | public static StringBuilder AppendJoin( 11 | this StringBuilder stringBuilder, IEnumerable values, string separator = ", ") 12 | => stringBuilder.AppendJoin(values, (sb, value) => sb.Append(value), separator); 13 | 14 | public static StringBuilder AppendJoin( 15 | this StringBuilder stringBuilder, string separator, params string[] values) 16 | => stringBuilder.AppendJoin(values, (sb, value) => sb.Append(value), separator); 17 | 18 | public static StringBuilder AppendJoin( 19 | this StringBuilder stringBuilder, 20 | IEnumerable values, 21 | Action joinAction, 22 | string separator = ", ") 23 | { 24 | var appended = false; 25 | 26 | foreach (var value in values) 27 | { 28 | joinAction(stringBuilder, value); 29 | stringBuilder.Append(separator); 30 | appended = true; 31 | } 32 | 33 | if (appended) 34 | { 35 | stringBuilder.Length -= separator.Length; 36 | } 37 | 38 | return stringBuilder; 39 | } 40 | 41 | public static StringBuilder AppendJoin( 42 | this StringBuilder stringBuilder, 43 | IEnumerable values, 44 | TParam param, 45 | Action joinAction, 46 | string separator = ", ") 47 | { 48 | var appended = false; 49 | 50 | foreach (var value in values) 51 | { 52 | joinAction(stringBuilder, value, param); 53 | stringBuilder.Append(separator); 54 | appended = true; 55 | } 56 | 57 | if (appended) 58 | { 59 | stringBuilder.Length -= separator.Length; 60 | } 61 | 62 | return stringBuilder; 63 | } 64 | 65 | public static StringBuilder AppendJoin( 66 | this StringBuilder stringBuilder, 67 | IEnumerable values, 68 | TParam1 param1, 69 | TParam2 param2, 70 | Action joinAction, 71 | string separator = ", ") 72 | { 73 | var appended = false; 74 | 75 | foreach (var value in values) 76 | { 77 | joinAction(stringBuilder, value, param1, param2); 78 | stringBuilder.Append(separator); 79 | appended = true; 80 | } 81 | 82 | if (appended) 83 | { 84 | stringBuilder.Length -= separator.Length; 85 | } 86 | 87 | return stringBuilder; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /test/Blueshift.EntityFrameworkCore.MongoDB.Tests/Adapter/Conventions/AbstractClassConventionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using Blueshift.EntityFrameworkCore.MongoDB.Adapter.Conventions; 4 | using Blueshift.EntityFrameworkCore.MongoDB.SampleDomain; 5 | using MongoDB.Bson.Serialization; 6 | using Xunit; 7 | 8 | namespace Blueshift.EntityFrameworkCore.MongoDB.Tests.Adapter.Conventions 9 | { 10 | public class AbstractClassConventionTest 11 | { 12 | [Theory] 13 | [InlineData(typeof(Animal))] 14 | [InlineData(typeof(Tiger))] 15 | [InlineData(typeof(PolarBear))] 16 | [InlineData(typeof(Otter))] 17 | [InlineData(typeof(SeaOtter))] 18 | [InlineData(typeof(EurasianOtter))] 19 | [InlineData(typeof(Employee))] 20 | public void Sets_is_root_class_and_discriminator_required_true_for_abstract_type(Type type) 21 | { 22 | var classMap = new BsonClassMap(type); 23 | var abstractClassMapConvention = new AbstractBaseClassConvention(); 24 | abstractClassMapConvention.Apply(classMap); 25 | Assert.Equal(type.GetTypeInfo().IsAbstract, classMap.DiscriminatorIsRequired); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /test/Blueshift.EntityFrameworkCore.MongoDB.Tests/Adapter/Conventions/IgnoreEmptyEnumerablesConventionTests.cs: -------------------------------------------------------------------------------- 1 | using Blueshift.EntityFrameworkCore.MongoDB.Adapter.Conventions; 2 | using Blueshift.EntityFrameworkCore.MongoDB.SampleDomain; 3 | using MongoDB.Bson.Serialization; 4 | using Xunit; 5 | 6 | namespace Blueshift.EntityFrameworkCore.MongoDB.Tests.Adapter.Conventions 7 | { 8 | public class IgnoreEmptyEnumerablesConventionTests 9 | { 10 | [Fact] 11 | public void Should_not_serialize_empty_enumerables() 12 | { 13 | var bsonClassMap = new BsonClassMap(); 14 | BsonMemberMap bsonMemberMap = bsonClassMap.MapMember(e => e.Specialties); 15 | var ignoreEmptyEnumerableConvention = new IgnoreEmptyEnumerablesConvention(); 16 | ignoreEmptyEnumerableConvention.Apply(bsonMemberMap); 17 | var employee = new Employee(); 18 | employee.Specialties.Clear(); 19 | Assert.False(bsonMemberMap.ShouldSerialize(employee, employee.Specialties)); 20 | } 21 | 22 | [Fact] 23 | public void Should_serialize_non_empty_enumerables() 24 | { 25 | var bsonClassMap = new BsonClassMap(); 26 | BsonMemberMap bsonMemberMap = bsonClassMap.MapMember(e => e.Specialties); 27 | var ignoreEmptyEnumerableConvention = new IgnoreEmptyEnumerablesConvention(); 28 | ignoreEmptyEnumerableConvention.Apply(bsonMemberMap); 29 | var employee = new Employee 30 | { 31 | Specialties = 32 | { 33 | new Specialty { AnimalType = nameof(Tiger), Task = ZooTask.Feeding }, 34 | new Specialty { AnimalType = nameof(PolarBear), Task = ZooTask.Feeding }, 35 | new Specialty { AnimalType = nameof(Otter), Task = ZooTask.Feeding } 36 | } 37 | }; 38 | Assert.True(bsonMemberMap.ShouldSerialize(employee, employee.Specialties)); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /test/Blueshift.EntityFrameworkCore.MongoDB.Tests/Adapter/Conventions/IgnoreNullOrEmptyStringsConventionTests.cs: -------------------------------------------------------------------------------- 1 | using Blueshift.EntityFrameworkCore.MongoDB.Adapter.Conventions; 2 | using Blueshift.EntityFrameworkCore.MongoDB.SampleDomain; 3 | using MongoDB.Bson.Serialization; 4 | using Xunit; 5 | 6 | namespace Blueshift.EntityFrameworkCore.MongoDB.Tests.Adapter.Conventions 7 | { 8 | public class IgnoreNullOrEmptyStringsConventionTests 9 | { 10 | [Theory] 11 | [InlineData(null)] 12 | [InlineData("")] 13 | [InlineData(" \t\v\r\n")] 14 | [InlineData("TestData")] 15 | public void Should_not_serialize_null_or_empty_strings(string value) 16 | { 17 | var bsonClassMap = new BsonClassMap(); 18 | BsonMemberMap bsonMemberMap = bsonClassMap.MapMember(e => e.FirstName); 19 | var ignoreNullOrEmptyStringsConvention = new IgnoreNullOrEmptyStringsConvention(); 20 | ignoreNullOrEmptyStringsConvention.Apply(bsonMemberMap); 21 | var employee = new Employee 22 | { 23 | FirstName = value 24 | }; 25 | Assert.Equal(!string.IsNullOrEmpty(value), bsonMemberMap.ShouldSerialize(employee, employee.FirstName)); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /test/Blueshift.EntityFrameworkCore.MongoDB.Tests/Adapter/Conventions/KeyAttributeConventionTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.Reflection; 4 | using Blueshift.EntityFrameworkCore.MongoDB.Adapter.Conventions; 5 | using Blueshift.EntityFrameworkCore.MongoDB.SampleDomain; 6 | using MongoDB.Bson.Serialization; 7 | using Xunit; 8 | 9 | namespace Blueshift.EntityFrameworkCore.MongoDB.Tests.Adapter.Conventions 10 | { 11 | public class KeyAttributeConventionTests 12 | { 13 | [Fact] 14 | public void Should_set_id_member_when_key_attribute_present() 15 | { 16 | MemberInfo memberInfo = typeof(Animal) 17 | .GetTypeInfo() 18 | .GetProperty(nameof(Animal.AnimalId)); 19 | Assert.NotNull(memberInfo); 20 | Assert.True(memberInfo.IsDefined(typeof(KeyAttribute), false)); 21 | var keyAttributeConvention = new KeyAttributeConvention(); 22 | var bsonClasspMap = new BsonClassMap(); 23 | BsonMemberMap bsonMemberMap = bsonClasspMap.MapMember(memberInfo); 24 | keyAttributeConvention.Apply(bsonMemberMap); 25 | Assert.Same(bsonMemberMap, bsonClasspMap.IdMemberMap); 26 | } 27 | 28 | [Theory] 29 | [InlineData(nameof(Employee.FirstName))] 30 | [InlineData(nameof(Employee.Age))] 31 | public void Should_not_set_id_member_when_key_attribute_present(string memberName) 32 | { 33 | MemberInfo memberInfo = typeof(Employee) 34 | .GetTypeInfo() 35 | .GetProperty(memberName); 36 | Assert.NotNull(memberInfo); 37 | Assert.False(memberInfo.IsDefined(typeof(KeyAttribute), false)); 38 | var keyAttributeConvention = new KeyAttributeConvention(); 39 | var bsonClasspMap = new BsonClassMap(); 40 | BsonMemberMap bsonMemberMap = bsonClasspMap.MapMember(memberInfo); 41 | keyAttributeConvention.Apply(bsonMemberMap); 42 | Assert.NotSame(bsonMemberMap, bsonClasspMap.IdMemberMap); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /test/Blueshift.EntityFrameworkCore.MongoDB.Tests/Adapter/EntityFrameworkConventionPackTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using Blueshift.EntityFrameworkCore.MongoDB.Adapter; 4 | using Blueshift.EntityFrameworkCore.MongoDB.Adapter.Conventions; 5 | using MongoDB.Bson.Serialization.Conventions; 6 | using Xunit; 7 | 8 | namespace Blueshift.EntityFrameworkCore.MongoDB.Tests.Adapter 9 | { 10 | public class EntityFrameworkConventionPackTests 11 | { 12 | [Theory] 13 | [InlineData(typeof(AbstractBaseClassConvention))] 14 | [InlineData(typeof(KeyAttributeConvention))] 15 | [InlineData(typeof(NavigationSrializationMemberMapConvention))] 16 | [InlineData(typeof(NotMappedAttributeConvention))] 17 | public void Singleton_contains_default_convention_set(Type conventionType) 18 | { 19 | ConventionPack conventionPack = EntityFrameworkConventionPack.Instance; 20 | Assert.Contains(conventionPack, conventionType.GetTypeInfo().IsInstanceOfType); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /test/Blueshift.EntityFrameworkCore.MongoDB.Tests/ApiConsistencyTest.cs: -------------------------------------------------------------------------------- 1 | //using System.Reflection; 2 | //using Microsoft.EntityFrameworkCore.Storage; 3 | //using Microsoft.EntityFrameworkCore.Tests; 4 | 5 | //namespace Blueshift.EntityFrameworkCore.MongoDB.Tests 6 | //{ 7 | // public class ApiConsistencyTest : ApiConsistencyTestBase 8 | // { 9 | // protected override Assembly TargetAssembly => typeof(MongoDbDatabase).GetTypeInfo().Assembly; 10 | // } 11 | //} -------------------------------------------------------------------------------- /test/Blueshift.EntityFrameworkCore.MongoDB.Tests/Blueshift.EntityFrameworkCore.MongoDB.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(TestFrameworks) 5 | MongoDb.EFCore.Tests 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | PreserveNewest 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /test/Blueshift.EntityFrameworkCore.MongoDB.Tests/Metadata/Conventions/MongoDatabaseConventionTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Blueshift.EntityFrameworkCore.MongoDB.Annotations; 3 | using Blueshift.EntityFrameworkCore.MongoDB.Infrastructure; 4 | using Blueshift.EntityFrameworkCore.MongoDB.Metadata.Builders; 5 | using Microsoft.EntityFrameworkCore; 6 | using Xunit; 7 | 8 | namespace Blueshift.EntityFrameworkCore.MongoDB.Tests.Metadata.Conventions 9 | { 10 | public class MongoDatabaseConventionTests 11 | { 12 | [Theory] 13 | [InlineData(typeof(ZooDb), "zoo")] 14 | [InlineData(typeof(ZooDbContext), "zoo")] 15 | [InlineData(typeof(ZooContext), "zoo")] 16 | [InlineData(typeof(ZooMongo), "zoo")] 17 | [InlineData(typeof(ZooMongoDb), "zoo")] 18 | [InlineData(typeof(ZooMongoDbContext), "zoo")] 19 | [InlineData(typeof(ZooMongoContext), "zoo")] 20 | [InlineData(typeof(AnnodatedZooContext), "zoo")] 21 | [InlineData(typeof(DifferentlyAnnodatedZooContext), "zhou")] 22 | public void Should_set_expected_database_name(Type dbContextType, string expectedName) 23 | { 24 | DbContext dbContext = (DbContext) Activator.CreateInstance(dbContextType); 25 | Assert.Equal(expectedName, dbContext.Model.MongoDb().Database); 26 | } 27 | 28 | private class MongoDatabaseAttributeDbContextBase : DbContext 29 | { 30 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 31 | { 32 | optionsBuilder 33 | .UseMongoDb("mongodb://localhost:27017"); 34 | base.OnConfiguring(optionsBuilder); 35 | } 36 | } 37 | 38 | private class ZooDb : MongoDatabaseAttributeDbContextBase { } 39 | 40 | private class ZooDbContext : MongoDatabaseAttributeDbContextBase { } 41 | 42 | private class ZooContext : MongoDatabaseAttributeDbContextBase { } 43 | 44 | private class ZooMongo : MongoDatabaseAttributeDbContextBase { } 45 | 46 | private class ZooMongoDb : MongoDatabaseAttributeDbContextBase { } 47 | 48 | private class ZooMongoDbContext : MongoDatabaseAttributeDbContextBase { } 49 | 50 | private class ZooMongoContext : MongoDatabaseAttributeDbContextBase { } 51 | 52 | [MongoDatabase("zoo")] 53 | private class AnnodatedZooContext : MongoDatabaseAttributeDbContextBase { } 54 | 55 | [MongoDatabase("zhou")] 56 | private class DifferentlyAnnodatedZooContext : MongoDatabaseAttributeDbContextBase { } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /test/Blueshift.EntityFrameworkCore.MongoDB.Tests/Metadata/MongoDbEntityTypeAnnotationsTests.cs: -------------------------------------------------------------------------------- 1 | using Blueshift.EntityFrameworkCore.MongoDB.Metadata; 2 | using Blueshift.EntityFrameworkCore.MongoDB.SampleDomain; 3 | using Microsoft.EntityFrameworkCore.Metadata.Internal; 4 | using Xunit; 5 | 6 | namespace Blueshift.EntityFrameworkCore.MongoDB.Tests.Metadata 7 | { 8 | public class MongoDbEntityTypeAnnotationsTests 9 | { 10 | [Fact] 11 | public void Collection_name_is_pluralized_camel_cased_entity_type_by_default() 12 | { 13 | var model = new Model(); 14 | var entityType = new EntityType(typeof(Animal), model, ConfigurationSource.Explicit); 15 | var mongoDbEntityTypeAnnotations = new MongoDbEntityTypeAnnotations(entityType); 16 | Assert.Equal(MongoDbUtilities.Pluralize(MongoDbUtilities.ToLowerCamelCase(nameof(Animal))), 17 | mongoDbEntityTypeAnnotations.CollectionName); 18 | } 19 | 20 | [Fact] 21 | public void Can_write_collection_name() 22 | { 23 | var collectionName = "myCollection"; 24 | var model = new Model(); 25 | var entityType = new EntityType(typeof(Animal), model, ConfigurationSource.Explicit); 26 | var mongoDbEntityTypeAnnotations = new MongoDbEntityTypeAnnotations(entityType) 27 | { 28 | CollectionName = collectionName 29 | }; 30 | Assert.Equal(collectionName, mongoDbEntityTypeAnnotations.CollectionName); 31 | } 32 | 33 | [Fact] 34 | public void Discriminator_is_type_name_by_default() 35 | { 36 | var model = new Model(); 37 | var entityType = new EntityType(typeof(Animal), model, ConfigurationSource.Explicit); 38 | var mongoDbEntityTypeAnnotations = new MongoDbEntityTypeAnnotations(entityType); 39 | Assert.Equal(typeof(Animal).Name, mongoDbEntityTypeAnnotations.Discriminator); 40 | } 41 | 42 | [Fact] 43 | public void Can_write_discriminator() 44 | { 45 | var discriminator = "discriminator"; 46 | var model = new Model(); 47 | var entityType = new EntityType(typeof(Animal), model, ConfigurationSource.Explicit); 48 | var mongoDbEntityTypeAnnotations = new MongoDbEntityTypeAnnotations(entityType) 49 | { 50 | Discriminator = discriminator 51 | }; 52 | Assert.Equal(discriminator, mongoDbEntityTypeAnnotations.Discriminator); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /test/Blueshift.EntityFrameworkCore.MongoDB.Tests/Metadata/MongoDbModelAnnotationsTests.cs: -------------------------------------------------------------------------------- 1 | using Blueshift.EntityFrameworkCore.MongoDB.Metadata; 2 | using Microsoft.EntityFrameworkCore.Metadata.Internal; 3 | using Xunit; 4 | 5 | namespace Blueshift.EntityFrameworkCore.MongoDB.Tests.Metadata 6 | { 7 | public class MongoDbModelAnnotationsTests 8 | { 9 | [Fact] 10 | public void Database_name_null_by_default() 11 | { 12 | var mongoDbModelAnnotations = new MongoDbModelAnnotations(new Model()); 13 | Assert.Null(mongoDbModelAnnotations.Database); 14 | } 15 | 16 | [Fact] 17 | public void Can_write_database_name() 18 | { 19 | var mongoDbModelAnnotations = new MongoDbModelAnnotations(new Model()) { Database = "test" }; 20 | Assert.Equal(expected: "test", actual: mongoDbModelAnnotations.Database); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /test/Blueshift.EntityFrameworkCore.MongoDB.Tests/MongoDbContextTestBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Blueshift.EntityFrameworkCore.MongoDB.Infrastructure; 4 | using Blueshift.EntityFrameworkCore.MongoDB.SampleDomain; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace Blueshift.EntityFrameworkCore.MongoDB.Tests 8 | { 9 | public abstract class MongoDbContextTestBase : IDisposable 10 | { 11 | private const string MongoUrl = "mongodb://localhost:27017"; 12 | 13 | protected IServiceProvider ServiceProvider; 14 | 15 | protected MongoDbContextTestBase() 16 | { 17 | ServiceProvider = new ServiceCollection() 18 | .AddDbContext(options => options 19 | .UseMongoDb(MongoUrl) 20 | .EnableSensitiveDataLogging(true)) 21 | .BuildServiceProvider(); 22 | 23 | ExecuteUnitOfWork(zooDbContext => zooDbContext.Database.EnsureCreated()); 24 | } 25 | 26 | public void Dispose() 27 | { 28 | ExecuteUnitOfWork(zooDbContext => zooDbContext.Database.EnsureDeleted()); 29 | } 30 | 31 | protected void ExecuteUnitOfWork(Action unitOfWork) 32 | { 33 | using (IServiceScope serviceScope = ServiceProvider.CreateScope()) 34 | { 35 | ZooDbContext zooDbContext = serviceScope.ServiceProvider.GetService(); 36 | unitOfWork(zooDbContext); 37 | } 38 | } 39 | 40 | protected async Task ExecuteUnitOfWorkAsync(Func unitOfWork) 41 | { 42 | using (IServiceScope serviceScope = ServiceProvider.CreateScope()) 43 | { 44 | ZooDbContext zooDbContext = serviceScope.ServiceProvider.GetService(); 45 | await unitOfWork(zooDbContext); 46 | } 47 | } 48 | 49 | protected async Task ExecuteUnitOfWorkAsync(Func> unitOfWork) 50 | { 51 | using (IServiceScope serviceScope = ServiceProvider.CreateScope()) 52 | { 53 | ZooDbContext zooDbContext = serviceScope.ServiceProvider.GetService(); 54 | return await unitOfWork(zooDbContext); 55 | } 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /test/Blueshift.EntityFrameworkCore.MongoDB.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Blueshift Software, LLC. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.Reflection; 5 | using System.Resources; 6 | using Xunit; 7 | 8 | [assembly: NeutralResourcesLanguage("en-US")] 9 | [assembly: AssemblyMetadata("Serviceable", "True")] 10 | 11 | [assembly: TestFramework("Microsoft.EntityFrameworkCore.Specification.Tests.TestUtilities.Xunit.ConditionalTestFramework", "Microsoft.EntityFrameworkCore.Specification.Tests")] 12 | -------------------------------------------------------------------------------- /test/Blueshift.EntityFrameworkCore.MongoDB.Tests/Storage/MongoDbDatabaseCreatorTests.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using Blueshift.EntityFrameworkCore.MongoDB.Storage; 4 | using MongoDB.Driver; 5 | using Moq; 6 | using Xunit; 7 | 8 | namespace Blueshift.EntityFrameworkCore.MongoDB.Tests.Storage 9 | { 10 | public class MongoDbDatabaseCreatorTests 11 | { 12 | [Fact] 13 | public void Ensure_created_returns_false() 14 | { 15 | var mockMongoDbConnection = new Mock(); 16 | mockMongoDbConnection 17 | .Setup(mongoDbConnection => mongoDbConnection.GetDatabase()) 18 | .Returns(new Mock().Object) 19 | .Verifiable(); 20 | var mongoDbDatabaseCreator = new MongoDbDatabaseCreator(mockMongoDbConnection.Object); 21 | Assert.False(mongoDbDatabaseCreator.EnsureCreated()); 22 | mockMongoDbConnection.Verify( 23 | mongoDbConnection => mongoDbConnection.GetDatabase(), 24 | Times.Exactly(callCount: 1)); 25 | } 26 | 27 | [Fact] 28 | public async Task Ensure_created_async_returns_false() 29 | { 30 | var mockMongoDbConnection = new Mock(); 31 | mockMongoDbConnection 32 | .Setup(mongoDbConnection => mongoDbConnection.GetDatabaseAsync(It.IsAny())) 33 | .Returns(Task.FromResult(new Mock().Object)) 34 | .Verifiable(); 35 | var mongoDbDatabaseCreator = new MongoDbDatabaseCreator(mockMongoDbConnection.Object); 36 | Assert.False(await mongoDbDatabaseCreator.EnsureCreatedAsync()); 37 | mockMongoDbConnection.Verify( 38 | mongoDbConnection => mongoDbConnection.GetDatabaseAsync(It.IsAny()), 39 | Times.Exactly(callCount: 1)); 40 | } 41 | 42 | [Fact] 43 | public void Ensure_deleted_succeeds() 44 | { 45 | var mockMongoDbConnection = new Mock(); 46 | mockMongoDbConnection 47 | .Setup(mongoDbConnection => mongoDbConnection.DropDatabase()) 48 | .Verifiable(); 49 | var mongoDbDatabaseCreator = new MongoDbDatabaseCreator(mockMongoDbConnection.Object); 50 | Assert.True(mongoDbDatabaseCreator.EnsureDeleted()); 51 | mockMongoDbConnection.Verify( 52 | mongoDbConnection => mongoDbConnection.DropDatabase(), 53 | Times.Exactly(callCount: 1)); 54 | } 55 | 56 | [Fact] 57 | public async Task Ensure_deleted_async_succeeds() 58 | { 59 | var mockMongoDbConnection = new Mock(); 60 | mockMongoDbConnection 61 | .Setup(mongoDbConnection => mongoDbConnection.DropDatabaseAsync(It.IsAny())) 62 | .Returns(Task.FromResult(result: 0)) 63 | .Verifiable(); 64 | var mongoDbDatabaseCreator = new MongoDbDatabaseCreator(mockMongoDbConnection.Object); 65 | Assert.True(await mongoDbDatabaseCreator.EnsureDeletedAsync()); 66 | mockMongoDbConnection.Verify( 67 | mongoDbConnection => mongoDbConnection.DropDatabaseAsync(It.IsAny()), 68 | Times.Exactly(callCount: 1)); 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /test/Blueshift.EntityFrameworkCore.MongoDB.Tests/ValueGeneration/MongoDbValueGeneratorSelectorTests.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using Blueshift.EntityFrameworkCore.MongoDB.SampleDomain; 3 | using Blueshift.EntityFrameworkCore.MongoDB.ValueGeneration; 4 | using Microsoft.EntityFrameworkCore.Metadata.Internal; 5 | using Microsoft.EntityFrameworkCore.ValueGeneration; 6 | using Xunit; 7 | 8 | namespace Blueshift.EntityFrameworkCore.MongoDB.Tests.ValueGeneration 9 | { 10 | public class MongoDbValueGeneratorSelectorTests 11 | { 12 | [Fact] 13 | public void X() 14 | { 15 | var model = new Model(); 16 | EntityType entityType = model.AddEntityType(typeof(Employee)); 17 | Property property = entityType.AddProperty(typeof(Employee) 18 | .GetTypeInfo() 19 | .GetProperty(nameof(Employee.EmployeeId))); 20 | 21 | var valueGeneratorCacheDependencies = new ValueGeneratorCacheDependencies(); 22 | var valueGeneratorCache = new ValueGeneratorCache(valueGeneratorCacheDependencies); 23 | var valueGeneratorSelectorDependencies = new ValueGeneratorSelectorDependencies(valueGeneratorCache); 24 | var mongoDbValueGeneratorSelector = new MongoDbValueGeneratorSelector(valueGeneratorSelectorDependencies); 25 | ValueGenerator valueGenerator = mongoDbValueGeneratorSelector.Select(property, entityType); 26 | Assert.IsAssignableFrom(valueGenerator); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /test/Blueshift.EntityFrameworkCore.MongoDB.Tests/ValueGeneration/ObjectIdValueGeneratorTests.cs: -------------------------------------------------------------------------------- 1 | using Blueshift.EntityFrameworkCore.MongoDB.ValueGeneration; 2 | using MongoDB.Bson; 3 | using Xunit; 4 | 5 | namespace Blueshift.EntityFrameworkCore.MongoDB.Tests.ValueGeneration 6 | { 7 | public class ObjectIdValueGeneratorTests 8 | { 9 | [Fact] 10 | public void Generates_temporary_values_returns_false() 11 | { 12 | var objectIdValueGenerator = new ObjectIdValueGenerator(); 13 | Assert.False(objectIdValueGenerator.GeneratesTemporaryValues); 14 | } 15 | 16 | [Fact] 17 | public void Does_not_generate_empty_object_id() 18 | { 19 | var objectIdValueGenerator = new ObjectIdValueGenerator(); 20 | Assert.NotEqual(ObjectId.Empty, objectIdValueGenerator.Next(entry: null)); 21 | } 22 | 23 | [Fact] 24 | public void Generates_unique_object_ids() 25 | { 26 | var objectIdValueGenerator = new ObjectIdValueGenerator(); 27 | ObjectId id = ObjectId.Empty; 28 | for (var i = 0; i < 100; i++) 29 | { 30 | Assert.NotEqual(id, id = objectIdValueGenerator.Next(entry: null)); 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /test/Blueshift.EntityFrameworkCore.MongoDB.Tests/xunit.runner.json: -------------------------------------------------------------------------------- 1 | { 2 | "longRunningTestSeconds": 30, 3 | "parallelizeAssembly": false 4 | } -------------------------------------------------------------------------------- /test/Blueshift.Identity.MongoDB.Tests/Blueshift.Identity.MongoDB.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(TestFrameworks) 5 | MongoDb.EFCore.Identity.Tests 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | PreserveNewest 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /test/Blueshift.Identity.MongoDB.Tests/MongoDbIdentityFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Blueshift.EntityFrameworkCore.MongoDB.Infrastructure; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using MongoDB.Bson; 5 | 6 | namespace Blueshift.Identity.MongoDB.Tests 7 | { 8 | public class MongoDbIdentityFixture : IDisposable 9 | { 10 | private const string MongoUrl = "mongodb://localhost:27017"; 11 | 12 | protected IServiceProvider ServiceProvider; 13 | private IdentityMongoDbContext _identityDbContext; 14 | 15 | public MongoDbIdentityFixture() 16 | { 17 | ServiceProvider = new ServiceCollection() 18 | .AddDbContext(options => options 19 | .UseMongoDb( 20 | connectionString: MongoUrl, 21 | mongoDbOptionsAction: optionsBuilder => optionsBuilder.UseDatabase("__test_identities")) 22 | .EnableSensitiveDataLogging(true)) 23 | .AddIdentity() 24 | .AddEntityFrameworkMongoDbStores() 25 | .Services 26 | .BuildServiceProvider(); 27 | _identityDbContext = ServiceProvider.GetRequiredService(); 28 | _identityDbContext.Database.EnsureCreated(); 29 | } 30 | 31 | public T GetService() 32 | => ServiceProvider.GetRequiredService(); 33 | 34 | /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. 35 | public void Dispose() 36 | { 37 | Dispose(true); 38 | GC.SuppressFinalize(this); 39 | } 40 | 41 | protected virtual void Dispose(bool disposing) 42 | { 43 | if (disposing) 44 | { 45 | if (_identityDbContext != null) 46 | { 47 | _identityDbContext.Database.EnsureDeleted(); 48 | _identityDbContext = null; 49 | } 50 | } 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /test/Blueshift.Identity.MongoDB.Tests/MongoDbIdentityTestBase.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using Microsoft.AspNetCore.Identity; 4 | using Xunit; 5 | 6 | namespace Blueshift.Identity.MongoDB.Tests 7 | { 8 | [Collection("MongoDB.Identity.Tests")] 9 | public abstract class MongoDbIdentityStoreTestBase 10 | { 11 | protected static readonly string RoleName = "TestRole"; 12 | protected static readonly string RoleNameNormalized = RoleName.ToUpper(); 13 | 14 | protected static readonly string UserName = "user.name@domain.com"; 15 | protected static readonly string UserNameNormalized = UserName.ToUpper(); 16 | 17 | private readonly IUserStore _userStore; 18 | private readonly IRoleStore _roleStore; 19 | 20 | protected MongoDbIdentityStoreTestBase(MongoDbIdentityFixture mongoDbIdentityFixture) 21 | { 22 | _userStore = mongoDbIdentityFixture.GetService>(); 23 | _roleStore = mongoDbIdentityFixture.GetService>(); 24 | } 25 | 26 | protected virtual MongoDbIdentityRole CreateRole() 27 | => new MongoDbIdentityRole 28 | { 29 | RoleName = RoleName, 30 | NormalizedRoleName = RoleNameNormalized 31 | }; 32 | 33 | protected async Task CreateRoleInDatabase() 34 | { 35 | MongoDbIdentityRole role = CreateRole(); 36 | Assert.Equal(IdentityResult.Success, await _roleStore.CreateAsync(role, new CancellationToken())); 37 | return role; 38 | } 39 | 40 | protected virtual MongoDbIdentityUser CreateUser() 41 | => new MongoDbIdentityUser 42 | { 43 | UserName = UserName, 44 | NormalizedUserName = UserNameNormalized 45 | }; 46 | 47 | protected async Task CreateUserInDatabase() 48 | { 49 | MongoDbIdentityUser user = CreateUser(); 50 | Assert.Equal(IdentityResult.Success, await _userStore.CreateAsync(user, new CancellationToken())); 51 | return user; 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /test/Blueshift.Identity.MongoDB.Tests/MongoDbIdentityTestCollection.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace Blueshift.Identity.MongoDB.Tests 4 | { 5 | [CollectionDefinition("MongoDB.Identity.Tests")] 6 | public class MongoDdIdentityTestCollection : ICollectionFixture 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/Blueshift.Identity.MongoDB.Tests/MongoDbRoleClaimStoreTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Security.Claims; 3 | using System.Threading.Tasks; 4 | using Microsoft.AspNetCore.Identity; 5 | using MongoDB.Bson; 6 | using Xunit; 7 | 8 | namespace Blueshift.Identity.MongoDB.Tests 9 | { 10 | public class MongoDbRoleClaimStoreTests : MongoDbIdentityStoreTestBase 11 | { 12 | private const string Claim1Type = nameof(Claim1Type); 13 | private const string Claim1Value = nameof(Claim1Value); 14 | private const string Claim2Type = nameof(Claim2Type); 15 | private const string Claim2Value = nameof(Claim2Value); 16 | private const string Claim3Type = nameof(Claim3Type); 17 | private const string Claim3Value = nameof(Claim3Value); 18 | 19 | private static readonly Claim Claim1 = new Claim(Claim1Type, Claim1Value); 20 | private static readonly Claim Claim2 = new Claim(Claim2Type, Claim2Value); 21 | private static readonly Claim Claim3 = new Claim(Claim3Type, Claim3Value); 22 | 23 | private readonly IRoleClaimStore _mongoDbRoleClaimStore; 24 | 25 | public MongoDbRoleClaimStoreTests(MongoDbIdentityFixture mongoDbIdentityFixture) 26 | : base(mongoDbIdentityFixture) 27 | { 28 | _mongoDbRoleClaimStore = mongoDbIdentityFixture.GetService>(); 29 | } 30 | 31 | protected override MongoDbIdentityRole CreateRole() 32 | { 33 | var role = base.CreateRole(); 34 | role.Claims.Add(new MongoDbIdentityClaim(Claim1)); 35 | role.Claims.Add(new MongoDbIdentityClaim(Claim2)); 36 | return role; 37 | } 38 | 39 | [Fact] 40 | public async Task Can_add_claim_async() 41 | { 42 | var role = CreateRole(); 43 | await _mongoDbRoleClaimStore.AddClaimAsync(role, Claim3); 44 | var newClaims = new [] { Claim1, Claim2, Claim3 }; 45 | Assert.Equal(newClaims, role.Claims.Select(claim => claim.ToClaim()), new ClaimComparer()); 46 | } 47 | 48 | [Fact] 49 | public async Task Can_get_claims_async() 50 | { 51 | var role = CreateRole(); 52 | var claims = new [] { Claim1, Claim2 }; 53 | Assert.Equal(claims, await _mongoDbRoleClaimStore.GetClaimsAsync(role), new ClaimComparer()); 54 | } 55 | 56 | [Fact] 57 | public async Task Can_remove_claims_async() 58 | { 59 | var role = CreateRole(); 60 | await _mongoDbRoleClaimStore.RemoveClaimAsync(role, Claim1); 61 | Assert.Equal(Claim2, role.Claims.Select(claim => claim.ToClaim()).Single(), new ClaimComparer()); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /test/Blueshift.Identity.MongoDB.Tests/MongoDbUserAuthenticatorKeyStoreTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Identity; 6 | using Xunit; 7 | 8 | namespace Blueshift.Identity.MongoDB.Tests 9 | { 10 | public class MongoDbUserAuthenticatorKeyStoreTests : MongoDbIdentityStoreTestBase 11 | { 12 | private static readonly string AuthenticatorKey = new Guid().ToString(); 13 | 14 | private readonly IUserAuthenticatorKeyStore _mongoDbUserAuthenticatorKeyStore; 15 | 16 | public MongoDbUserAuthenticatorKeyStoreTests(MongoDbIdentityFixture mongoDbIdentityFixture) 17 | : base(mongoDbIdentityFixture) 18 | { 19 | _mongoDbUserAuthenticatorKeyStore = mongoDbIdentityFixture.GetService>(); 20 | } 21 | 22 | protected override MongoDbIdentityUser CreateUser() 23 | { 24 | var user = base.CreateUser(); 25 | user.Logins.Add(new MongoDbIdentityUserLogin 26 | { 27 | LoginProvider = "[BlueshiftMongoDbUserStore]", 28 | ProviderKey = new Guid().ToString(), 29 | ProviderDisplayName = "Blueshift MongoDb Provider", 30 | UserTokens = 31 | { 32 | new MongoDbIdentityUserToken() { Name = "AuthenticatorKey", Value = AuthenticatorKey } 33 | } 34 | }); 35 | return user; 36 | } 37 | 38 | [Fact] 39 | public async Task Can_get_authenticator_key_async() 40 | { 41 | var user = CreateUser(); 42 | Assert.Equal(AuthenticatorKey, await _mongoDbUserAuthenticatorKeyStore.GetAuthenticatorKeyAsync(user, new CancellationToken()), StringComparer.Ordinal); 43 | } 44 | 45 | [Fact] 46 | public async Task Can_set_token_async() 47 | { 48 | var user = CreateUser(); 49 | var newAuthenticatorKey = new Guid().ToString(); 50 | await _mongoDbUserAuthenticatorKeyStore.SetAuthenticatorKeyAsync(user, newAuthenticatorKey, new CancellationToken()); 51 | Assert.Equal(newAuthenticatorKey, user.Logins 52 | .First(login => login.LoginProvider == "[BlueshiftMongoDbUserStore]") 53 | .UserTokens 54 | .First(userToken => userToken.Name == "AuthenticatorKey").Value, 55 | StringComparer.Ordinal); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /test/Blueshift.Identity.MongoDB.Tests/MongoDbUserLockoutStoreTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Microsoft.AspNetCore.Identity; 5 | using Xunit; 6 | 7 | namespace Blueshift.Identity.MongoDB.Tests 8 | { 9 | public class MongoDbUserLockoutStoreTests : MongoDbIdentityStoreTestBase 10 | { 11 | private static readonly DateTime LockoutEndDate = DateTime.Now.AddDays(1); 12 | 13 | private readonly IUserLockoutStore _mongoDbUserLockoutStore; 14 | 15 | public MongoDbUserLockoutStoreTests(MongoDbIdentityFixture mongoDbIdentityFixture) 16 | : base(mongoDbIdentityFixture) 17 | { 18 | _mongoDbUserLockoutStore = mongoDbIdentityFixture.GetService>(); 19 | } 20 | 21 | protected override MongoDbIdentityUser CreateUser() 22 | { 23 | var user = base.CreateUser(); 24 | user.LockoutEnd = LockoutEndDate; 25 | return user; 26 | } 27 | 28 | [Fact] 29 | public async Task Can_get_access_failed_count_async() 30 | { 31 | var user = CreateUser(); 32 | Assert.Equal(user.AccessFailedCount, await _mongoDbUserLockoutStore.GetAccessFailedCountAsync(user, new CancellationToken())); 33 | } 34 | 35 | [Fact] 36 | public async Task Can_check_lockout_enabled_async() 37 | { 38 | var user = CreateUser(); 39 | user.LockoutEnabled = false; 40 | Assert.False(await _mongoDbUserLockoutStore.GetLockoutEnabledAsync(user, new CancellationToken())); 41 | user.LockoutEnabled = true; 42 | Assert.True(await _mongoDbUserLockoutStore.GetLockoutEnabledAsync(user, new CancellationToken())); 43 | } 44 | 45 | [Fact] 46 | public async Task Can_set_lockout_enabled_async() 47 | { 48 | var user = CreateUser(); 49 | await _mongoDbUserLockoutStore.SetLockoutEnabledAsync(user, false, new CancellationToken()); 50 | Assert.False(user.LockoutEnabled); 51 | await _mongoDbUserLockoutStore.SetLockoutEnabledAsync(user, true, new CancellationToken()); 52 | Assert.True(user.LockoutEnabled); 53 | } 54 | 55 | [Fact] 56 | public async Task Can_get_lockout_end_date_async() 57 | { 58 | var user = CreateUser(); 59 | Assert.Equal(user.LockoutEnd, await _mongoDbUserLockoutStore.GetLockoutEndDateAsync(user, new CancellationToken())); 60 | } 61 | 62 | [Fact] 63 | public async Task Can_set_lockout_end_date_async() 64 | { 65 | var user = CreateUser(); 66 | var lockoutEndDate = DateTime.Now.AddDays(3); 67 | await _mongoDbUserLockoutStore.SetLockoutEndDateAsync(user, lockoutEndDate, new CancellationToken()); 68 | Assert.Equal(lockoutEndDate, user.LockoutEnd); 69 | } 70 | 71 | [Fact] 72 | public async Task Can_reset_access_failed_count_async() 73 | { 74 | var user = CreateUser(); 75 | user.AccessFailedCount = 10; 76 | await _mongoDbUserLockoutStore.ResetAccessFailedCountAsync(user, new CancellationToken()); 77 | Assert.Equal(0, user.AccessFailedCount); 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /test/Blueshift.Identity.MongoDB.Tests/MongoDbUserLoginStoreTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Identity; 6 | using Xunit; 7 | 8 | namespace Blueshift.Identity.MongoDB.Tests 9 | { 10 | public class MongoDbUserLoginStoreTests : MongoDbIdentityStoreTestBase 11 | { 12 | private readonly IUserLoginStore _mongoDbUserLoginStore; 13 | 14 | private const string ProviderName = nameof(ProviderName); 15 | private static readonly string ProviderKey = new Guid().ToString(); 16 | private const string ProviderDisplayName = nameof(ProviderDisplayName); 17 | 18 | public MongoDbUserLoginStoreTests(MongoDbIdentityFixture mongoDbIdentityFixture) 19 | : base(mongoDbIdentityFixture) 20 | { 21 | _mongoDbUserLoginStore = mongoDbIdentityFixture.GetService>(); 22 | } 23 | 24 | protected override MongoDbIdentityUser CreateUser() 25 | { 26 | var user = base.CreateUser(); 27 | user.Logins.Add(new MongoDbIdentityUserLogin 28 | { 29 | LoginProvider = ProviderName, 30 | ProviderKey = ProviderKey, 31 | ProviderDisplayName = ProviderDisplayName 32 | }); 33 | return user; 34 | } 35 | 36 | [Fact] 37 | public async Task Can_add_login_async() 38 | { 39 | var user = CreateUser(); 40 | var userLoginInfo = new UserLoginInfo("Google", "1234567890abcdef", "Google+"); 41 | await _mongoDbUserLoginStore.AddLoginAsync(user, userLoginInfo, new CancellationToken()); 42 | Assert.NotNull(user.Logins 43 | .SingleOrDefault(identityUserLogin => 44 | userLoginInfo.LoginProvider == identityUserLogin.LoginProvider 45 | && userLoginInfo.ProviderKey == identityUserLogin.ProviderKey 46 | && userLoginInfo.ProviderDisplayName == identityUserLogin.ProviderDisplayName)); 47 | } 48 | 49 | [Fact] 50 | public async Task Can_remove_login_async() 51 | { 52 | var user = CreateUser(); 53 | await _mongoDbUserLoginStore.RemoveLoginAsync(user, ProviderName, ProviderKey, new CancellationToken()); 54 | Assert.Empty(user.Logins); 55 | } 56 | 57 | [Fact] 58 | public async Task Can_find_by_login_async() 59 | { 60 | var user = await CreateUserInDatabase(); 61 | Assert.Equal(user, await _mongoDbUserLoginStore.FindByLoginAsync(ProviderName, ProviderKey, new CancellationToken()), new MongoDbIdentityUserComparer()); 62 | } 63 | 64 | [Fact] 65 | public async Task Can_get_logins_async() 66 | { 67 | var user = CreateUser(); 68 | var login = (await _mongoDbUserLoginStore.GetLoginsAsync(user, new CancellationToken())).Single(); 69 | Assert.Equal(ProviderName, login.LoginProvider, StringComparer.Ordinal); 70 | Assert.Equal(ProviderKey, login.ProviderKey, StringComparer.Ordinal); 71 | Assert.Equal(ProviderDisplayName, login.ProviderDisplayName, StringComparer.Ordinal); 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /test/Blueshift.Identity.MongoDB.Tests/MongoDbUserPasswordStoreTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Microsoft.AspNetCore.Identity; 5 | using Xunit; 6 | 7 | namespace Blueshift.Identity.MongoDB.Tests 8 | { 9 | public class MongoDbUserPasswordStoreTests : MongoDbIdentityStoreTestBase 10 | { 11 | private static readonly string PasswordHash = new Guid().ToString(); 12 | 13 | private readonly IUserPasswordStore _mongoDbUserPasswordStore; 14 | 15 | public MongoDbUserPasswordStoreTests(MongoDbIdentityFixture mongoDbIdentityFixture) 16 | : base(mongoDbIdentityFixture) 17 | { 18 | _mongoDbUserPasswordStore = mongoDbIdentityFixture.GetService>(); 19 | } 20 | 21 | protected override MongoDbIdentityUser CreateUser() 22 | { 23 | var user = base.CreateUser(); 24 | user.PasswordHash = PasswordHash; 25 | return user; 26 | } 27 | 28 | [Fact] 29 | public async Task Can_get_password_hash_async() 30 | { 31 | var user = CreateUser(); 32 | Assert.Equal(PasswordHash, await _mongoDbUserPasswordStore.GetPasswordHashAsync(user, new CancellationToken()), StringComparer.Ordinal); 33 | } 34 | 35 | [Fact] 36 | public async Task Can_set_password_hash_async() 37 | { 38 | var user = CreateUser(); 39 | var newPassword = new Guid().ToString(); 40 | await _mongoDbUserPasswordStore.SetPasswordHashAsync(user, newPassword, new CancellationToken()); 41 | Assert.Equal(newPassword, user.PasswordHash, StringComparer.Ordinal); 42 | } 43 | 44 | [Fact] 45 | public async Task Can_check_has_password_async() 46 | { 47 | var user = CreateUser(); 48 | Assert.True(await _mongoDbUserPasswordStore.HasPasswordAsync(user, new CancellationToken())); 49 | user.PasswordHash = null; 50 | Assert.False(await _mongoDbUserPasswordStore.HasPasswordAsync(user, new CancellationToken())); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /test/Blueshift.Identity.MongoDB.Tests/MongoDbUserPhoneNumberStoreTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Microsoft.AspNetCore.Identity; 5 | using Xunit; 6 | 7 | namespace Blueshift.Identity.MongoDB.Tests 8 | { 9 | public class MongoDbUserPhoneNumberStoreTests : MongoDbIdentityStoreTestBase 10 | { 11 | private const string PhoneNumber = "+1.123.456.7890"; 12 | 13 | private readonly IUserPhoneNumberStore _mongoDbUserPhoneNumberStore; 14 | 15 | public MongoDbUserPhoneNumberStoreTests(MongoDbIdentityFixture mongoDbIdentityFixture) 16 | : base(mongoDbIdentityFixture) 17 | { 18 | _mongoDbUserPhoneNumberStore = mongoDbIdentityFixture.GetService>(); 19 | } 20 | 21 | protected override MongoDbIdentityUser CreateUser() 22 | { 23 | var user = base.CreateUser(); 24 | user.PhoneNumber = PhoneNumber; 25 | user.PhoneNumberConfirmed = true; 26 | return user; 27 | } 28 | 29 | [Fact] 30 | public async Task Can_get_phone_number_async() 31 | { 32 | var user = CreateUser(); 33 | Assert.Equal(PhoneNumber, await _mongoDbUserPhoneNumberStore.GetPhoneNumberAsync(user, new CancellationToken())); 34 | } 35 | 36 | [Fact] 37 | public async Task Can_set_phone_number_async() 38 | { 39 | var user = CreateUser(); 40 | string newPhoneNumber = "+1.987.654.3210"; 41 | await _mongoDbUserPhoneNumberStore.SetPhoneNumberAsync(user, newPhoneNumber, new CancellationToken()); 42 | Assert.Equal(newPhoneNumber, user.PhoneNumber, StringComparer.Ordinal); 43 | } 44 | 45 | [Fact] 46 | public async Task Can_check_phone_number_confirmed_async() 47 | { 48 | var user = CreateUser(); 49 | user.PhoneNumberConfirmed = false; 50 | Assert.False(await _mongoDbUserPhoneNumberStore.GetPhoneNumberConfirmedAsync(user, new CancellationToken())); 51 | user.PhoneNumberConfirmed = true; 52 | Assert.True(await _mongoDbUserPhoneNumberStore.GetPhoneNumberConfirmedAsync(user, new CancellationToken())); 53 | } 54 | 55 | [Fact] 56 | public async Task Can_set_phone_number_confirmed_async() 57 | { 58 | var user = CreateUser(); 59 | await _mongoDbUserPhoneNumberStore.SetPhoneNumberConfirmedAsync(user, false, new CancellationToken()); 60 | Assert.False(user.PhoneNumberConfirmed); 61 | await _mongoDbUserPhoneNumberStore.SetPhoneNumberConfirmedAsync(user, true, new CancellationToken()); 62 | Assert.True(user.PhoneNumberConfirmed); 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /test/Blueshift.Identity.MongoDB.Tests/MongoDbUserSecurityStampStoreTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Microsoft.AspNetCore.Identity; 5 | using Xunit; 6 | 7 | namespace Blueshift.Identity.MongoDB.Tests 8 | { 9 | public class MongoDbUserSecurityStampStoreTests : MongoDbIdentityStoreTestBase 10 | { 11 | private static readonly string SecurityStamp = new Guid().ToString(); 12 | 13 | private readonly IUserSecurityStampStore _mongoDbUserSecurityStampStore; 14 | 15 | public MongoDbUserSecurityStampStoreTests(MongoDbIdentityFixture mongoDbIdentityFixture) 16 | : base(mongoDbIdentityFixture) 17 | { 18 | _mongoDbUserSecurityStampStore = mongoDbIdentityFixture.GetService>(); 19 | } 20 | 21 | protected override MongoDbIdentityUser CreateUser() 22 | { 23 | var user = base.CreateUser(); 24 | user.SecurityStamp = SecurityStamp; 25 | return user; 26 | } 27 | 28 | [Fact] 29 | public async Task Can_get_security_stamp_async() 30 | { 31 | var user = CreateUser(); 32 | Assert.Equal(SecurityStamp, await _mongoDbUserSecurityStampStore.GetSecurityStampAsync(user, new CancellationToken()), StringComparer.Ordinal); 33 | } 34 | 35 | [Fact] 36 | public async Task Can_set_security_stamp_async() 37 | { 38 | var user = CreateUser(); 39 | string newSecurityStamp = new Guid().ToString(); 40 | await _mongoDbUserSecurityStampStore.SetSecurityStampAsync(user, newSecurityStamp, new CancellationToken()); 41 | Assert.Equal(newSecurityStamp, user.SecurityStamp, StringComparer.Ordinal); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /test/Blueshift.Identity.MongoDB.Tests/MongoDbUserTwoFactorRecoveryCodeStoreTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Identity; 6 | using Xunit; 7 | 8 | namespace Blueshift.Identity.MongoDB.Tests 9 | { 10 | public class MongoDbUserTwoFactorRecoveryCodeStoreTests : MongoDbIdentityStoreTestBase 11 | { 12 | private const string RecoveryCode1 = nameof(RecoveryCode1); 13 | private const string RecoveryCode2 = nameof(RecoveryCode2); 14 | private const string RecoveryCode3 = nameof(RecoveryCode3); 15 | private static readonly string[] RecoveryCodes = new string[] 16 | { 17 | RecoveryCode1, RecoveryCode2, RecoveryCode3 18 | }; 19 | 20 | private readonly IUserTwoFactorRecoveryCodeStore _mongoDbUserTwoFactorRecoveryCodeStore; 21 | 22 | public MongoDbUserTwoFactorRecoveryCodeStoreTests(MongoDbIdentityFixture mongoDbIdentityFixture) 23 | : base(mongoDbIdentityFixture) 24 | { 25 | _mongoDbUserTwoFactorRecoveryCodeStore = mongoDbIdentityFixture.GetService>(); 26 | } 27 | 28 | protected override MongoDbIdentityUser CreateUser() 29 | { 30 | var user = base.CreateUser(); 31 | user.Logins.Add(new MongoDbIdentityUserLogin 32 | { 33 | LoginProvider = "[BlueshiftMongoDbUserStore]", 34 | ProviderKey = new Guid().ToString(), 35 | ProviderDisplayName = "[BlueshiftMongoDbUserStore]", 36 | UserTokens = 37 | { 38 | new MongoDbIdentityUserToken() { Name = "RecoveryCodes", Value = string.Join(";", RecoveryCodes) } 39 | } 40 | }); 41 | return user; 42 | } 43 | 44 | [Theory] 45 | [InlineData(RecoveryCode1)] 46 | [InlineData(RecoveryCode2)] 47 | [InlineData(RecoveryCode3)] 48 | public async Task Can_redeem_codes_exactly_once_async(string recoveryCode) 49 | { 50 | var user = CreateUser(); 51 | Assert.True(await _mongoDbUserTwoFactorRecoveryCodeStore.RedeemCodeAsync(user, recoveryCode, new CancellationToken())); 52 | Assert.False(await _mongoDbUserTwoFactorRecoveryCodeStore.RedeemCodeAsync(user, recoveryCode, new CancellationToken())); 53 | } 54 | 55 | [Fact] 56 | public async Task Can_replace_codes_async() 57 | { 58 | var user = CreateUser(); 59 | var newRecoveryCodes = new [] { "New Code 1", "New Code 2", "New Code 3" }; 60 | await _mongoDbUserTwoFactorRecoveryCodeStore.ReplaceCodesAsync(user, newRecoveryCodes, new CancellationToken()); 61 | var recoveryCodes = user.Logins 62 | .First(login => login.LoginProvider == "[BlueshiftMongoDbUserStore]") 63 | .UserTokens 64 | .First(userToken => userToken.Name == "RecoveryCodes").Value; 65 | Assert.DoesNotContain(RecoveryCode1, recoveryCodes, StringComparison.Ordinal); 66 | Assert.DoesNotContain(RecoveryCode2, recoveryCodes, StringComparison.Ordinal); 67 | Assert.DoesNotContain(RecoveryCode3, recoveryCodes, StringComparison.Ordinal); 68 | Assert.Equal(string.Join(";", newRecoveryCodes), recoveryCodes, StringComparer.Ordinal); 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /test/Blueshift.Identity.MongoDB.Tests/MongoDbUserTwoFactorStoreTests.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using Microsoft.AspNetCore.Identity; 4 | using Xunit; 5 | 6 | namespace Blueshift.Identity.MongoDB.Tests 7 | { 8 | public class MongoDbUserTwoFactorStoreTests : MongoDbIdentityStoreTestBase 9 | { 10 | private readonly IUserTwoFactorStore _mongoDbUserTwoFactorStore; 11 | 12 | public MongoDbUserTwoFactorStoreTests(MongoDbIdentityFixture mongoDbIdentityFixture) 13 | : base(mongoDbIdentityFixture) 14 | { 15 | _mongoDbUserTwoFactorStore = mongoDbIdentityFixture.GetService>(); 16 | } 17 | 18 | [Fact] 19 | public async Task Can_check_two_factor_enabled_async() 20 | { 21 | var user = CreateUser(); 22 | user.TwoFactorEnabled = false; 23 | Assert.False(await _mongoDbUserTwoFactorStore.GetTwoFactorEnabledAsync(user, new CancellationToken())); 24 | user.TwoFactorEnabled = true; 25 | Assert.True(await _mongoDbUserTwoFactorStore.GetTwoFactorEnabledAsync(user, new CancellationToken())); 26 | } 27 | 28 | [Fact] 29 | public async Task Can_set_two_factor_enabled_async() 30 | { 31 | var user = CreateUser(); 32 | await _mongoDbUserTwoFactorStore.SetTwoFactorEnabledAsync(user, false, new CancellationToken()); 33 | Assert.False(user.TwoFactorEnabled); 34 | await _mongoDbUserTwoFactorStore.SetTwoFactorEnabledAsync(user, true, new CancellationToken()); 35 | Assert.True(user.TwoFactorEnabled); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /test/Blueshift.Identity.MongoDB.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Blueshift Software, LLC. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.Reflection; 5 | using System.Resources; 6 | using Xunit; 7 | 8 | [assembly: NeutralResourcesLanguage("en-US")] 9 | [assembly: AssemblyMetadata("Serviceable", "True")] 10 | 11 | [assembly: TestFramework("Microsoft.EntityFrameworkCore.Specification.Tests.TestUtilities.Xunit.ConditionalTestFramework", "Microsoft.EntityFrameworkCore.Specification.Tests")] -------------------------------------------------------------------------------- /test/Blueshift.Identity.MongoDB.Tests/xunit.runner.json: -------------------------------------------------------------------------------- 1 | { 2 | "longRunningTestSeconds": 30, 3 | "parallelizeAssembly": false 4 | } -------------------------------------------------------------------------------- /test/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | netcoreapp2.1 6 | $(DeveloperTestFrameworks) 7 | netcoreapp2.0;netcoreapp2.1 8 | net461;$(TestFrameworks) 9 | 10 | 11 | 12 | $(NoWarn);CA1822;xUnit1004 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /version.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 2.1.0 5 | preview2 6 | $(VersionPrefix) 7 | $(VersionPrefix)-$(VersionSuffix)-final 8 | $(VersionSuffix)-$(BuildNumber) 9 | 10 | 11 | --------------------------------------------------------------------------------