commit b789b367c2e069513c237dc50f7572a75244affb Author: David Lebee Date: Tue Feb 12 17:39:16 2019 -0500 initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bf793ed --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# Autosave files +*~ + +# build +[Oo]bj/ +[Bb]in/ +packages/ +TestResults/ + +# globs +Makefile.in +*.DS_Store +*.sln.cache +*.suo +*.cache +*.pidb +*.userprefs +*.usertasks +config.log +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.user +*.tar.gz +tarballs/ +test-results/ +Thumbs.db +.vs/ + +# Mac bundle stuff +*.dmg +*.app + +# resharper +*_Resharper.* +*.Resharper + +# dotCover +*.dotCover diff --git a/ObjectStorage.sln b/ObjectStorage.sln new file mode 100644 index 0000000..3449b3f --- /dev/null +++ b/ObjectStorage.sln @@ -0,0 +1,29 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoweredSoft.ObjectStorage.Core", "PoweredSoft.ObjectStorage.Core\PoweredSoft.ObjectStorage.Core.csproj", "{81070762-4F21-402D-B10E-6285A46F0572}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoweredSoft.ObjectStorage.MongoDB", "PoweredSoft.ObjectStorage.MongoDB\PoweredSoft.ObjectStorage.MongoDB.csproj", "{D430A3A6-5D41-4B4D-9379-5761FB691619}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoweredSoft.ObjectStorage.MongoDB.Tests", "PoweredSoft.ObjectStorage.MongoDB.Tests\PoweredSoft.ObjectStorage.MongoDB.Tests.csproj", "{D8CB2E20-7298-4EEB-9694-C54654B1BD1C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {81070762-4F21-402D-B10E-6285A46F0572}.Debug|x86.ActiveCfg = Debug|Any CPU + {81070762-4F21-402D-B10E-6285A46F0572}.Debug|x86.Build.0 = Debug|Any CPU + {81070762-4F21-402D-B10E-6285A46F0572}.Release|x86.ActiveCfg = Release|Any CPU + {81070762-4F21-402D-B10E-6285A46F0572}.Release|x86.Build.0 = Release|Any CPU + {D430A3A6-5D41-4B4D-9379-5761FB691619}.Debug|x86.ActiveCfg = Debug|Any CPU + {D430A3A6-5D41-4B4D-9379-5761FB691619}.Debug|x86.Build.0 = Debug|Any CPU + {D430A3A6-5D41-4B4D-9379-5761FB691619}.Release|x86.ActiveCfg = Release|Any CPU + {D430A3A6-5D41-4B4D-9379-5761FB691619}.Release|x86.Build.0 = Release|Any CPU + {D8CB2E20-7298-4EEB-9694-C54654B1BD1C}.Debug|x86.ActiveCfg = Debug|Any CPU + {D8CB2E20-7298-4EEB-9694-C54654B1BD1C}.Debug|x86.Build.0 = Debug|Any CPU + {D8CB2E20-7298-4EEB-9694-C54654B1BD1C}.Release|x86.ActiveCfg = Release|Any CPU + {D8CB2E20-7298-4EEB-9694-C54654B1BD1C}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/PoweredSoft.ObjectStorage.Core/IObjectStorageClient.cs b/PoweredSoft.ObjectStorage.Core/IObjectStorageClient.cs new file mode 100644 index 0000000..d3059f1 --- /dev/null +++ b/PoweredSoft.ObjectStorage.Core/IObjectStorageClient.cs @@ -0,0 +1,9 @@ +using System; + +namespace PoweredSoft.ObjectStorage.Core +{ + public interface IObjectStorageClient + { + IObjectStorageCollection GetCollection(); + } +} diff --git a/PoweredSoft.ObjectStorage.Core/IObjectStorageCollection.cs b/PoweredSoft.ObjectStorage.Core/IObjectStorageCollection.cs new file mode 100644 index 0000000..ba1a307 --- /dev/null +++ b/PoweredSoft.ObjectStorage.Core/IObjectStorageCollection.cs @@ -0,0 +1,16 @@ +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace PoweredSoft.ObjectStorage.Core +{ + public interface IObjectStorageCollection + { + string CollectionName { get; } + + Task AddAsync(TEntity entity, CancellationToken cancellationToken = default(CancellationToken)); + Task DeleteAsync(TEntity entity, CancellationToken cancellationToken = default(CancellationToken)); + Task UpdateAsync(TEntity entity, CancellationToken cancellationToken = default(CancellationToken)); + IQueryable AsQueryable(); + } +} diff --git a/PoweredSoft.ObjectStorage.Core/PoweredSoft.ObjectStorage.Core.csproj b/PoweredSoft.ObjectStorage.Core/PoweredSoft.ObjectStorage.Core.csproj new file mode 100644 index 0000000..d0ff031 --- /dev/null +++ b/PoweredSoft.ObjectStorage.Core/PoweredSoft.ObjectStorage.Core.csproj @@ -0,0 +1,10 @@ + + + + netstandard2.0 + + + + + + diff --git a/PoweredSoft.ObjectStorage.MongoDB.Tests/CollectionNameAttributeTests.cs b/PoweredSoft.ObjectStorage.MongoDB.Tests/CollectionNameAttributeTests.cs new file mode 100644 index 0000000..2543080 --- /dev/null +++ b/PoweredSoft.ObjectStorage.MongoDB.Tests/CollectionNameAttributeTests.cs @@ -0,0 +1,20 @@ +using System; +using MongoDB.Driver; +using PoweredSoft.ObjectStorage.MongoDB.Tests.Mock; +using PoweredSoft.ObjectStorage.MongoDB.Tests.Mock.Dal; +using Xunit; + +namespace PoweredSoft.ObjectStorage.MongoDB.Tests +{ + public class CollectionNameAttributeTests + { + [Fact] + public void TestingGetCollection() + { + var objectStorageClient = MongoDatabaseFactory.GetObjectStorageClient(); + var collection = objectStorageClient.GetCollection(); + Assert.NotNull(collection); + Assert.NotNull(collection.CollectionName); + } + } +} diff --git a/PoweredSoft.ObjectStorage.MongoDB.Tests/CrudTests.cs b/PoweredSoft.ObjectStorage.MongoDB.Tests/CrudTests.cs new file mode 100644 index 0000000..3d5d1af --- /dev/null +++ b/PoweredSoft.ObjectStorage.MongoDB.Tests/CrudTests.cs @@ -0,0 +1,37 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using PoweredSoft.ObjectStorage.MongoDB.Tests.Mock; +using PoweredSoft.ObjectStorage.MongoDB.Tests.Mock.Dal; +using Xunit; + +namespace PoweredSoft.ObjectStorage.MongoDB.Tests +{ + public class CrudTests + { + [Fact] + public async Task CreateUpdateThenDelete() + { + var osc = MongoDatabaseFactory.GetObjectStorageClient(); + var collection = osc.GetCollection(); + var contact = await collection.AddAsync(new Contact + { + FirstName = "Chuck", + LastName = "Not Norris" + }); + Assert.NotNull(contact); + + + contact.LastName = "Norris"; + var updatedContact = await collection.UpdateAsync(contact); + Assert.Equal("Norris", updatedContact.LastName); + + // delete the test. + await collection.DeleteAsync(updatedContact); + + // check that it was deleted. + var shouldBeNull = collection.AsQueryable().FirstOrDefault(t => t.Id == updatedContact.Id); + Assert.Null(shouldBeNull); + } + } +} diff --git a/PoweredSoft.ObjectStorage.MongoDB.Tests/Mock/Dal/Contact.cs b/PoweredSoft.ObjectStorage.MongoDB.Tests/Mock/Dal/Contact.cs new file mode 100644 index 0000000..9a7e17b --- /dev/null +++ b/PoweredSoft.ObjectStorage.MongoDB.Tests/Mock/Dal/Contact.cs @@ -0,0 +1,17 @@ +using System; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; + +namespace PoweredSoft.ObjectStorage.MongoDB.Tests.Mock.Dal +{ + [MongoCollection("contacts")] + public class Contact + { + [BsonId, BsonElement("id")] + public ObjectId Id { get; set; } + [BsonElement("firstName")] + public string FirstName { get; set; } + [BsonElement("lastName")] + public string LastName { get; set; } + } +} diff --git a/PoweredSoft.ObjectStorage.MongoDB.Tests/Mock/Dal/Tag.cs b/PoweredSoft.ObjectStorage.MongoDB.Tests/Mock/Dal/Tag.cs new file mode 100644 index 0000000..62681a2 --- /dev/null +++ b/PoweredSoft.ObjectStorage.MongoDB.Tests/Mock/Dal/Tag.cs @@ -0,0 +1,14 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; + +namespace PoweredSoft.ObjectStorage.MongoDB.Tests.Mock.Dal +{ + [MongoCollection("tags")] + public class Tag + { + [BsonId, BsonElement("id")] + public ObjectId Id { get; set; } + [BsonElement("name")] + public string Name { get; set; } + } +} diff --git a/PoweredSoft.ObjectStorage.MongoDB.Tests/Mock/MongoDatabaseFactory.cs b/PoweredSoft.ObjectStorage.MongoDB.Tests/Mock/MongoDatabaseFactory.cs new file mode 100644 index 0000000..662e119 --- /dev/null +++ b/PoweredSoft.ObjectStorage.MongoDB.Tests/Mock/MongoDatabaseFactory.cs @@ -0,0 +1,26 @@ +using System; +using MongoDB.Driver; + +namespace PoweredSoft.ObjectStorage.MongoDB.Tests.Mock +{ + public class MongoDatabaseFactory + { + public static IMongoDatabase GetDatabase() + { + var client = GetClient(); + var db = client.GetDatabase("acme"); + return db; + } + + public static MongoObjectStorageClient GetObjectStorageClient() + { + return new MongoObjectStorageClient(GetDatabase()); + } + + public static IMongoClient GetClient() + { + var mongoClient = new MongoClient(); + return mongoClient; + } + } +} diff --git a/PoweredSoft.ObjectStorage.MongoDB.Tests/PoweredSoft.ObjectStorage.MongoDB.Tests.csproj b/PoweredSoft.ObjectStorage.MongoDB.Tests/PoweredSoft.ObjectStorage.MongoDB.Tests.csproj new file mode 100644 index 0000000..ed6f419 --- /dev/null +++ b/PoweredSoft.ObjectStorage.MongoDB.Tests/PoweredSoft.ObjectStorage.MongoDB.Tests.csproj @@ -0,0 +1,24 @@ + + + + netcoreapp2.2 + + false + + + + + + + + + + + + + + + + + + diff --git a/PoweredSoft.ObjectStorage.MongoDB/MongoCollectionAttribute.cs b/PoweredSoft.ObjectStorage.MongoDB/MongoCollectionAttribute.cs new file mode 100644 index 0000000..9459652 --- /dev/null +++ b/PoweredSoft.ObjectStorage.MongoDB/MongoCollectionAttribute.cs @@ -0,0 +1,14 @@ +using System; +namespace PoweredSoft.ObjectStorage.MongoDB +{ + [AttributeUsage(AttributeTargets.Class)] + public class MongoCollectionAttribute : Attribute + { + public MongoCollectionAttribute(string name) + { + Name = name; + } + + public string Name { get; } + } +} diff --git a/PoweredSoft.ObjectStorage.MongoDB/MongoObjectStorageClient.cs b/PoweredSoft.ObjectStorage.MongoDB/MongoObjectStorageClient.cs new file mode 100644 index 0000000..61b5307 --- /dev/null +++ b/PoweredSoft.ObjectStorage.MongoDB/MongoObjectStorageClient.cs @@ -0,0 +1,28 @@ +using System; +using System.Reflection; +using MongoDB.Driver; +using PoweredSoft.ObjectStorage.Core; + +namespace PoweredSoft.ObjectStorage.MongoDB +{ + public class MongoObjectStorageClient : IObjectStorageClient + { + public MongoObjectStorageClient(IMongoDatabase database) + { + Database = database; + } + + public IMongoDatabase Database { get; } + + public IObjectStorageCollection GetCollection() + { + var attribute = typeof(TEntity).GetCustomAttribute(); + if (attribute == null) + throw new Exception("Must add MongoCollectionAttribute on entity class to use this method."); + + var mongoCollection = Database.GetCollection(attribute.Name); + var ret = new MongoObjectStorageCollection(mongoCollection); + return ret; + } + } +} diff --git a/PoweredSoft.ObjectStorage.MongoDB/MongoObjectStorageCollection.cs b/PoweredSoft.ObjectStorage.MongoDB/MongoObjectStorageCollection.cs new file mode 100644 index 0000000..5828e93 --- /dev/null +++ b/PoweredSoft.ObjectStorage.MongoDB/MongoObjectStorageCollection.cs @@ -0,0 +1,67 @@ +using System; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using PoweredSoft.DynamicLinq.Helpers; +using PoweredSoft.ObjectStorage.Core; + +namespace PoweredSoft.ObjectStorage.MongoDB +{ + public class MongoObjectStorageCollection : IObjectStorageCollection + { + public MongoObjectStorageCollection(IMongoCollection collection) + { + if (collection == null) + throw new ArgumentNullException("collection"); + + Collection = collection; + } + + public string CollectionName => Collection.CollectionNamespace.CollectionName; + + public IMongoCollection Collection { get; } + + public async Task AddAsync(TEntity entity, CancellationToken cancellationToken = default(CancellationToken)) + { + await Collection.InsertOneAsync(entity, cancellationToken: cancellationToken); + return entity; + } + + public IQueryable AsQueryable() + { + return Collection.AsQueryable(); + } + + protected virtual Expression> CreateEntityExpression(TEntity entity) + { + var objectKey = typeof(TEntity) + .GetProperties(BindingFlags.Public | BindingFlags.Instance) + .FirstOrDefault(t => t.GetCustomAttribute() != null); + if (objectKey == null) + throw new Exception("Must have a BsonIdAttribute on one of the properties."); + + var constant = objectKey.GetValue(entity); + var expression = QueryableHelpers.CreateConditionExpression(objectKey.Name, + DynamicLinq.ConditionOperators.Equal, constant, DynamicLinq.QueryConvertStrategy.LeaveAsIs); + + return expression; + } + + public async Task DeleteAsync(TEntity entity, CancellationToken cancellationToken = default(CancellationToken)) + { + var expression = CreateEntityExpression(entity); + await Collection.DeleteOneAsync(expression); + } + + public async Task UpdateAsync(TEntity entity, CancellationToken cancellationToken = default(CancellationToken)) + { + var expression = CreateEntityExpression(entity); + await Collection.ReplaceOneAsync(expression, entity); + return entity; + } + } +} diff --git a/PoweredSoft.ObjectStorage.MongoDB/PoweredSoft.ObjectStorage.MongoDB.csproj b/PoweredSoft.ObjectStorage.MongoDB/PoweredSoft.ObjectStorage.MongoDB.csproj new file mode 100644 index 0000000..60b3595 --- /dev/null +++ b/PoweredSoft.ObjectStorage.MongoDB/PoweredSoft.ObjectStorage.MongoDB.csproj @@ -0,0 +1,12 @@ + + + netstandard2.0 + + + + + + + + +