├── .gitignore ├── README.md ├── Repository.Mongo.Tests ├── ConstructorTests.cs ├── Database │ └── mongod.exe ├── IntegrationTests.cs ├── Repository.Mongo.Tests.csproj └── Util │ ├── MongoDeamon.cs │ └── Sample.cs ├── Repository.Mongo.sln ├── Repository.Mongo ├── Attributes │ ├── CollectionName.cs │ └── ConnectionName.cs ├── Database.cs ├── Entity.cs ├── IEntity.cs ├── IRepository.cs ├── Properties │ └── AssemblyInfo.cs ├── Repository.Mongo.csproj └── Repository.cs └── azure-pipelines.yml /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studo 2015 cache/options directory 26 | .vs/ 27 | 28 | # MSTest test Results 29 | [Tt]est[Rr]esult*/ 30 | [Bb]uild[Ll]og.* 31 | 32 | # NUNIT 33 | *.VisualState.xml 34 | TestResult.xml 35 | 36 | # Build Results of an ATL Project 37 | [Dd]ebugPS/ 38 | [Rr]eleasePS/ 39 | dlldata.c 40 | 41 | *_i.c 42 | *_p.c 43 | *_i.h 44 | *.ilk 45 | *.meta 46 | *.obj 47 | *.pch 48 | *.pdb 49 | *.pgc 50 | *.pgd 51 | *.rsp 52 | *.sbr 53 | *.tlb 54 | *.tli 55 | *.tlh 56 | *.tmp 57 | *.tmp_proj 58 | *.log 59 | *.vspscc 60 | *.vssscc 61 | .builds 62 | *.pidb 63 | *.svclog 64 | *.scc 65 | 66 | # Chutzpah Test files 67 | _Chutzpah* 68 | 69 | # Visual C++ cache files 70 | ipch/ 71 | *.aps 72 | *.ncb 73 | *.opensdf 74 | *.sdf 75 | *.cachefile 76 | 77 | # Visual Studio profiler 78 | *.psess 79 | *.vsp 80 | *.vspx 81 | 82 | # TFS 2012 Local Workspace 83 | $tf/ 84 | 85 | # Guidance Automation Toolkit 86 | *.gpState 87 | 88 | # ReSharper is a .NET coding add-in 89 | _ReSharper*/ 90 | *.[Rr]e[Ss]harper 91 | *.DotSettings.user 92 | 93 | # JustCode is a .NET coding addin-in 94 | .JustCode 95 | 96 | # TeamCity is a build add-in 97 | _TeamCity* 98 | 99 | # DotCover is a Code Coverage Tool 100 | *.dotCover 101 | 102 | # NCrunch 103 | _NCrunch_* 104 | .*crunch*.local.xml 105 | 106 | # MightyMoose 107 | *.mm.* 108 | AutoTest.Net/ 109 | 110 | # Web workbench (sass) 111 | .sass-cache/ 112 | 113 | # Installshield output folder 114 | [Ee]xpress/ 115 | 116 | # DocProject is a documentation generator add-in 117 | DocProject/buildhelp/ 118 | DocProject/Help/*.HxT 119 | DocProject/Help/*.HxC 120 | DocProject/Help/*.hhc 121 | DocProject/Help/*.hhk 122 | DocProject/Help/*.hhp 123 | DocProject/Help/Html2 124 | DocProject/Help/html 125 | 126 | # Click-Once directory 127 | publish/ 128 | 129 | # Publish Web Output 130 | *.[Pp]ublish.xml 131 | *.azurePubxml 132 | # TODO: Comment the next line if you want to checkin your web deploy settings 133 | # but database connection strings (with potential passwords) will be unencrypted 134 | *.pubxml 135 | *.publishproj 136 | 137 | # NuGet Packages 138 | *.nupkg 139 | # The packages folder can be ignored because of Package Restore 140 | **/packages/* 141 | # except build/, which is used as an MSBuild target. 142 | !**/packages/build/ 143 | # Uncomment if necessary however generally it will be regenerated when needed 144 | #!**/packages/repositories.config 145 | 146 | # Windows Azure Build Output 147 | csx/ 148 | *.build.csdef 149 | 150 | # Windows Store app package directory 151 | AppPackages/ 152 | 153 | # Others 154 | *.[Cc]ache 155 | ClientBin/ 156 | [Ss]tyle[Cc]op.* 157 | ~$* 158 | *~ 159 | *.dbmdl 160 | *.dbproj.schemaview 161 | *.pfx 162 | *.publishsettings 163 | node_modules/ 164 | bower_components/ 165 | 166 | # RIA/Silverlight projects 167 | Generated_Code/ 168 | 169 | # Backup & report files from converting an old project file 170 | # to a newer Visual Studio version. Backup files are not needed, 171 | # because we have git ;-) 172 | _UpgradeReport_Files/ 173 | Backup*/ 174 | UpgradeLog*.XML 175 | UpgradeLog*.htm 176 | 177 | # SQL Server files 178 | *.mdf 179 | *.ldf 180 | 181 | # Business Intelligence projects 182 | *.rdl.data 183 | *.bim.layout 184 | *.bim_*.settings 185 | 186 | # Microsoft Fakes 187 | FakesAssemblies/ 188 | 189 | # Node.js Tools for Visual Studio 190 | .ntvs_analysis.dat 191 | 192 | # Visual Studio 6 build log 193 | *.plg 194 | 195 | # Visual Studio 6 workspace options file 196 | *.opt 197 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Version](https://img.shields.io/nuget/v/Repository.Mongo.svg?style=flat-square)](https://www.nuget.org/packages/Repository.Mongo) 2 | [![Downloads](https://img.shields.io/nuget/dt/Repository.Mongo.svg?style=flat-square)](https://www.nuget.org/packages/Repository.Mongo) 3 | 4 | ## MongoRepository 5 | 6 | Repository pattern for MongoDB with extended features 7 | 8 | ### Definition 9 | 10 | #### Model 11 | 12 | You don't need to create a model, but if you are doing so you need to extend Entity 13 | 14 | ```csharp 15 | // If you are able to define your model 16 | public class User : Entity 17 | { 18 | public string Username { get; set; } 19 | public string Password { get; set; } 20 | } 21 | ``` 22 | 23 | #### Repository 24 | 25 | There are multiple base constructors, read summaries of others 26 | 27 | ```csharp 28 | public class UserRepository : Repository 29 | { 30 | public UserRepository(string connectionString) : base(connectionString) { } 31 | 32 | // Custom method 33 | public User FindByUsername(string username) 34 | { 35 | return First(i => i.Username == username); 36 | } 37 | 38 | // Custom method2 39 | public void UpdatePassword(User item, string newPassword) 40 | { 41 | repo.Update(item, i => i.Password, newPassword); 42 | } 43 | 44 | // Custom async method 45 | public async Task FindByUsernameAsync(string username) 46 | { 47 | return awaitFirstAsync(i => i.Username == username); 48 | } 49 | } 50 | ``` 51 | 52 | *If you want to create a repository for already defined non-entity model* 53 | 54 | ```csharp 55 | public class UserRepository : Repository> 56 | { 57 | public UserRepository(string connectionString) : base(connectionString) { } 58 | 59 | 60 | // Custom method 61 | public User FindByUsername(string username) 62 | { 63 | return First(i => i.Content.Username == username); 64 | } 65 | } 66 | ``` 67 | 68 | ### Usage 69 | 70 | Each method has multiple overloads, read method summary for additional parameters 71 | 72 | ```csharp 73 | UserRepository repo = new UserRepository("mongodb://localhost/sample") 74 | 75 | // Get 76 | User user = repo.Get("58a18d16bc1e253bb80a67c9"); 77 | 78 | // Insert 79 | User item = new User() 80 | { 81 | Username = "username", 82 | Password = "password" 83 | }; 84 | repo.Insert(item); 85 | 86 | // Update 87 | // Single property 88 | repo.Update(item, i => i.Username, "newUsername"); 89 | 90 | // Multiple property 91 | // Updater has many methods like Inc, Push, CurrentDate, etc. 92 | var update1 = Updater.Set(i => i.Username, "oldUsername"); 93 | var update2 = Updater.Set(i => i.Password, "newPassword"); 94 | repo.Update(item, update1, update2); 95 | 96 | // All entity 97 | item.Username = "someUsername"; 98 | repo.Replace(item); 99 | 100 | // Delete 101 | repo.Delete(item); 102 | 103 | // Queries - all queries has filter, order and paging features 104 | var first = repo.First(); 105 | var last = repo.Last(); 106 | var search = repo.Find(i => i.Username == "username"); 107 | var allItems = repo.FindAll(); 108 | 109 | // Utils 110 | var any = repo.Any(i => i.Username.Contains("user")); 111 | 112 | // Count 113 | // Get number of filtered documents 114 | var count = repo.Count(p => p.Age > 20); 115 | 116 | // EstimatedCount 117 | // Get number of all documents 118 | var count = repo.EstimatedCount(); 119 | ``` 120 | -------------------------------------------------------------------------------- /Repository.Mongo.Tests/ConstructorTests.cs: -------------------------------------------------------------------------------- 1 | using AutoFixture; 2 | using AutoFixture.AutoMoq; 3 | using MongoDB.Driver; 4 | using System.Diagnostics; 5 | using Xunit; 6 | 7 | namespace Repository.Mongo.Tests 8 | { 9 | public class ConstructorTests 10 | { 11 | [Fact] 12 | public void Constructor_IMongoDatabase_ShouldBe_LessThen5ms() 13 | { 14 | var fixture = new Fixture().Customize(new AutoMoqCustomization()); 15 | var param1 = fixture.Create(); 16 | var param2 = fixture.Create(); 17 | 18 | var watch = Stopwatch.StartNew(); 19 | 20 | new Repository(param1, param2); 21 | 22 | var duration = watch.ElapsedMilliseconds; 23 | Assert.True(duration < 5); 24 | } 25 | 26 | [Fact] 27 | public void Constructor_IMongoClient_ShouldBe_LessThen5ms() 28 | { 29 | var fixture = new Fixture().Customize(new AutoMoqCustomization()); 30 | var param1 = fixture.Create(); 31 | var param2 = fixture.Create(); 32 | var watch = Stopwatch.StartNew(); 33 | 34 | new Repository(param1, param2, param2); 35 | 36 | var duration = watch.ElapsedMilliseconds; 37 | Assert.True(duration < 5); 38 | } 39 | 40 | [Fact] 41 | public void Constructor_ConnectionString_ShouldBe_LessThen5ms() 42 | { 43 | var fixture = new Fixture().Customize(new AutoMoqCustomization()); 44 | var param1 = fixture.Create(); 45 | var watch = Stopwatch.StartNew(); 46 | 47 | new Repository(param1, param1); 48 | 49 | var duration = watch.ElapsedMilliseconds; 50 | Assert.True(duration < 5); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Repository.Mongo.Tests/Database/mongod.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/esendir/MongoRepository/2def82174b18d19b32a988141895f6e10e8c3100/Repository.Mongo.Tests/Database/mongod.exe -------------------------------------------------------------------------------- /Repository.Mongo.Tests/IntegrationTests.cs: -------------------------------------------------------------------------------- 1 | using Repository.Mongo.Tests.Util; 2 | using System.Linq; 3 | using Xunit; 4 | 5 | namespace Repository.Mongo.Tests 6 | { 7 | public class IntegrationTests : IClassFixture 8 | { 9 | private readonly MongoDaemon deamon; 10 | private readonly Repository repository; 11 | 12 | public IntegrationTests(MongoDaemon deamon) 13 | { 14 | this.deamon = deamon; 15 | this.repository = new Repository(MongoDaemon.ConnectionString); 16 | } 17 | 18 | [Fact] 19 | public void Insert_Entity() 20 | { 21 | var entity = new Sample(); 22 | 23 | repository.Insert(entity); 24 | 25 | var dbEntity = repository.Get(entity.Id); 26 | 27 | Assert.Equal(entity.Id, dbEntity.Id); 28 | } 29 | 30 | [Fact] 31 | public void Update_Entity() 32 | { 33 | var entity = new Sample(); 34 | repository.Insert(entity); 35 | 36 | var dbEntity = repository.Get(entity.Id); 37 | Assert.Null(dbEntity.Name); 38 | 39 | var update = repository.Updater.Set(i => i.Name, "Update"); 40 | repository.Update(entity.Id, update); 41 | 42 | dbEntity = repository.Get(entity.Id); 43 | Assert.Equal("Update", dbEntity.Name); 44 | } 45 | 46 | [Fact] 47 | public void Delete_Entity() 48 | { 49 | var entity = new Sample(); 50 | repository.Insert(entity); 51 | 52 | var dbEntity = repository.Get(entity.Id); 53 | Assert.Equal(entity.Id, dbEntity.Id); 54 | 55 | repository.Delete(entity.Id); 56 | 57 | dbEntity = repository.Get(entity.Id); 58 | Assert.Null(dbEntity); 59 | } 60 | 61 | [Fact] 62 | public void Find_Entity() 63 | { 64 | var entity = new Sample(); 65 | 66 | repository.Insert(entity); 67 | 68 | var dbEntity = repository.Find(i => i.Id == entity.Id); 69 | 70 | Assert.Equal(entity.Id, dbEntity.First().Id); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Repository.Mongo.Tests/Repository.Mongo.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.2 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | all 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | PreserveNewest 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Repository.Mongo.Tests/Util/MongoDeamon.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | 5 | namespace Repository.Mongo.Tests.Util 6 | { 7 | public class MongoDaemon : IDisposable 8 | { 9 | public const string ConnectionString = "mongodb://localhost:27017/test"; 10 | public const string Host = "localhost"; 11 | public const string Port = "27017"; 12 | private readonly Process process; 13 | 14 | public MongoDaemon() 15 | { 16 | var assemblyFolder = Path.GetDirectoryName(new Uri(typeof(MongoDaemon).Assembly.CodeBase).LocalPath); 17 | var mongoFolder = Path.Combine(assemblyFolder, "Database"); 18 | var dbFolder = Path.Combine(mongoFolder, "temp"); 19 | 20 | if (Directory.Exists(dbFolder)) 21 | Directory.Delete(dbFolder, true); 22 | Directory.CreateDirectory(dbFolder); 23 | 24 | process = new Process(); 25 | process.StartInfo.FileName = Path.Combine(mongoFolder, "mongod.exe"); 26 | process.StartInfo.Arguments = "--dbpath " + dbFolder + " --storageEngine ephemeralForTest"; 27 | process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; 28 | process.Start(); 29 | } 30 | 31 | public void Dispose() 32 | { 33 | Dispose(true); 34 | GC.SuppressFinalize(this); 35 | } 36 | 37 | protected virtual void Dispose(bool disposing) 38 | { 39 | if (process != null && !process.HasExited) 40 | { 41 | process.Kill(); 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Repository.Mongo.Tests/Util/Sample.cs: -------------------------------------------------------------------------------- 1 | namespace Repository.Mongo.Tests.Util 2 | { 3 | public class Sample : Entity 4 | { 5 | public string Name { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Repository.Mongo.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.28803.202 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Repository.Mongo", "Repository.Mongo\Repository.Mongo.csproj", "{99AE887A-DB4A-4C56-80C8-21F36007028D}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Repository.Mongo.Tests", "Repository.Mongo.Tests\Repository.Mongo.Tests.csproj", "{D593B165-DC0F-44E0-9010-2A8A58C9C911}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {99AE887A-DB4A-4C56-80C8-21F36007028D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {99AE887A-DB4A-4C56-80C8-21F36007028D}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {99AE887A-DB4A-4C56-80C8-21F36007028D}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {99AE887A-DB4A-4C56-80C8-21F36007028D}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {D593B165-DC0F-44E0-9010-2A8A58C9C911}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {D593B165-DC0F-44E0-9010-2A8A58C9C911}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {D593B165-DC0F-44E0-9010-2A8A58C9C911}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {D593B165-DC0F-44E0-9010-2A8A58C9C911}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {3A3BA3A2-3547-4ACF-A0C6-1CDABB6765BE} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /Repository.Mongo/Attributes/CollectionName.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Repository.Mongo 4 | { 5 | 6 | /// 7 | /// Attribute used to annotate Enities with to override mongo collection name. By default, when this attribute 8 | /// is not specified, the classname will be used. 9 | /// 10 | [AttributeUsage(AttributeTargets.Class, Inherited = true)] 11 | public class CollectionNameAttribute : Attribute 12 | { 13 | /// 14 | /// Initializes a new instance of the CollectionName class attribute with the desired name. 15 | /// 16 | /// Name of the collection. 17 | public CollectionNameAttribute(string value) 18 | { 19 | if (string.IsNullOrWhiteSpace(value)) 20 | throw new ArgumentException("Empty collection name is not allowed", nameof(value)); 21 | Name = value; 22 | } 23 | 24 | /// 25 | /// Gets the name of the collection. 26 | /// 27 | /// The name of the collection. 28 | public virtual string Name { get; private set; } 29 | } 30 | } -------------------------------------------------------------------------------- /Repository.Mongo/Attributes/ConnectionName.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Repository.Mongo 4 | { 5 | 6 | /// 7 | /// Attribute used to annotate Enities with to override mongo collection name. By default, when this attribute 8 | /// is not specified, the classname will be used. 9 | /// 10 | [AttributeUsage(AttributeTargets.Class, Inherited = true)] 11 | public class ConnectionNameAttribute : Attribute 12 | { 13 | /// 14 | /// Initializes a new instance of the CollectionName class attribute with the desired name. 15 | /// 16 | /// Name of the collection. 17 | public ConnectionNameAttribute(string value) 18 | { 19 | if (string.IsNullOrWhiteSpace(value)) 20 | throw new ArgumentException("Empty connection name is not allowed", nameof(value)); 21 | Name = value; 22 | } 23 | 24 | /// 25 | /// Gets the name of the collection. 26 | /// 27 | /// The name of the collection. 28 | public virtual string Name { get; private set; } 29 | } 30 | } -------------------------------------------------------------------------------- /Repository.Mongo/Database.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | // Nuget MongoDB 4 | using MongoDB.Driver; 5 | 6 | namespace Repository.Mongo 7 | { 8 | /// The type to get the collection of. 9 | internal class Database where T : IEntity 10 | { 11 | private Database() 12 | { 13 | } 14 | 15 | /// 16 | /// Creates and returns a MongoCollection from the connectionstring name and collection name 17 | /// 18 | /// The connectionstring to use to get the collection from. 19 | /// The name of the collection to use. 20 | /// Returns a MongoCollection from the specified type and connectionstring. 21 | internal static IMongoCollection GetCollectionFromConnectionString(string connectionString, string collectionName) 22 | { 23 | return GetCollectionFromUrl(new MongoUrl(connectionString), collectionName ?? GetCollectionName()); 24 | } 25 | 26 | /// 27 | /// Creates and returns a MongoCollection from the specified type and url. 28 | /// 29 | /// The url to use to get the collection from. 30 | /// The name of the collection to use. 31 | /// Returns a MongoCollection from the specified type and url. 32 | internal static IMongoCollection GetCollectionFromUrl(MongoUrl url, string collectionName) 33 | { 34 | return GetDatabaseFromUrl(url).GetCollection(collectionName ?? GetCollectionName()); 35 | } 36 | 37 | internal static IMongoCollection GetCollection(IMongoClient mongoClient, string databaseName, string collectionName) 38 | { 39 | return GetCollection(mongoClient.GetDatabase(databaseName), collectionName ?? GetCollectionName()); 40 | } 41 | 42 | internal static IMongoCollection GetCollection(IMongoDatabase mongoDatabase, string collectionName) 43 | { 44 | return mongoDatabase.GetCollection(collectionName ?? GetCollectionName()); 45 | } 46 | 47 | /// 48 | /// Creates and returns a MongoDatabase from the specified url. 49 | /// 50 | /// The url to use to get the database from. 51 | /// Returns a MongoDatabase from the specified url. 52 | private static IMongoDatabase GetDatabaseFromUrl(MongoUrl url) 53 | { 54 | var client = new MongoClient(url); 55 | return client.GetDatabase(url.DatabaseName); // WriteConcern defaulted to Acknowledged 56 | } 57 | 58 | #region Collection Name 59 | 60 | /// 61 | /// Determines the collection name for T and assures it is not empty 62 | /// 63 | /// Returns the collection name for T. 64 | private static string GetCollectionName() 65 | { 66 | string collectionName; 67 | collectionName = typeof(T).GetTypeInfo().BaseType.Equals(typeof(object)) ? 68 | GetCollectionNameFromInterface() : 69 | GetCollectionNameFromType(); 70 | 71 | if (string.IsNullOrEmpty(collectionName)) 72 | { 73 | collectionName = typeof(T).Name; 74 | } 75 | return collectionName.ToLowerInvariant(); 76 | } 77 | 78 | /// 79 | /// Determines the collection name from the specified type. 80 | /// 81 | /// Returns the collection name from the specified type. 82 | private static string GetCollectionNameFromInterface() 83 | { 84 | // Check to see if the object (inherited from Entity) has a CollectionName attribute 85 | var att = CustomAttributeExtensions.GetCustomAttribute(typeof(T).GetTypeInfo()); 86 | 87 | return att?.Name ?? typeof(T).Name; 88 | } 89 | 90 | /// 91 | /// Determines the collectionname from the specified type. 92 | /// 93 | /// Returns the collectionname from the specified type. 94 | private static string GetCollectionNameFromType() 95 | { 96 | Type entitytype = typeof(T); 97 | string collectionname; 98 | 99 | // Check to see if the object (inherited from Entity) has a CollectionName attribute 100 | var att = CustomAttributeExtensions.GetCustomAttribute(typeof(T).GetTypeInfo()); 101 | if (att != null) 102 | { 103 | // It does! Return the value specified by the CollectionName attribute 104 | collectionname = att.Name; 105 | } 106 | else 107 | { 108 | collectionname = entitytype.Name; 109 | } 110 | 111 | return collectionname; 112 | } 113 | 114 | #endregion Collection Name 115 | 116 | #region Connection Name 117 | 118 | /// 119 | /// Determines the connection name for T and assures it is not empty 120 | /// 121 | /// Returns the connection name for T. 122 | private static string GetConnectionName() 123 | { 124 | string connectionName; 125 | connectionName = typeof(T).GetTypeInfo().BaseType.Equals(typeof(object)) ? 126 | GetConnectionNameFromInterface() : 127 | GetConnectionNameFromType(); 128 | 129 | if (string.IsNullOrEmpty(connectionName)) 130 | { 131 | connectionName = typeof(T).Name; 132 | } 133 | return connectionName.ToLowerInvariant(); 134 | } 135 | 136 | /// 137 | /// Determines the connection name from the specified type. 138 | /// 139 | /// Returns the connection name from the specified type. 140 | private static string GetConnectionNameFromInterface() 141 | { 142 | // Check to see if the object (inherited from Entity) has a ConnectionName attribute 143 | var att = CustomAttributeExtensions.GetCustomAttribute(typeof(T).GetTypeInfo()); 144 | return att?.Name ?? typeof(T).Name; 145 | } 146 | 147 | /// 148 | /// Determines the connection name from the specified type. 149 | /// 150 | /// Returns the connection name from the specified type. 151 | private static string GetConnectionNameFromType() 152 | { 153 | Type entitytype = typeof(T); 154 | string collectionname; 155 | 156 | // Check to see if the object (inherited from Entity) has a ConnectionName attribute 157 | var att = CustomAttributeExtensions.GetCustomAttribute(typeof(T).GetTypeInfo()); 158 | if (att != null) 159 | { 160 | // It does! Return the value specified by the ConnectionName attribute 161 | collectionname = att.Name; 162 | } 163 | else 164 | { 165 | if (typeof(Entity).GetTypeInfo().IsAssignableFrom(entitytype)) 166 | { 167 | // No attribute found, get the basetype 168 | while (!entitytype.GetTypeInfo().BaseType.Equals(typeof(Entity))) 169 | { 170 | entitytype = entitytype.GetTypeInfo().BaseType; 171 | } 172 | } 173 | collectionname = entitytype.Name; 174 | } 175 | 176 | return collectionname; 177 | } 178 | 179 | #endregion Connection Name 180 | } 181 | } -------------------------------------------------------------------------------- /Repository.Mongo/Entity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | // Nuget MongoDB 3 | using MongoDB.Bson; 4 | using MongoDB.Bson.Serialization.Attributes; 5 | 6 | namespace Repository.Mongo 7 | { 8 | /// 9 | /// Entity wrapper for non-edittable models 10 | /// 11 | /// 12 | public class Entity : Entity 13 | { 14 | /// 15 | /// Generic content 16 | /// 17 | public T Content { get; set; } 18 | } 19 | 20 | /// 21 | /// mongo entity 22 | /// 23 | [BsonIgnoreExtraElements(Inherited = true)] 24 | public class Entity : IEntity 25 | { 26 | private DateTime _createdOn; 27 | 28 | /// 29 | /// Constructor, assigns new id 30 | /// 31 | public Entity() 32 | { 33 | Id = ObjectId.GenerateNewId().ToString(); 34 | } 35 | 36 | /// 37 | /// id in string format 38 | /// 39 | [BsonElement(Order = 0)] 40 | [BsonRepresentation(BsonType.ObjectId)] 41 | public string Id { get; set; } 42 | 43 | 44 | /// 45 | /// create date 46 | /// 47 | [BsonElement("_c", Order = 1)] 48 | public DateTime CreatedOn 49 | { 50 | get 51 | { 52 | if (_createdOn == null || _createdOn == DateTime.MinValue) 53 | _createdOn = ObjectId.CreationTime; 54 | return _createdOn; 55 | } 56 | set 57 | { 58 | _createdOn = value; 59 | } 60 | } 61 | 62 | /// 63 | /// modify date 64 | /// 65 | [BsonElement("_m", Order = 2)] 66 | [BsonRepresentation(BsonType.DateTime)] 67 | public DateTime ModifiedOn { get; set; } 68 | 69 | /// 70 | /// id in objectId format 71 | /// 72 | public ObjectId ObjectId => ObjectId.Parse(Id); 73 | 74 | } 75 | } -------------------------------------------------------------------------------- /Repository.Mongo/IEntity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | // Nuget MongoDB 3 | using MongoDB.Bson; 4 | using MongoDB.Bson.Serialization.Attributes; 5 | 6 | namespace Repository.Mongo 7 | { 8 | /// 9 | /// mongo entity interface 10 | /// 11 | public interface IEntity 12 | { 13 | /// 14 | /// create date 15 | /// 16 | DateTime CreatedOn { get; } 17 | 18 | /// 19 | /// id in string format 20 | /// 21 | [BsonId] 22 | string Id { get; set; } 23 | 24 | /// 25 | /// modify date 26 | /// 27 | DateTime ModifiedOn { get; } 28 | 29 | /// 30 | /// id in objectId format 31 | /// 32 | [BsonIgnore] 33 | ObjectId ObjectId { get; } 34 | } 35 | } -------------------------------------------------------------------------------- /Repository.Mongo/IRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using System.Linq.Expressions; 4 | using System.Collections.Generic; 5 | // Nuget MongoDB 6 | using MongoDB.Driver; 7 | 8 | namespace Repository.Mongo 9 | { 10 | /// 11 | /// mongo based repository interface 12 | /// 13 | /// 14 | public interface IRepository where T : IEntity 15 | { 16 | #region MongoSpecific 17 | 18 | /// 19 | /// mongo collection 20 | /// 21 | IMongoCollection Collection { get; } 22 | 23 | /// 24 | /// filter for collection 25 | /// 26 | FilterDefinitionBuilder Filter { get; } 27 | 28 | /// 29 | /// projector for collection 30 | /// 31 | ProjectionDefinitionBuilder Project { get; } 32 | 33 | /// 34 | /// updater for collection 35 | /// 36 | UpdateDefinitionBuilder Updater { get; } 37 | 38 | /// 39 | /// Defined collection name 40 | /// 41 | string CollectionName { get; } 42 | #endregion MongoSpecific 43 | 44 | #region CRUD 45 | 46 | #region Delete 47 | 48 | /// 49 | /// delete by id 50 | /// 51 | /// id 52 | bool Delete(string id); 53 | 54 | 55 | /// 56 | /// delete entity 57 | /// 58 | /// entity 59 | bool Delete(T entity); 60 | 61 | /// 62 | /// delete items with filter 63 | /// 64 | /// expression filter 65 | bool Delete(Expression> filter); 66 | 67 | /// 68 | /// delete all documents 69 | /// 70 | bool DeleteAll(); 71 | 72 | /// 73 | /// delete by id 74 | /// 75 | /// id 76 | Task DeleteAsync(string id); 77 | 78 | /// 79 | /// delete entity 80 | /// 81 | /// entity 82 | Task DeleteAsync(T entity); 83 | 84 | /// 85 | /// delete items with filter 86 | /// 87 | /// expression filter 88 | Task DeleteAsync(Expression> filter); 89 | 90 | /// 91 | /// delete all documents 92 | /// 93 | Task DeleteAllAsync(); 94 | #endregion Delete 95 | 96 | #region Find 97 | 98 | /// 99 | /// find entities 100 | /// 101 | /// filter definition 102 | /// collection of entity 103 | IEnumerable Find(FilterDefinition filter); 104 | 105 | /// 106 | /// find entities 107 | /// 108 | /// expression filter 109 | /// collection of entity 110 | IEnumerable Find(Expression> filter); 111 | 112 | /// 113 | /// find entities with paging 114 | /// 115 | /// expression filter 116 | /// page index, based on 0 117 | /// number of items in page 118 | /// collection of entity 119 | IEnumerable Find(Expression> filter, int pageIndex, int size); 120 | 121 | /// 122 | /// find entities with paging and ordering 123 | /// default ordering is descending 124 | /// 125 | /// expression filter 126 | /// ordering parameters 127 | /// page index, based on 0 128 | /// number of items in page 129 | /// collection of entity 130 | IEnumerable Find(Expression> filter, Expression> order, int pageIndex, int size); 131 | 132 | /// 133 | /// find entities with paging and ordering in direction 134 | /// 135 | /// expression filter 136 | /// ordering parameters 137 | /// page index, based on 0 138 | /// number of items in page 139 | /// ordering direction 140 | /// collection of entity 141 | IEnumerable Find(Expression> filter, Expression> order, int pageIndex, int size, bool isDescending); 142 | 143 | /// 144 | /// find entities 145 | /// 146 | /// filter definition 147 | /// collection of entity 148 | Task> FindAsync(FilterDefinition filter); 149 | 150 | /// 151 | /// find entities 152 | /// 153 | /// expression filter 154 | /// collection of entity 155 | Task> FindAsync(Expression> filter); 156 | 157 | /// 158 | /// find entities with paging 159 | /// 160 | /// expression filter 161 | /// page index, based on 0 162 | /// number of items in page 163 | /// collection of entity 164 | Task> FindAsync(Expression> filter, int pageIndex, int size); 165 | 166 | /// 167 | /// find entities with paging and ordering 168 | /// default ordering is descending 169 | /// 170 | /// expression filter 171 | /// ordering parameters 172 | /// page index, based on 0 173 | /// number of items in page 174 | /// collection of entity 175 | Task> FindAsync(Expression> filter, Expression> order, int pageIndex, int size); 176 | 177 | /// 178 | /// find entities with paging and ordering in direction 179 | /// 180 | /// expression filter 181 | /// ordering parameters 182 | /// page index, based on 0 183 | /// number of items in page 184 | /// ordering direction 185 | /// collection of entity 186 | Task> FindAsync(Expression> filter, Expression> order, int pageIndex, int size, bool isDescending); 187 | #endregion Find 188 | 189 | #region FindAll 190 | 191 | /// 192 | /// fetch all items in collection 193 | /// 194 | /// collection of entity 195 | IEnumerable FindAll(); 196 | 197 | /// 198 | /// fetch all items in collection with paging 199 | /// 200 | /// page index, based on 0 201 | /// number of items in page 202 | /// collection of entity 203 | IEnumerable FindAll(int pageIndex, int size); 204 | 205 | /// 206 | /// fetch all items in collection with paging and ordering 207 | /// default ordering is descending 208 | /// 209 | /// ordering parameters 210 | /// page index, based on 0 211 | /// number of items in page 212 | /// collection of entity 213 | IEnumerable FindAll(Expression> order, int pageIndex, int size); 214 | 215 | /// 216 | /// fetch all items in collection with paging and ordering in direction 217 | /// 218 | /// ordering parameters 219 | /// page index, based on 0 220 | /// number of items in page 221 | /// ordering direction 222 | /// collection of entity 223 | IEnumerable FindAll(Expression> order, int pageIndex, int size, bool isDescending); 224 | 225 | /// 226 | /// fetch all items in collection 227 | /// 228 | /// collection of entity 229 | Task> FindAllAsync(); 230 | 231 | /// 232 | /// fetch all items in collection with paging 233 | /// 234 | /// page index, based on 0 235 | /// number of items in page 236 | /// collection of entity 237 | Task> FindAllAsync(int pageIndex, int size); 238 | 239 | /// 240 | /// fetch all items in collection with paging and ordering 241 | /// default ordering is descending 242 | /// 243 | /// ordering parameters 244 | /// page index, based on 0 245 | /// number of items in page 246 | /// collection of entity 247 | Task> FindAllAsync(Expression> order, int pageIndex, int size); 248 | 249 | /// 250 | /// fetch all items in collection with paging and ordering in direction 251 | /// 252 | /// ordering parameters 253 | /// page index, based on 0 254 | /// number of items in page 255 | /// ordering direction 256 | /// collection of entity 257 | Task> FindAllAsync(Expression> order, int pageIndex, int size, bool isDescending); 258 | #endregion FindAll 259 | 260 | #region First 261 | 262 | /// 263 | /// get first item in collection 264 | /// 265 | /// entity of 266 | T First(); 267 | 268 | /// 269 | /// get first item in query 270 | /// 271 | /// filter definition 272 | /// entity of 273 | T First(FilterDefinition filter); 274 | 275 | /// 276 | /// get first item in query 277 | /// 278 | /// expression filter 279 | /// entity of 280 | T First(Expression> filter); 281 | 282 | /// 283 | /// get first item in query with order 284 | /// 285 | /// expression filter 286 | /// ordering parameters 287 | /// entity of 288 | T First(Expression> filter, Expression> order); 289 | 290 | /// 291 | /// get first item in query with order and direction 292 | /// 293 | /// expression filter 294 | /// ordering parameters 295 | /// ordering direction 296 | /// entity of 297 | T First(Expression> filter, Expression> order, bool isDescending); 298 | 299 | /// 300 | /// get first item in collection 301 | /// 302 | /// entity of 303 | Task FirstAsync(); 304 | 305 | /// 306 | /// get first item in query 307 | /// 308 | /// filter definition 309 | /// entity of 310 | Task FirstAsync(FilterDefinition filter); 311 | 312 | /// 313 | /// get first item in query 314 | /// 315 | /// expression filter 316 | /// entity of 317 | Task FirstAsync(Expression> filter); 318 | 319 | /// 320 | /// get first item in query with order 321 | /// 322 | /// expression filter 323 | /// ordering parameters 324 | /// entity of 325 | Task FirstAsync(Expression> filter, Expression> order); 326 | 327 | /// 328 | /// get first item in query with order and direction 329 | /// 330 | /// expression filter 331 | /// ordering parameters 332 | /// ordering direction 333 | /// entity of 334 | Task FirstAsync(Expression> filter, Expression> order, bool isDescending); 335 | #endregion First 336 | 337 | #region Get 338 | 339 | /// 340 | /// get by id 341 | /// 342 | /// id value 343 | /// entity of 344 | T Get(string id); 345 | 346 | /// 347 | /// get by id 348 | /// 349 | /// id value 350 | /// entity of 351 | Task GetAsync(string id); 352 | #endregion Get 353 | 354 | #region Insert 355 | 356 | /// 357 | /// insert entity 358 | /// 359 | /// entity 360 | void Insert(T entity); 361 | 362 | /// 363 | /// insert entity 364 | /// 365 | /// entity 366 | Task InsertAsync(T entity); 367 | 368 | /// 369 | /// insert entity collection 370 | /// 371 | /// collection of entities 372 | void Insert(IEnumerable entities); 373 | 374 | /// 375 | /// insert entity collection 376 | /// 377 | /// collection of entities 378 | Task InsertAsync(IEnumerable entities); 379 | 380 | #endregion Insert 381 | 382 | #region Last 383 | 384 | /// 385 | /// get last item in collection 386 | /// 387 | /// entity of 388 | T Last(); 389 | 390 | /// 391 | /// get last item in query 392 | /// 393 | /// filter definition 394 | /// entity of 395 | T Last(FilterDefinition filter); 396 | 397 | /// 398 | /// get last item in query 399 | /// 400 | /// expression filter 401 | /// entity of 402 | T Last(Expression> filter); 403 | 404 | /// 405 | /// get last item in query with order 406 | /// 407 | /// expression filter 408 | /// ordering parameters 409 | /// entity of 410 | T Last(Expression> filter, Expression> order); 411 | 412 | /// 413 | /// get last item in query with order and direction 414 | /// 415 | /// expression filter 416 | /// ordering parameters 417 | /// ordering direction 418 | /// entity of 419 | T Last(Expression> filter, Expression> order, bool isDescending); 420 | 421 | /// 422 | /// get last item in collection 423 | /// 424 | /// entity of 425 | Task LastAsync(); 426 | 427 | /// 428 | /// get last item in query 429 | /// 430 | /// filter definition 431 | /// entity of 432 | Task LastAsync(FilterDefinition filter); 433 | 434 | /// 435 | /// get last item in query 436 | /// 437 | /// expression filter 438 | /// entity of 439 | Task LastAsync(Expression> filter); 440 | 441 | /// 442 | /// get last item in query with order 443 | /// 444 | /// expression filter 445 | /// ordering parameters 446 | /// entity of 447 | Task LastAsync(Expression> filter, Expression> order); 448 | 449 | /// 450 | /// get last item in query with order and direction 451 | /// 452 | /// expression filter 453 | /// ordering parameters 454 | /// ordering direction 455 | /// entity of 456 | Task LastAsync(Expression> filter, Expression> order, bool isDescending); 457 | #endregion Last 458 | 459 | #region Replace 460 | 461 | /// 462 | /// replace an existing entity 463 | /// 464 | /// entity 465 | bool Replace(T entity); 466 | 467 | /// 468 | /// replace an existing entity 469 | /// 470 | /// entity 471 | Task ReplaceAsync(T entity); 472 | 473 | /// 474 | /// replace collection of entities 475 | /// 476 | /// collection of entities 477 | void Replace(IEnumerable entities); 478 | 479 | /// 480 | /// replace collection of entities 481 | /// 482 | /// collection of entities 483 | Task ReplaceAsync(IEnumerable entities); 484 | 485 | #endregion Replace 486 | 487 | #region FindOneAndUpdate 488 | 489 | /// 490 | /// Find by filter definition and update with update definition 491 | /// 492 | /// filter definition 493 | /// update definition 494 | /// optional options like isUpsert 495 | /// returns T 496 | T FindOneAndUpdate(FilterDefinition filter, UpdateDefinition update, FindOneAndUpdateOptions options = null); 497 | 498 | /// 499 | /// Find by filter definition and update with update definition 500 | /// 501 | /// filter definition 502 | /// update definition 503 | /// optional options like isUpsert 504 | /// returns T 505 | Task FindOneAndUpdateAsync(FilterDefinition filter, UpdateDefinition update, FindOneAndUpdateOptions options = null); 506 | 507 | /// 508 | /// find one and update 509 | /// 510 | /// 511 | /// 512 | /// 513 | /// return updated entity 514 | T FindOneAndUpdate(Expression> filter, UpdateDefinition update, FindOneAndUpdateOptions options = null); 515 | 516 | /// 517 | /// find one and update 518 | /// 519 | /// 520 | /// 521 | /// 522 | /// return updated entity 523 | Task FindOneAndUpdateAsync(Expression> filter, UpdateDefinition update, FindOneAndUpdateOptions options = null); 524 | #endregion FindOneAndUpdate 525 | 526 | #region Update 527 | 528 | /// 529 | /// update an entity with updated fields 530 | /// 531 | /// id 532 | /// updated field(s) 533 | /// true if successful, otherwise false 534 | bool Update(string id, params UpdateDefinition[] updates); 535 | 536 | /// 537 | /// update an entity with updated fields 538 | /// 539 | /// entity 540 | /// updated field(s) 541 | /// true if successful, otherwise false 542 | bool Update(T entity, params UpdateDefinition[] updates); 543 | 544 | /// 545 | /// update found entities by filter with updated fields 546 | /// 547 | /// collection filter 548 | /// updated field(s) 549 | /// true if successful, otherwise false 550 | bool Update(FilterDefinition filter, params UpdateDefinition[] updates); 551 | 552 | /// 553 | /// update found entities by filter with updated fields 554 | /// 555 | /// collection filter 556 | /// updated field(s) 557 | /// true if successful, otherwise false 558 | bool Update(Expression> filter, params UpdateDefinition[] updates); 559 | 560 | /// 561 | /// update a property field in an entity 562 | /// 563 | /// field type 564 | /// entity 565 | /// field 566 | /// new value 567 | /// true if successful, otherwise false 568 | bool Update(T entity, Expression> field, TField value); 569 | 570 | /// 571 | /// update a property field in entities 572 | /// 573 | /// field type 574 | /// filter 575 | /// field 576 | /// new value 577 | /// true if successful, otherwise false 578 | bool Update(FilterDefinition filter, Expression> field, TField value); 579 | 580 | /// 581 | /// update a property field in an entity 582 | /// 583 | /// field type 584 | /// entity 585 | /// field 586 | /// new value 587 | Task UpdateAsync(T entity, Expression> field, TField value); 588 | 589 | /// 590 | /// update an entity with updated fields 591 | /// 592 | /// id 593 | /// updated field(s) 594 | Task UpdateAsync(string id, params UpdateDefinition[] updates); 595 | 596 | /// 597 | /// update an entity with updated fields 598 | /// 599 | /// entity 600 | /// updated field(s) 601 | Task UpdateAsync(T entity, params UpdateDefinition[] updates); 602 | 603 | /// 604 | /// update a property field in entities 605 | /// 606 | /// field type 607 | /// filter 608 | /// field 609 | /// new value 610 | Task UpdateAsync(FilterDefinition filter, Expression> field, TField value); 611 | 612 | /// 613 | /// update found entities by filter with updated fields 614 | /// 615 | /// collection filter 616 | /// updated field(s) 617 | Task UpdateAsync(FilterDefinition filter, params UpdateDefinition[] updates); 618 | 619 | /// 620 | /// update found entities by filter with updated fields 621 | /// 622 | /// collection filter 623 | /// updated field(s) 624 | Task UpdateAsync(Expression> filter, params UpdateDefinition[] updates); 625 | #endregion Update 626 | 627 | #endregion CRUD 628 | 629 | #region Utils 630 | /// 631 | /// validate if filter result exists 632 | /// 633 | /// expression filter 634 | /// true if exists, otherwise false 635 | bool Any(Expression> filter); 636 | 637 | /// 638 | /// validate if filter result exists 639 | /// 640 | /// expression filter 641 | /// true if exists, otherwise false 642 | Task AnyAsync(Expression> filter); 643 | 644 | /// 645 | /// Drop collection from database 646 | /// 647 | void DropCollection(); 648 | 649 | /// 650 | /// Drop collection from database 651 | /// 652 | Task DropCollectionAsync(); 653 | 654 | #region Count 655 | /// 656 | /// get number of filtered documents 657 | /// 658 | /// expression filter 659 | /// returns the count of documents that match the query for a collection or view. 660 | long Count(Expression> filter); 661 | 662 | /// 663 | /// get number of filtered documents async 664 | /// 665 | /// expression filter 666 | /// returns the count of documents that match the query for a collection or view. 667 | Task CountAsync(Expression> filter); 668 | #endregion Count 669 | 670 | #region EstimatedCount 671 | /// 672 | /// get number of all documents in collection with options 673 | /// 674 | /// count options 675 | /// returns the count of all documents in a collection or view with count options. 676 | long EstimatedCount(EstimatedDocumentCountOptions options); 677 | 678 | /// 679 | /// get number of all documents in collection with options 680 | /// 681 | /// count options 682 | /// returns the count of all documents in a collection or view with count options. 683 | Task EstimatedCountAsync(EstimatedDocumentCountOptions options); 684 | 685 | /// 686 | /// get number of all documents in collection 687 | /// 688 | /// returns the count of all documents in a collection or view. 689 | long EstimatedCount(); 690 | 691 | /// 692 | /// get number of all documents in collection 693 | /// 694 | /// returns the count of all documents in a collection or view. 695 | Task EstimatedCountAsync(); 696 | #endregion EstimatedCount 697 | 698 | #endregion Utils 699 | } 700 | } -------------------------------------------------------------------------------- /Repository.Mongo/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("Repository.Mongo")] 8 | [assembly: AssemblyDescription("Repository pattern for MongoDB with extended features")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("MongoDB Repository Pattern")] 12 | [assembly: AssemblyCopyright("Copyright © 2017")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("99ae887a-db4a-4c56-80c8-21f36007028d")] 23 | -------------------------------------------------------------------------------- /Repository.Mongo/Repository.Mongo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Repository pattern for MongoDB 5 | Repository.Mongo 6 | Usame Esendir 7 | netstandard1.5;netstandard2.0;net452 8 | latest 9 | true 10 | Repository.Mongo 11 | Repository.Mongo 12 | mongo;repository;mongodb;nosql; 13 | Async functions 14 | https://github.com/esendir/MongoRepository 15 | false 16 | false 17 | false 18 | false 19 | false 20 | false 21 | 2.3.0 22 | 2.3.0 23 | 2.3.0 24 | False 25 | False 26 | https://github.com/esendir/mongorepository.git 27 | en 28 | git 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | TRACE;DEBUG;NETSTANDARD1_5;netstandard2_0;NET452 38 | 39 | 40 | 41 | obj\Debug\netstandard1.5\Repository.Mongo.xml 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Repository.Mongo/Repository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Net.Sockets; 5 | using System.Threading.Tasks; 6 | using System.Linq.Expressions; 7 | using System.Collections.Generic; 8 | // Nuget Polly 9 | using Polly; 10 | using Polly.Retry; 11 | // Nuget MongoDB 12 | using MongoDB.Driver; 13 | 14 | namespace Repository.Mongo 15 | { 16 | /// 17 | /// repository implementation for mongo 18 | /// 19 | /// 20 | public class Repository : IRepository 21 | where T : IEntity 22 | { 23 | #region MongoSpecific 24 | private IMongoCollection _collection; 25 | private readonly IMongoDatabase _mongoDatabase; 26 | private readonly string _connectionString; 27 | private readonly string _collectionName; 28 | private readonly IMongoClient _mongoClient; 29 | private readonly string _databaseName; 30 | private RetryPolicy _retryPolicy; 31 | private AsyncRetryPolicy _asyncRetryPolicy; 32 | 33 | /// 34 | /// if you already have mongo database and where collection name will be name of the repository 35 | /// 36 | /// 37 | public Repository(IMongoDatabase mongoDatabase) : this(mongoDatabase, null) 38 | { 39 | } 40 | 41 | /// 42 | /// if you already have mongo database 43 | /// 44 | /// 45 | /// 46 | public Repository(IMongoDatabase mongoDatabase, string collectionName) 47 | { 48 | _mongoDatabase = mongoDatabase; 49 | _collectionName = collectionName; 50 | } 51 | 52 | /// 53 | /// if you already have mongo client and where collection name will be name of the repository 54 | /// 55 | /// mongo client object 56 | /// database name 57 | public Repository(IMongoClient mongoClient, string databaseName) : this(mongoClient, databaseName, null) 58 | { 59 | } 60 | 61 | /// 62 | /// if you already have mongo client 63 | /// 64 | /// mongo client object 65 | /// database name 66 | /// collection name 67 | public Repository(IMongoClient mongoClient, string databaseName, string collectionName) 68 | { 69 | _mongoClient = mongoClient; 70 | _databaseName = databaseName; 71 | _collectionName = collectionName; 72 | } 73 | 74 | 75 | /// 76 | /// where collection name will be name of the repository 77 | /// 78 | /// connection string 79 | public Repository(string connectionString) : this(connectionString, null) 80 | { 81 | } 82 | 83 | /// 84 | /// with custom settings 85 | /// 86 | /// connection string 87 | /// collection name 88 | public Repository(string connectionString, string collectionName) 89 | { 90 | _connectionString = connectionString; 91 | _collectionName = collectionName; 92 | } 93 | 94 | private IMongoCollection GetCollection() 95 | { 96 | if (_mongoDatabase != null) 97 | { 98 | return Database.GetCollection(_mongoDatabase, _collectionName); 99 | } 100 | 101 | if (_mongoClient != null) 102 | { 103 | return Database.GetCollection(_mongoClient, _databaseName, _collectionName); 104 | } 105 | 106 | return Database.GetCollectionFromConnectionString(_connectionString, _collectionName); 107 | } 108 | 109 | /// 110 | /// Defined collection name 111 | /// 112 | public string CollectionName 113 | { 114 | get { return _collectionName; } 115 | } 116 | 117 | /// 118 | /// mongo collection 119 | /// 120 | public IMongoCollection Collection 121 | { 122 | get 123 | { 124 | return _collection ??= GetCollection(); 125 | } 126 | } 127 | 128 | /// 129 | /// filter for collection 130 | /// 131 | public FilterDefinitionBuilder Filter 132 | { 133 | get 134 | { 135 | return Builders.Filter; 136 | } 137 | } 138 | 139 | /// 140 | /// projector for collection 141 | /// 142 | public ProjectionDefinitionBuilder Project 143 | { 144 | get 145 | { 146 | return Builders.Projection; 147 | } 148 | } 149 | 150 | /// 151 | /// updater for collection 152 | /// 153 | public UpdateDefinitionBuilder Updater 154 | { 155 | get 156 | { 157 | return Builders.Update; 158 | } 159 | } 160 | 161 | /// 162 | /// sort for collection 163 | /// 164 | public SortDefinitionBuilder Sort 165 | { 166 | get 167 | { 168 | return Builders.Sort; 169 | } 170 | } 171 | 172 | private IAsyncCursor Query(FilterDefinition filter, FindOptions options = null) 173 | { 174 | return Retry(() => Collection.FindSync(filter, options)); 175 | } 176 | 177 | private IAsyncCursor Query(Expression> filter, FindOptions options = null) 178 | { 179 | return Retry(() => Collection.FindSync(filter, options)); 180 | } 181 | 182 | private IAsyncCursor Query(FindOptions options = null) 183 | { 184 | return Query(Filter.Empty, options); 185 | } 186 | 187 | private Task> QueryAsync(FilterDefinition filter, FindOptions options = null) 188 | { 189 | return RetryAsync(() => Collection.FindAsync(filter, options)); 190 | } 191 | 192 | private Task> QueryAsync(Expression> filter, FindOptions options = null) 193 | { 194 | return RetryAsync(() => Collection.FindAsync(filter, options)); 195 | } 196 | 197 | private Task> QueryAsync(FindOptions options = null) 198 | { 199 | return QueryAsync(Filter.Empty, options); 200 | } 201 | #endregion MongoSpecific 202 | 203 | #region CRUD 204 | 205 | #region Delete 206 | 207 | /// 208 | /// delete entity 209 | /// 210 | /// entity 211 | public bool Delete(T entity) 212 | { 213 | return Delete(entity.Id); 214 | } 215 | 216 | /// 217 | /// delete entity 218 | /// 219 | /// entity 220 | public Task DeleteAsync(T entity) 221 | { 222 | return DeleteAsync(entity.Id); 223 | } 224 | 225 | /// 226 | /// delete by id 227 | /// 228 | /// id 229 | public virtual bool Delete(string id) 230 | { 231 | return Retry(() => 232 | { 233 | return Collection.DeleteOne(i => i.Id == id).IsAcknowledged; 234 | }); 235 | } 236 | 237 | /// 238 | /// delete by id 239 | /// 240 | /// id 241 | public virtual Task DeleteAsync(string id) 242 | { 243 | return RetryAsync(async () => 244 | { 245 | var result = await Collection.DeleteOneAsync(i => i.Id == id); 246 | return result.IsAcknowledged; 247 | }); 248 | } 249 | 250 | /// 251 | /// delete items with filter 252 | /// 253 | /// expression filter 254 | public virtual bool Delete(Expression> filter) 255 | { 256 | return Retry(() => 257 | { 258 | return Collection.DeleteMany(filter).IsAcknowledged; 259 | }); 260 | } 261 | 262 | /// 263 | /// delete items with filter 264 | /// 265 | /// expression filter 266 | public virtual Task DeleteAsync(Expression> filter) 267 | { 268 | return RetryAsync(async () => 269 | { 270 | var result = await Collection.DeleteManyAsync(filter); 271 | return result.IsAcknowledged; 272 | }); 273 | } 274 | 275 | /// 276 | /// delete all documents 277 | /// 278 | public virtual bool DeleteAll() 279 | { 280 | return Retry(() => 281 | { 282 | return Collection.DeleteMany(Filter.Empty).IsAcknowledged; 283 | }); 284 | } 285 | 286 | /// 287 | /// delete all documents 288 | /// 289 | public virtual Task DeleteAllAsync() 290 | { 291 | return RetryAsync(async () => 292 | { 293 | var result = await Collection.DeleteManyAsync(Filter.Empty); 294 | return result.IsAcknowledged; 295 | }); 296 | } 297 | #endregion Delete 298 | 299 | #region Find 300 | /// 301 | /// find entities 302 | /// 303 | /// expression filter 304 | /// collection of entity 305 | public virtual IEnumerable Find(FilterDefinition filter) 306 | { 307 | return Query(filter).ToEnumerable(); 308 | } 309 | 310 | /// 311 | /// find entities 312 | /// 313 | /// expression filter 314 | /// collection of entity 315 | public virtual IEnumerable Find(Expression> filter) 316 | { 317 | return Query(filter).ToEnumerable(); 318 | } 319 | 320 | /// 321 | /// find entities with paging 322 | /// 323 | /// expression filter 324 | /// page index, based on 0 325 | /// number of items in page 326 | /// collection of entity 327 | public IEnumerable Find(Expression> filter, int pageIndex, int size) 328 | { 329 | return Find(filter, i => i.Id, pageIndex, size); 330 | } 331 | 332 | /// 333 | /// find entities with paging and ordering 334 | /// default ordering is descending 335 | /// 336 | /// expression filter 337 | /// ordering parameters 338 | /// page index, based on 0 339 | /// number of items in page 340 | /// collection of entity 341 | public IEnumerable Find(Expression> filter, Expression> order, int pageIndex, int size) 342 | { 343 | return Find(filter, order, pageIndex, size, true); 344 | } 345 | 346 | /// 347 | /// find entities with paging and ordering in direction 348 | /// 349 | /// expression filter 350 | /// ordering parameters 351 | /// page index, based on 0 352 | /// number of items in page 353 | /// ordering direction 354 | /// collection of entity 355 | public virtual IEnumerable Find(Expression> filter, Expression> order, int pageIndex, int size, bool isDescending) 356 | { 357 | return Query(filter, 358 | new FindOptions 359 | { 360 | Skip = pageIndex * size, 361 | Limit = size, 362 | Sort = isDescending ? Sort.Descending(order) : Sort.Ascending(order) 363 | }).ToEnumerable(); 364 | } 365 | 366 | /// 367 | /// find entities 368 | /// 369 | /// expression filter 370 | /// collection of entity 371 | public virtual async Task> FindAsync(FilterDefinition filter) 372 | { 373 | return (await QueryAsync(filter)).ToEnumerable(); 374 | } 375 | 376 | /// 377 | /// find entities 378 | /// 379 | /// expression filter 380 | /// collection of entity 381 | public virtual async Task> FindAsync(Expression> filter) 382 | { 383 | return (await QueryAsync(filter)).ToEnumerable(); 384 | } 385 | 386 | /// 387 | /// find entities with paging 388 | /// 389 | /// expression filter 390 | /// page index, based on 0 391 | /// number of items in page 392 | /// collection of entity 393 | public Task> FindAsync(Expression> filter, int pageIndex, int size) 394 | { 395 | return FindAsync(filter, i => i.Id, pageIndex, size); 396 | } 397 | 398 | /// 399 | /// find entities with paging and ordering 400 | /// default ordering is descending 401 | /// 402 | /// expression filter 403 | /// ordering parameters 404 | /// page index, based on 0 405 | /// number of items in page 406 | /// collection of entity 407 | public Task> FindAsync(Expression> filter, Expression> order, int pageIndex, int size) 408 | { 409 | return FindAsync(filter, order, pageIndex, size, true); 410 | } 411 | 412 | /// 413 | /// find entities with paging and ordering in direction 414 | /// 415 | /// expression filter 416 | /// ordering parameters 417 | /// page index, based on 0 418 | /// number of items in page 419 | /// ordering direction 420 | /// collection of entity 421 | public virtual async Task> FindAsync(Expression> filter, Expression> order, int pageIndex, int size, bool isDescending) 422 | { 423 | return (await QueryAsync(filter, 424 | new FindOptions 425 | { 426 | Skip = pageIndex * size, 427 | Limit = size, 428 | Sort = isDescending ? Sort.Descending(order) : Sort.Ascending(order) 429 | })).ToEnumerable(); 430 | } 431 | #endregion Find 432 | 433 | #region FindAll 434 | 435 | /// 436 | /// fetch all items in collection 437 | /// 438 | /// collection of entity 439 | public virtual IEnumerable FindAll() 440 | { 441 | return Query().ToEnumerable(); 442 | } 443 | 444 | /// 445 | /// fetch all items in collection with paging 446 | /// 447 | /// page index, based on 0 448 | /// number of items in page 449 | /// collection of entity 450 | public IEnumerable FindAll(int pageIndex, int size) 451 | { 452 | return FindAll(i => i.Id, pageIndex, size); 453 | } 454 | 455 | /// 456 | /// fetch all items in collection with paging and ordering 457 | /// default ordering is descending 458 | /// 459 | /// ordering parameters 460 | /// page index, based on 0 461 | /// number of items in page 462 | /// collection of entity 463 | public IEnumerable FindAll(Expression> order, int pageIndex, int size) 464 | { 465 | return FindAll(order, pageIndex, size, true); 466 | } 467 | 468 | /// 469 | /// fetch all items in collection with paging and ordering in direction 470 | /// 471 | /// ordering parameters 472 | /// page index, based on 0 473 | /// number of items in page 474 | /// ordering direction 475 | /// collection of entity 476 | public virtual IEnumerable FindAll(Expression> order, int pageIndex, int size, bool isDescending) 477 | { 478 | return Query(new FindOptions 479 | { 480 | Skip = pageIndex * size, 481 | Limit = size, 482 | Sort = isDescending ? Sort.Descending(order) : Sort.Ascending(order) 483 | }).ToEnumerable(); 484 | } 485 | 486 | /// 487 | /// fetch all items in collection 488 | /// 489 | /// collection of entity 490 | public virtual async Task> FindAllAsync() 491 | { 492 | return (await QueryAsync()).ToEnumerable(); 493 | } 494 | 495 | /// 496 | /// fetch all items in collection with paging 497 | /// 498 | /// page index, based on 0 499 | /// number of items in page 500 | /// collection of entity 501 | public Task> FindAllAsync(int pageIndex, int size) 502 | { 503 | return FindAllAsync(i => i.Id, pageIndex, size); 504 | } 505 | 506 | /// 507 | /// fetch all items in collection with paging and ordering 508 | /// default ordering is descending 509 | /// 510 | /// ordering parameters 511 | /// page index, based on 0 512 | /// number of items in page 513 | /// collection of entity 514 | public Task> FindAllAsync(Expression> order, int pageIndex, int size) 515 | { 516 | return FindAllAsync(order, pageIndex, size, true); 517 | } 518 | 519 | /// 520 | /// fetch all items in collection with paging and ordering in direction 521 | /// 522 | /// ordering parameters 523 | /// page index, based on 0 524 | /// number of items in page 525 | /// ordering direction 526 | /// collection of entity 527 | public virtual async Task> FindAllAsync(Expression> order, int pageIndex, int size, bool isDescending) 528 | { 529 | return (await QueryAsync(new FindOptions 530 | { 531 | Skip = pageIndex * size, 532 | Limit = size, 533 | Sort = isDescending ? Sort.Descending(order) : Sort.Ascending(order) 534 | })).ToEnumerable(); 535 | } 536 | #endregion FindAll 537 | 538 | #region First 539 | 540 | /// 541 | /// get first item in collection 542 | /// 543 | /// entity of 544 | public T First() 545 | { 546 | return FindAll(i => i.Id, 0, 1, false).FirstOrDefault(); 547 | } 548 | 549 | /// 550 | /// get first item in query 551 | /// 552 | /// expression filter 553 | /// entity of 554 | public T First(FilterDefinition filter) 555 | { 556 | return Find(filter).FirstOrDefault(); 557 | } 558 | 559 | /// 560 | /// get first item in query 561 | /// 562 | /// expression filter 563 | /// entity of 564 | public T First(Expression> filter) 565 | { 566 | return First(filter, i => i.Id); 567 | } 568 | 569 | /// 570 | /// get first item in query with order 571 | /// 572 | /// expression filter 573 | /// ordering parameters 574 | /// entity of 575 | public T First(Expression> filter, Expression> order) 576 | { 577 | return First(filter, order, false); 578 | } 579 | 580 | /// 581 | /// get first item in query with order and direction 582 | /// 583 | /// expression filter 584 | /// ordering parameters 585 | /// ordering direction 586 | /// entity of 587 | public T First(Expression> filter, Expression> order, bool isDescending) 588 | { 589 | return Find(filter, order, 0, 1, isDescending).FirstOrDefault(); 590 | } 591 | 592 | /// 593 | /// get first item in collection 594 | /// 595 | /// entity of 596 | public async Task FirstAsync() 597 | { 598 | return (await FindAllAsync(i => i.Id, 0, 1, false)).FirstOrDefault(); 599 | } 600 | 601 | /// 602 | /// get first item in query 603 | /// 604 | /// expression filter 605 | /// entity of 606 | public async Task FirstAsync(FilterDefinition filter) 607 | { 608 | return (await FindAsync(filter)).FirstOrDefault(); 609 | } 610 | 611 | /// 612 | /// get first item in query 613 | /// 614 | /// expression filter 615 | /// entity of 616 | public Task FirstAsync(Expression> filter) 617 | { 618 | return FirstAsync(filter, i => i.Id); 619 | } 620 | 621 | /// 622 | /// get first item in query with order 623 | /// 624 | /// expression filter 625 | /// ordering parameters 626 | /// entity of 627 | public Task FirstAsync(Expression> filter, Expression> order) 628 | { 629 | return FirstAsync(filter, order, false); 630 | } 631 | 632 | /// 633 | /// get first item in query with order and direction 634 | /// 635 | /// expression filter 636 | /// ordering parameters 637 | /// ordering direction 638 | /// entity of 639 | public async Task FirstAsync(Expression> filter, Expression> order, bool isDescending) 640 | { 641 | return (await FindAsync(filter, order, 0, 1, isDescending)).FirstOrDefault(); 642 | } 643 | #endregion First 644 | 645 | #region Get 646 | 647 | /// 648 | /// get by id 649 | /// 650 | /// id value 651 | /// entity of 652 | public virtual T Get(string id) 653 | { 654 | return First(i => i.Id == id); 655 | } 656 | 657 | /// 658 | /// get by id 659 | /// 660 | /// id value 661 | /// entity of 662 | public virtual Task GetAsync(string id) 663 | { 664 | return FirstAsync(i => i.Id == id); 665 | } 666 | #endregion Get 667 | 668 | #region Insert 669 | 670 | /// 671 | /// insert entity 672 | /// 673 | /// entity 674 | public virtual void Insert(T entity) 675 | { 676 | Retry(() => 677 | { 678 | Collection.InsertOne(entity); 679 | return true; 680 | }); 681 | } 682 | 683 | /// 684 | /// insert entity 685 | /// 686 | /// entity 687 | public virtual Task InsertAsync(T entity) 688 | { 689 | return RetryAsync(() => 690 | { 691 | return Collection.InsertOneAsync(entity); 692 | }); 693 | } 694 | 695 | /// 696 | /// insert entity collection 697 | /// 698 | /// collection of entities 699 | public virtual void Insert(IEnumerable entities) 700 | { 701 | Retry(() => 702 | { 703 | Collection.InsertMany(entities); 704 | return true; 705 | }); 706 | } 707 | 708 | /// 709 | /// insert entity collection 710 | /// 711 | /// collection of entities 712 | public virtual Task InsertAsync(IEnumerable entities) 713 | { 714 | return RetryAsync(() => 715 | { 716 | return Collection.InsertManyAsync(entities); 717 | }); 718 | } 719 | #endregion Insert 720 | 721 | #region Last 722 | 723 | /// 724 | /// get first item in collection 725 | /// 726 | /// entity of 727 | public T Last() 728 | { 729 | return FindAll(i => i.Id, 0, 1, true).FirstOrDefault(); 730 | } 731 | 732 | /// 733 | /// get last item in query 734 | /// 735 | /// expression filter 736 | /// entity of 737 | public T Last(FilterDefinition filter) 738 | { 739 | return Query(filter, 740 | new FindOptions 741 | { 742 | Sort = Sort.Descending(i => i.Id) 743 | }).FirstOrDefault(); 744 | } 745 | 746 | /// 747 | /// get last item in query 748 | /// 749 | /// expression filter 750 | /// entity of 751 | public T Last(Expression> filter) 752 | { 753 | return Last(filter, i => i.Id); 754 | } 755 | 756 | /// 757 | /// get last item in query with order 758 | /// 759 | /// expression filter 760 | /// ordering parameters 761 | /// entity of 762 | public T Last(Expression> filter, Expression> order) 763 | { 764 | return Last(filter, order, false); 765 | } 766 | 767 | /// 768 | /// get last item in query with order and direction 769 | /// 770 | /// expression filter 771 | /// ordering parameters 772 | /// ordering direction 773 | /// entity of 774 | public T Last(Expression> filter, Expression> order, bool isDescending) 775 | { 776 | return First(filter, order, !isDescending); 777 | } 778 | 779 | /// 780 | /// get first item in collection 781 | /// 782 | /// entity of 783 | public async Task LastAsync() 784 | { 785 | return (await FindAllAsync(i => i.Id, 0, 1, true)).FirstOrDefault(); 786 | } 787 | 788 | /// 789 | /// get last item in query 790 | /// 791 | /// expression filter 792 | /// entity of 793 | public async Task LastAsync(FilterDefinition filter) 794 | { 795 | return (await QueryAsync(filter, 796 | new FindOptions 797 | { 798 | Sort = Sort.Descending(i => i.Id) 799 | })).FirstOrDefault(); 800 | } 801 | 802 | /// 803 | /// get last item in query 804 | /// 805 | /// expression filter 806 | /// entity of 807 | public Task LastAsync(Expression> filter) 808 | { 809 | return LastAsync(filter, i => i.Id); 810 | } 811 | 812 | /// 813 | /// get last item in query with order 814 | /// 815 | /// expression filter 816 | /// ordering parameters 817 | /// entity of 818 | public Task LastAsync(Expression> filter, Expression> order) 819 | { 820 | return LastAsync(filter, order, false); 821 | } 822 | 823 | /// 824 | /// get last item in query with order and direction 825 | /// 826 | /// expression filter 827 | /// ordering parameters 828 | /// ordering direction 829 | /// entity of 830 | public Task LastAsync(Expression> filter, Expression> order, bool isDescending) 831 | { 832 | return FirstAsync(filter, order, !isDescending); 833 | } 834 | #endregion Last 835 | 836 | #region Replace 837 | 838 | /// 839 | /// replace an existing entity 840 | /// 841 | /// entity 842 | public virtual bool Replace(T entity) 843 | { 844 | return Retry(() => 845 | { 846 | return Collection.ReplaceOne(i => i.Id == entity.Id, entity, new ReplaceOptions { IsUpsert = true }).IsAcknowledged; 847 | }); 848 | } 849 | 850 | /// 851 | /// replace an existing entity 852 | /// 853 | /// entity 854 | public virtual Task ReplaceAsync(T entity) 855 | { 856 | return RetryAsync(async () => 857 | { 858 | var result = await Collection.ReplaceOneAsync(i => i.Id == entity.Id, entity, new ReplaceOptions { IsUpsert = true }); 859 | return result.IsAcknowledged; 860 | }); 861 | } 862 | 863 | /// 864 | /// replace collection of entities 865 | /// 866 | /// collection of entities 867 | public void Replace(IEnumerable entities) 868 | { 869 | foreach (T entity in entities) 870 | { 871 | Replace(entity); 872 | } 873 | } 874 | 875 | /// 876 | /// replace collection of entities 877 | /// 878 | /// collection of entities 879 | public async Task ReplaceAsync(IEnumerable entities) 880 | { 881 | foreach (T entity in entities) 882 | { 883 | await ReplaceAsync(entity); 884 | } 885 | } 886 | #endregion Replace 887 | 888 | #region FindOneAndUpdate 889 | 890 | /// 891 | /// find one and update 892 | /// 893 | /// 894 | /// 895 | /// 896 | /// return updated entity 897 | public T FindOneAndUpdate(FilterDefinition filter, UpdateDefinition update, FindOneAndUpdateOptions options = null) 898 | { 899 | return Collection.FindOneAndUpdate(filter, update, options); 900 | } 901 | 902 | /// 903 | /// find one and update async 904 | /// 905 | /// 906 | /// 907 | /// 908 | /// 909 | public async Task FindOneAndUpdateAsync(FilterDefinition filter, UpdateDefinition update, FindOneAndUpdateOptions options = null) 910 | { 911 | return await Collection.FindOneAndUpdateAsync(filter, update, options); 912 | } 913 | 914 | /// 915 | /// find one and update 916 | /// 917 | /// 918 | /// 919 | /// 920 | /// return updated entity 921 | public T FindOneAndUpdate(Expression> filter, UpdateDefinition update, FindOneAndUpdateOptions options = null) 922 | { 923 | return Collection.FindOneAndUpdate(filter, update, options); 924 | } 925 | 926 | /// 927 | /// find one and update async 928 | /// 929 | /// 930 | /// 931 | /// 932 | /// 933 | public async Task FindOneAndUpdateAsync(Expression> filter, UpdateDefinition update, FindOneAndUpdateOptions options = null) 934 | { 935 | return await Collection.FindOneAndUpdateAsync(filter, update, options); 936 | } 937 | 938 | #endregion FindOneAndUpdate 939 | 940 | #region Update 941 | 942 | /// 943 | /// update a property field in an entity 944 | /// 945 | /// field type 946 | /// entity 947 | /// field 948 | /// new value 949 | /// true if successful, otherwise false 950 | public bool Update(T entity, Expression> field, TField value) 951 | { 952 | return Update(entity, Updater.Set(field, value)); 953 | } 954 | 955 | /// 956 | /// update a property field in an entity 957 | /// 958 | /// field type 959 | /// entity 960 | /// field 961 | /// new value 962 | public Task UpdateAsync(T entity, Expression> field, TField value) 963 | { 964 | return UpdateAsync(entity, Updater.Set(field, value)); 965 | } 966 | 967 | /// 968 | /// update an entity with updated fields 969 | /// 970 | /// id 971 | /// updated field(s) 972 | /// true if successful, otherwise false 973 | public virtual bool Update(string id, params UpdateDefinition[] updates) 974 | { 975 | return Update(Filter.Eq(i => i.Id, id), updates); 976 | } 977 | 978 | /// 979 | /// update an entity with updated fields 980 | /// 981 | /// id 982 | /// updated field(s) 983 | public virtual Task UpdateAsync(string id, params UpdateDefinition[] updates) 984 | { 985 | return UpdateAsync(Filter.Eq(i => i.Id, id), updates); 986 | } 987 | 988 | /// 989 | /// update an entity with updated fields 990 | /// 991 | /// entity 992 | /// updated field(s) 993 | /// true if successful, otherwise false 994 | public virtual bool Update(T entity, params UpdateDefinition[] updates) 995 | { 996 | return Update(entity.Id, updates); 997 | } 998 | 999 | /// 1000 | /// update an entity with updated fields 1001 | /// 1002 | /// entity 1003 | /// updated field(s) 1004 | public virtual Task UpdateAsync(T entity, params UpdateDefinition[] updates) 1005 | { 1006 | return UpdateAsync(entity.Id, updates); 1007 | } 1008 | 1009 | /// 1010 | /// update a property field in entities 1011 | /// 1012 | /// field type 1013 | /// filter 1014 | /// field 1015 | /// new value 1016 | /// true if successful, otherwise false 1017 | public bool Update(FilterDefinition filter, Expression> field, TField value) 1018 | { 1019 | return Update(filter, Updater.Set(field, value)); 1020 | } 1021 | 1022 | /// 1023 | /// update a property field in entities 1024 | /// 1025 | /// field type 1026 | /// filter 1027 | /// field 1028 | /// new value 1029 | public Task UpdateAsync(FilterDefinition filter, Expression> field, TField value) 1030 | { 1031 | return UpdateAsync(filter, Updater.Set(field, value)); 1032 | } 1033 | 1034 | /// 1035 | /// update found entities by filter with updated fields 1036 | /// 1037 | /// collection filter 1038 | /// updated field(s) 1039 | /// true if successful, otherwise false 1040 | public bool Update(FilterDefinition filter, params UpdateDefinition[] updates) 1041 | { 1042 | return Retry(() => 1043 | { 1044 | var update = Updater.Combine(updates).CurrentDate(i => i.ModifiedOn); 1045 | return Collection.UpdateMany(filter, update.CurrentDate(i => i.ModifiedOn)).IsAcknowledged; 1046 | }); 1047 | } 1048 | 1049 | /// 1050 | /// update found entities by filter with updated fields 1051 | /// 1052 | /// collection filter 1053 | /// updated field(s) 1054 | public Task UpdateAsync(FilterDefinition filter, params UpdateDefinition[] updates) 1055 | { 1056 | return RetryAsync(async () => 1057 | { 1058 | var update = Updater.Combine(updates).CurrentDate(i => i.ModifiedOn); 1059 | var result = await Collection.UpdateManyAsync(filter, update.CurrentDate(i => i.ModifiedOn)); 1060 | return result.IsAcknowledged; 1061 | }); 1062 | } 1063 | 1064 | /// 1065 | /// update found entities by filter with updated fields 1066 | /// 1067 | /// collection filter 1068 | /// updated field(s) 1069 | /// true if successful, otherwise false 1070 | public bool Update(Expression> filter, params UpdateDefinition[] updates) 1071 | { 1072 | return Retry(() => 1073 | { 1074 | var update = Updater.Combine(updates).CurrentDate(i => i.ModifiedOn); 1075 | return Collection.UpdateMany(filter, update).IsAcknowledged; 1076 | }); 1077 | } 1078 | 1079 | /// 1080 | /// update found entities by filter with updated fields 1081 | /// 1082 | /// collection filter 1083 | /// updated field(s) 1084 | public Task UpdateAsync(Expression> filter, params UpdateDefinition[] updates) 1085 | { 1086 | return RetryAsync(async () => 1087 | { 1088 | var update = Updater.Combine(updates).CurrentDate(i => i.ModifiedOn); 1089 | var result = await Collection.UpdateManyAsync(filter, update); 1090 | return result.IsAcknowledged; 1091 | }); 1092 | } 1093 | 1094 | #endregion Update 1095 | 1096 | #endregion CRUD 1097 | 1098 | #region Utils 1099 | 1100 | /// 1101 | /// validate if filter result exists 1102 | /// 1103 | /// expression filter 1104 | /// true if exists, otherwise false 1105 | public bool Any(Expression> filter) 1106 | { 1107 | return First(filter) != null; 1108 | } 1109 | 1110 | /// 1111 | /// validate if filter result exists 1112 | /// 1113 | /// expression filter 1114 | /// true if exists, otherwise false 1115 | public async Task AnyAsync(Expression> filter) 1116 | { 1117 | return (await FirstAsync(filter)) != null; 1118 | } 1119 | 1120 | /// 1121 | /// Drop collection from database 1122 | /// 1123 | public void DropCollection() 1124 | { 1125 | Collection.Database.DropCollection(_collectionName); 1126 | } 1127 | 1128 | /// 1129 | /// Drop collection from database 1130 | /// 1131 | public Task DropCollectionAsync() 1132 | { 1133 | return Collection.Database.DropCollectionAsync(_collectionName); 1134 | } 1135 | 1136 | #region Count 1137 | /// 1138 | /// get number of filtered documents 1139 | /// 1140 | /// expression filter 1141 | /// returns the count of documents that match the query for a collection or view. 1142 | public long Count(Expression> filter) 1143 | { 1144 | return Retry(() => 1145 | { 1146 | return Collection.CountDocuments(filter); 1147 | }); 1148 | } 1149 | 1150 | /// 1151 | /// get number of filtered documents async 1152 | /// 1153 | /// expression filter 1154 | /// returns the count of documents that match the query for a collection or view. 1155 | public Task CountAsync(Expression> filter) 1156 | { 1157 | return RetryAsync(() => 1158 | { 1159 | return Collection.CountDocumentsAsync(filter); 1160 | }); 1161 | } 1162 | #endregion Count 1163 | 1164 | #region EstimatedCount 1165 | /// 1166 | /// get number of all documents in collection 1167 | /// 1168 | /// count options 1169 | /// returns the count of all documents in a collection or view with count options. 1170 | public long EstimatedCount(EstimatedDocumentCountOptions options) 1171 | { 1172 | return Retry(() => 1173 | { 1174 | return Collection.EstimatedDocumentCount(options); 1175 | }); 1176 | } 1177 | 1178 | /// 1179 | /// get number of all documents in collection 1180 | /// 1181 | /// count options 1182 | /// returns the count of all documents in a collection or view with count options. 1183 | public Task EstimatedCountAsync(EstimatedDocumentCountOptions options) 1184 | { 1185 | return RetryAsync(() => 1186 | { 1187 | return Collection.EstimatedDocumentCountAsync(options); 1188 | }); 1189 | } 1190 | 1191 | /// 1192 | /// get number of all documents in collection 1193 | /// 1194 | /// returns the count of all documents in a collection or view. 1195 | public long EstimatedCount() 1196 | { 1197 | return Retry(() => 1198 | { 1199 | return Collection.EstimatedDocumentCount(); 1200 | }); 1201 | } 1202 | 1203 | /// 1204 | /// get number of all documents in collection 1205 | /// 1206 | /// returns the count of all documents in a collection or view. 1207 | public Task EstimatedCountAsync() 1208 | { 1209 | return RetryAsync(() => 1210 | { 1211 | return Collection.EstimatedDocumentCountAsync(); 1212 | }); 1213 | } 1214 | #endregion Estimated Count 1215 | 1216 | #endregion Utils 1217 | 1218 | #region RetryPolicy 1219 | /// 1220 | /// retry operation for three times if IOException occurs 1221 | /// 1222 | /// return type 1223 | /// action 1224 | /// action result 1225 | /// 1226 | /// return Retry(() => 1227 | /// { 1228 | /// do_something; 1229 | /// return something; 1230 | /// }); 1231 | /// 1232 | protected virtual TResult Retry(Func action) 1233 | { 1234 | return (_retryPolicy ??= GetPolicyBuilder().Retry(3)).Execute(action); 1235 | } 1236 | 1237 | /// 1238 | /// retry operation for three times if IOException occurs 1239 | /// 1240 | /// return type 1241 | /// action 1242 | /// action result 1243 | /// 1244 | /// return Retry(() => 1245 | /// { 1246 | /// do_something; 1247 | /// return something; 1248 | /// }); 1249 | /// 1250 | protected virtual Task RetryAsync(Func> action) 1251 | { 1252 | return (_asyncRetryPolicy ??= GetPolicyBuilder().RetryAsync(3)).ExecuteAsync(action); 1253 | } 1254 | 1255 | /// 1256 | /// retry operation for three times if IOException occurs 1257 | /// 1258 | /// action 1259 | /// action result 1260 | /// 1261 | /// return Retry(() => 1262 | /// { 1263 | /// do_something; 1264 | /// return something; 1265 | /// }); 1266 | /// 1267 | protected virtual Task RetryAsync(Func action) 1268 | { 1269 | return (_asyncRetryPolicy ??= GetPolicyBuilder().RetryAsync(3)).ExecuteAsync(action); 1270 | } 1271 | 1272 | /// 1273 | /// Get retry policy 1274 | /// 1275 | /// 1276 | protected PolicyBuilder GetPolicyBuilder() 1277 | { 1278 | return RetryPolicy.Handle( 1279 | i => i.InnerException.GetType() == typeof(IOException) || 1280 | i.InnerException.GetType() == typeof(SocketException)); 1281 | } 1282 | #endregion 1283 | } 1284 | } -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # ASP.NET 2 | # Build and test ASP.NET projects. 3 | # Add steps that publish symbols, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/apps/aspnet/build-aspnet-4 5 | 6 | trigger: 7 | - master 8 | 9 | pool: 10 | vmImage: 'windows-latest' 11 | 12 | variables: 13 | solution: '**/*.sln' 14 | buildPlatform: 'Any CPU' 15 | buildConfiguration: 'Release' 16 | 17 | steps: 18 | - task: NuGetToolInstaller@1 19 | 20 | - task: NuGetCommand@2 21 | inputs: 22 | restoreSolution: '$(solution)' 23 | 24 | - task: VSBuild@1 25 | inputs: 26 | solution: '$(solution)' 27 | msbuildArgs: '/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:PackageLocation="$(build.artifactStagingDirectory)"' 28 | platform: '$(buildPlatform)' 29 | configuration: '$(buildConfiguration)' 30 | 31 | - task: VSTest@2 32 | inputs: 33 | platform: '$(buildPlatform)' 34 | configuration: '$(buildConfiguration)' 35 | --------------------------------------------------------------------------------