From cfb92da9c80ac135b19a2fc2f73d37a59e7001bc Mon Sep 17 00:00:00 2001 From: TieDyeGeek Date: Thu, 23 Jul 2020 11:35:55 -0500 Subject: [PATCH] Version 2 updates (#2) * Now works with smaller BaseEntity objects (from Core version 2.0) so your tables no longer have to have all the properties from BaseEntity * Expanded ReadOnlyRepository to accept Query or Expression based filtering for almost all functions * Queries that returned IEnumerable<> now return List<> * Create, Update, and Delete operations can now accept List * Delete can now accept an Expression to delete one or many entities --- ...ory.EntityFrameworkCore.TestProject.csproj | 12 +- .../CreateTests.cs | 143 +++++++++++++++ .../DeleteTests.cs | 132 ++++++++++++++ .../ReadOnlyRepositoryTests.cs | 70 ++++++-- .../RepositoryTests.cs | 128 ------------- .../Setup/BaseTest.cs | 18 ++ .../Setup/Crust.cs | 4 +- .../UpdateTests.cs | 170 ++++++++++++++++++ ...ware.Repository.EntityFrameworkCore.csproj | 12 +- .../ReadOnlyRepository.cs | 60 ++++--- .../Repository.cs | 54 +++++- 11 files changed, 619 insertions(+), 184 deletions(-) create mode 100644 src/CSESoftware.Repository.EntityFrameworkCore.TestProject/CreateTests.cs create mode 100644 src/CSESoftware.Repository.EntityFrameworkCore.TestProject/DeleteTests.cs delete mode 100644 src/CSESoftware.Repository.EntityFrameworkCore.TestProject/RepositoryTests.cs create mode 100644 src/CSESoftware.Repository.EntityFrameworkCore.TestProject/Setup/BaseTest.cs create mode 100644 src/CSESoftware.Repository.EntityFrameworkCore.TestProject/UpdateTests.cs diff --git a/src/CSESoftware.Repository.EntityFrameworkCore.TestProject/CSESoftware.Repository.EntityFrameworkCore.TestProject.csproj b/src/CSESoftware.Repository.EntityFrameworkCore.TestProject/CSESoftware.Repository.EntityFrameworkCore.TestProject.csproj index 4f4a8a3..8c35d02 100644 --- a/src/CSESoftware.Repository.EntityFrameworkCore.TestProject/CSESoftware.Repository.EntityFrameworkCore.TestProject.csproj +++ b/src/CSESoftware.Repository.EntityFrameworkCore.TestProject/CSESoftware.Repository.EntityFrameworkCore.TestProject.csproj @@ -7,12 +7,12 @@ - - - - - - + + + + + + diff --git a/src/CSESoftware.Repository.EntityFrameworkCore.TestProject/CreateTests.cs b/src/CSESoftware.Repository.EntityFrameworkCore.TestProject/CreateTests.cs new file mode 100644 index 0000000..7964a92 --- /dev/null +++ b/src/CSESoftware.Repository.EntityFrameworkCore.TestProject/CreateTests.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using CSESoftware.Repository.EntityFrameworkCore.TestProject.Setup; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace CSESoftware.Repository.EntityFrameworkCore.TestProject +{ + [TestClass] + public class CreateTests : BaseTest + { + [TestMethod] + public async Task CreateOneTest() + { + var options = GetOptions(); + var createRepository = GetRepository(options); + + createRepository.Create(new Topping + { + Name = "Canadian Bacon", + AdditionalCost = 2.5, + IsActive = false + }); + await createRepository.SaveAsync(); + + var readRepository = GetRepository(options); + var result = (await readRepository.GetAllAsync()).ToList(); + + Assert.AreEqual(1, result.Count); + Assert.AreEqual("Canadian Bacon", result.FirstOrDefault()?.Name); + Assert.AreEqual(2.5, result.FirstOrDefault()?.AdditionalCost); + Assert.IsTrue(result.FirstOrDefault()?.IsActive ?? false); + Assert.IsTrue(result.FirstOrDefault()?.CreatedDate > DateTime.UtcNow.AddSeconds(-3)); + Assert.IsTrue(result.FirstOrDefault()?.ModifiedDate > DateTime.UtcNow.AddSeconds(-3)); + Assert.IsTrue(result.First().IsActive); + } + + [TestMethod] + public async Task CreateOneNoActiveOrDateChangeTest() + { + var options = GetOptions(); + var createRepository = GetRepository(options); + + createRepository.Create(new Crust + { + Name = "Pan", + AdditionalCost = 2.5, + }); + await createRepository.SaveAsync(); + + var readRepository = GetRepository(options); + var result = (await readRepository.GetAllAsync()).ToList(); + + Assert.AreEqual(1, result.Count); + Assert.AreEqual("Pan", result.FirstOrDefault()?.Name); + Assert.AreEqual(2.5, result.FirstOrDefault()?.AdditionalCost); + } + + [TestMethod] + public async Task CreateManyTest() + { + var options = GetOptions(); + + var topping1 = new Topping + { + Name = "Sausage", + AdditionalCost = 0.5, + IsActive = false + }; + var topping2 = new Topping + { + Name = "Bacon", + AdditionalCost = 0.75, + IsActive = false + }; + var topping3 = new Topping + { + Name = "Canadian Bacon", + AdditionalCost = 0.75 + }; + var topping4 = new Topping + { + Name = "Olives", + AdditionalCost = 1.25 + }; + + var toppings = new List {topping1, topping2, topping3, topping4}; + + var createRepository = GetRepository(options); + createRepository.Create(toppings); + await createRepository.SaveAsync(); + + var readRepository = GetRepository(options); + var result = (await readRepository.GetAllAsync()).ToList(); + + Assert.AreEqual(4, result.Count); + Assert.AreEqual(2, result.Count(x => Math.Abs(x.AdditionalCost - 0.75) < 0.001)); + Assert.IsFalse(result.Any(x => !x.IsActive)); + Assert.IsFalse(result.Any(x => x.CreatedDate < DateTime.UtcNow.AddSeconds(-3))); + } + + [TestMethod] + public async Task CreateManyNoActiveOrDateChangeTest() + { + var options = GetOptions(); + + var crust1 = new Crust + { + Name = "Thin", + AdditionalCost = 0.5 + }; + var crust2 = new Crust + { + Name = "Crispy", + AdditionalCost = 0.75 + + }; + var crust3 = new Crust + { + Name = "Thin & Crispy", + AdditionalCost = 0.75 + }; + var crust4 = new Crust + { + Name = "Pan", + AdditionalCost = 1.25 + }; + + var crusts = new List {crust1, crust2, crust3, crust4}; + + var createRepository = GetRepository(options); + createRepository.Create(crusts); + await createRepository.SaveAsync(); + + var readRepository = GetRepository(options); + var result = (await readRepository.GetAllAsync()).ToList(); + + Assert.AreEqual(4, result.Count); + Assert.AreEqual(2, result.Count(x => Math.Abs(x.AdditionalCost - 0.75) < 0.001)); + } + } +} diff --git a/src/CSESoftware.Repository.EntityFrameworkCore.TestProject/DeleteTests.cs b/src/CSESoftware.Repository.EntityFrameworkCore.TestProject/DeleteTests.cs new file mode 100644 index 0000000..fc85109 --- /dev/null +++ b/src/CSESoftware.Repository.EntityFrameworkCore.TestProject/DeleteTests.cs @@ -0,0 +1,132 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using CSESoftware.Repository.EntityFrameworkCore.TestProject.Setup; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace CSESoftware.Repository.EntityFrameworkCore.TestProject +{ + [TestClass] + public class DeleteTests : BaseTest + { + [TestMethod] + public async Task DeleteByEntityTest() + { + var options = GetOptions(); + var repository = GetRepository(options); + + repository.Create(new Topping + { + Name = "Canadian Bacon", + AdditionalCost = 2.5 + }); + await repository.SaveAsync(); + + + var deleteRepository = GetRepository(options); + deleteRepository.Delete(await deleteRepository.GetFirstAsync()); + await deleteRepository.SaveAsync(); + + + var readRepository = GetRepository(options); + Assert.AreEqual(false, await readRepository.GetExistsAsync(x => x.Id == 1)); + Assert.AreEqual(0, await readRepository.GetCountAsync()); + } + + [TestMethod] + public async Task DeleteByIdTest() + { + var options = GetOptions(); + var createRepository = GetRepository(options); + + createRepository.Create(new Topping + { + Name = "Canadian Bacon", + AdditionalCost = 2.5 + }); + await createRepository.SaveAsync(); + + + var deleteRepository = GetRepository(options); + deleteRepository.Delete(1); + await deleteRepository.SaveAsync(); + + + var readRepository = GetRepository(options); + Assert.AreEqual(false, await readRepository.GetExistsAsync(x => x.Id == 1)); + Assert.AreEqual(0, await readRepository.GetCountAsync()); + } + + [TestMethod] + public async Task DeleteByListTest() + { + var options = GetOptions(); + + var topping1 = new Topping + { + Name = "Canadian Bacon", + AdditionalCost = 2.5 + }; + var topping2 = new Topping + { + Name = "Bacon", + AdditionalCost = 1 + }; + var topping3 = new Topping + { + Name = "Sausage", + AdditionalCost = 2.25 + }; + + var createRepository = GetRepository(options); + createRepository.Create(new List{topping1, topping2, topping3}); + await createRepository.SaveAsync(); + + + var deleteRepository = GetRepository(options); + var toppingsToDelete = await deleteRepository.GetAllAsync(x => x.AdditionalCost > 2); + deleteRepository.Delete(toppingsToDelete); + await deleteRepository.SaveAsync(); + + + var readRepository = GetRepository(options); + Assert.AreEqual(topping2.Id, (await readRepository.GetFirstAsync()).Id); + Assert.AreEqual(1, await readRepository.GetCountAsync()); + } + + [TestMethod] + public async Task DeleteByExpressionTest() + { + var options = GetOptions(); + + var topping1 = new Topping + { + Name = "Canadian Bacon", + AdditionalCost = 2.5 + }; + var topping2 = new Topping + { + Name = "Bacon", + AdditionalCost = 1 + }; + var topping3 = new Topping + { + Name = "Sausage", + AdditionalCost = 2.25 + }; + + var createRepository = GetRepository(options); + createRepository.Create(new List{topping1, topping2, topping3}); + await createRepository.SaveAsync(); + + + var deleteRepository = GetRepository(options); + deleteRepository.Delete(x => x.AdditionalCost > 2); + await deleteRepository.SaveAsync(); + + + var readRepository = GetRepository(options); + Assert.AreEqual(topping2.Id, (await readRepository.GetFirstAsync()).Id); + Assert.AreEqual(1, await readRepository.GetCountAsync()); + } + } +} diff --git a/src/CSESoftware.Repository.EntityFrameworkCore.TestProject/ReadOnlyRepositoryTests.cs b/src/CSESoftware.Repository.EntityFrameworkCore.TestProject/ReadOnlyRepositoryTests.cs index 9361a17..049a54c 100644 --- a/src/CSESoftware.Repository.EntityFrameworkCore.TestProject/ReadOnlyRepositoryTests.cs +++ b/src/CSESoftware.Repository.EntityFrameworkCore.TestProject/ReadOnlyRepositoryTests.cs @@ -49,19 +49,19 @@ public class ReadOnlyRepositoryTests { Id = 1, Name = "Pan Crust", - AdditionalCharge = 1, + AdditionalCost = 1, }, new Crust { Id = 2, Name = "Thin Crust", - AdditionalCharge = 0, + AdditionalCost = 0, }, new Crust { Id = 3, Name = "Regular Crust", - AdditionalCharge = 0, + AdditionalCost = 0, } }; @@ -134,17 +134,17 @@ public async Task GetAllAsyncTest() var allToppings = await repository.GetAllAsync(); - Assert.AreEqual(4, allToppings.Count()); + Assert.AreEqual(4, allToppings.Count); var extraCostToppings = await repository.GetAllAsync(x => x.AdditionalCost > 0); - Assert.AreEqual(1, extraCostToppings.Count()); + Assert.AreEqual(1, extraCostToppings.Count); // Where var freeToppings = new QueryBuilder().Where(x => x.AdditionalCost.Equals(0)).Build(); var inactiveToppings = await repository.GetAllAsync(freeToppings); - Assert.AreEqual(3, inactiveToppings.Count()); + Assert.AreEqual(3, inactiveToppings.Count); // OrderBy @@ -187,27 +187,53 @@ public async Task GetAllWithSelectAsyncTest() await AddDefaultMenuItems(options); - var toppings = await repository.GetAllWithSelectAsync( + var toppings = await repository.GetAllWithSelectAsync( new QueryBuilder().Select(x => x.Name).Build()); - var firstTopping = (string) toppings.FirstOrDefault(); + var firstTopping = toppings.FirstOrDefault(); Assert.AreEqual("Bacon", firstTopping); } [TestMethod] - public async Task GetFirstAsyncTest() + public async Task GetFirstAsyncQueryTest() { var options = GetOptions(); var repository = GetReadOnlyRepository(options); await AddDefaultMenuItems(options); - var result = await repository.GetFirstAsync(); + var result = await repository.GetFirstAsync(new QueryBuilder().Where(x => x.CrustId == 3).Build()); - Assert.AreEqual("Pan Sausage", result.Name); + Assert.AreEqual("Hawaiian", result.Name); } [TestMethod] - public async Task GetCountAsyncTest() + public async Task GetFirstAsyncFilterTest() + { + var options = GetOptions(); + var repository = GetReadOnlyRepository(options); + await AddDefaultMenuItems(options); + + var result = await repository.GetFirstAsync(x => x.CrustId == 3); + + Assert.AreEqual("Hawaiian", result.Name); + } + + [TestMethod] + public async Task GetCountAsyncQueryTest() + { + var options = GetOptions(); + var repository = GetReadOnlyRepository(options); + await AddDefaultMenuItems(options); + + var result1 = await repository.GetCountAsync(new QueryBuilder().Where(x => x.Id == 1).Build()); + var result2 = await repository.GetCountAsync(new QueryBuilder().Where(x => x.AdditionalCost > 0).Build()); + + Assert.AreEqual(1, result1); + Assert.AreEqual(1, result2); + } + + [TestMethod] + public async Task GetCountAsyncFilterTest() { var options = GetOptions(); var repository = GetReadOnlyRepository(options); @@ -223,7 +249,23 @@ public async Task GetCountAsyncTest() } [TestMethod] - public async Task GetExistsAsyncTest() + public async Task GetExistsAsyncQueryTest() + { + var options = GetOptions(); + var repository = GetReadOnlyRepository(options); + await AddDefaultMenuItems(options); + + var result1 = await repository.GetExistsAsync(new QueryBuilder().Where(x => x.Name.Equals("Bacon")).Build()); + var result2 = await repository.GetExistsAsync(new QueryBuilder().Where(x => x.Id > 2).Build()); + var result3 = await repository.GetExistsAsync(new QueryBuilder().Where(x => x.AdditionalCost < -1).Build()); + + Assert.IsTrue(result1); + Assert.IsTrue(result2); + Assert.IsFalse(result3); + } + + [TestMethod] + public async Task GetExistsAsyncFilterTest() { var options = GetOptions(); var repository = GetReadOnlyRepository(options); @@ -238,4 +280,4 @@ public async Task GetExistsAsyncTest() Assert.IsFalse(result3); } } -} \ No newline at end of file +} diff --git a/src/CSESoftware.Repository.EntityFrameworkCore.TestProject/RepositoryTests.cs b/src/CSESoftware.Repository.EntityFrameworkCore.TestProject/RepositoryTests.cs deleted file mode 100644 index 9a553c7..0000000 --- a/src/CSESoftware.Repository.EntityFrameworkCore.TestProject/RepositoryTests.cs +++ /dev/null @@ -1,128 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using CSESoftware.Repository.EntityFrameworkCore.TestProject.Setup; -using Microsoft.EntityFrameworkCore; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using DbContext = CSESoftware.Repository.EntityFrameworkCore.TestProject.Setup.DbContext; - -namespace CSESoftware.Repository.EntityFrameworkCore.TestProject -{ - [TestClass] - public class RepositoryTests - { - #region Repository Setup - - private static DbContextOptions GetOptions() - { - return new DbContextOptionsBuilder().UseInMemoryDatabase(Guid.NewGuid().ToString()).Options; - } - - private static Repository GetRepository(DbContextOptions options) - { - return new Repository(new DbContext(options)); - } - - #endregion - - [TestMethod] - public async Task CreateTest() - { - var options = GetOptions(); - var repository = GetRepository(options); - - repository.Create(new Topping - { - Name = "Canadian Bacon", - AdditionalCost = 2.5 - }); - await repository.SaveAsync(); - - var repository2 = GetRepository(options); - var result = (await repository2.GetAllAsync()).ToList(); - - Assert.AreEqual(1, result.Count); - Assert.AreEqual("Canadian Bacon", result.FirstOrDefault()?.Name); - Assert.AreEqual(2.5, result.FirstOrDefault()?.AdditionalCost); - Assert.IsTrue(result.FirstOrDefault()?.CreatedDate > DateTime.UtcNow.AddSeconds(-3)); - Assert.IsTrue(result.FirstOrDefault()?.ModifiedDate > DateTime.UtcNow.AddSeconds(-3)); - Assert.IsTrue(result.First().IsActive); - } - - [TestMethod] - public async Task UpdateTest() - { - var options = GetOptions(); - var repository = GetRepository(options); - - repository.Create(new Topping - { - Name = "Canadian Bacon", - AdditionalCost = 2.5 - }); - await repository.SaveAsync(); - - - var repository2 = GetRepository(options); - var topping = await repository2.GetFirstAsync(); - topping.Name = "Super Canadian Bacon"; - repository2.Update(topping); - await repository2.SaveAsync(); - - - var repository3 = GetRepository(options); - var updatedTopping = await repository3.GetFirstAsync(); - - Assert.AreEqual("Super Canadian Bacon", updatedTopping.Name); - Assert.AreEqual(2.5, updatedTopping.AdditionalCost); - } - - [TestMethod] - public async Task DeleteByEntityTest() - { - var options = GetOptions(); - var repository = GetRepository(options); - - repository.Create(new Topping - { - Name = "Canadian Bacon", - AdditionalCost = 2.5 - }); - await repository.SaveAsync(); - - - var repository2 = GetRepository(options); - repository2.Delete(await repository2.GetFirstAsync()); - await repository2.SaveAsync(); - - - var repository3 = GetRepository(options); - Assert.AreEqual(false, await repository3.GetExistsAsync(x => x.Id == 1)); - Assert.AreEqual(0, await repository3.GetCountAsync()); - } - - [TestMethod] - public async Task DeleteByIdTest() - { - var options = GetOptions(); - var repository = GetRepository(options); - - repository.Create(new Topping - { - Name = "Canadian Bacon", - AdditionalCost = 2.5 - }); - await repository.SaveAsync(); - - - var repository2 = GetRepository(options); - repository2.Delete(1); - await repository2.SaveAsync(); - - - var repository3 = GetRepository(options); - Assert.AreEqual(false, await repository3.GetExistsAsync(x => x.Id == 1)); - Assert.AreEqual(0, await repository3.GetCountAsync()); - } - } -} \ No newline at end of file diff --git a/src/CSESoftware.Repository.EntityFrameworkCore.TestProject/Setup/BaseTest.cs b/src/CSESoftware.Repository.EntityFrameworkCore.TestProject/Setup/BaseTest.cs new file mode 100644 index 0000000..e1fd12a --- /dev/null +++ b/src/CSESoftware.Repository.EntityFrameworkCore.TestProject/Setup/BaseTest.cs @@ -0,0 +1,18 @@ +using System; +using Microsoft.EntityFrameworkCore; + +namespace CSESoftware.Repository.EntityFrameworkCore.TestProject.Setup +{ + public class BaseTest + { + public static DbContextOptions GetOptions() + { + return new DbContextOptionsBuilder().UseInMemoryDatabase(Guid.NewGuid().ToString()).Options; + } + + public static Repository GetRepository(DbContextOptions options) + { + return new Repository(new DbContext(options)); + } + } +} diff --git a/src/CSESoftware.Repository.EntityFrameworkCore.TestProject/Setup/Crust.cs b/src/CSESoftware.Repository.EntityFrameworkCore.TestProject/Setup/Crust.cs index ebe2c7f..5add213 100644 --- a/src/CSESoftware.Repository.EntityFrameworkCore.TestProject/Setup/Crust.cs +++ b/src/CSESoftware.Repository.EntityFrameworkCore.TestProject/Setup/Crust.cs @@ -2,9 +2,9 @@ namespace CSESoftware.Repository.EntityFrameworkCore.TestProject.Setup { - public class Crust : BaseEntity + public class Crust : EntityWithId { public string Name { get; set; } - public double AdditionalCharge { get; set; } + public double AdditionalCost { get; set; } } } \ No newline at end of file diff --git a/src/CSESoftware.Repository.EntityFrameworkCore.TestProject/UpdateTests.cs b/src/CSESoftware.Repository.EntityFrameworkCore.TestProject/UpdateTests.cs new file mode 100644 index 0000000..9a93298 --- /dev/null +++ b/src/CSESoftware.Repository.EntityFrameworkCore.TestProject/UpdateTests.cs @@ -0,0 +1,170 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using CSESoftware.Repository.EntityFrameworkCore.TestProject.Setup; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace CSESoftware.Repository.EntityFrameworkCore.TestProject +{ + [TestClass] + public class UpdateTests : BaseTest + { + [TestMethod] + public async Task UpdateTest() + { + var options = GetOptions(); + var createRepository = GetRepository(options); + + var initialTopping = new Topping + { + Name = "Canadian Bacon", + AdditionalCost = 2.5 + }; + createRepository.Create(initialTopping); + await createRepository.SaveAsync(); + + + var updateRepository = GetRepository(options); + var topping = await updateRepository.GetFirstAsync(); + topping.Name = "Super Canadian Bacon"; + updateRepository.Update(topping); + await updateRepository.SaveAsync(); + + + var readRepository = GetRepository(options); + var updatedTopping = await readRepository.GetFirstAsync(); + + Assert.AreEqual("Super Canadian Bacon", updatedTopping.Name); + Assert.AreEqual(2.5, updatedTopping.AdditionalCost); + Assert.AreNotEqual(initialTopping.ModifiedDate, updatedTopping.ModifiedDate); + } + + [TestMethod] + public async Task UpdateNoActiveOrDateChangeTest() + { + var options = GetOptions(); + var createRepository = GetRepository(options); + + var initialCrust = new Crust + { + Name = "Thin", + AdditionalCost = 2.5 + }; + createRepository.Create(initialCrust); + await createRepository.SaveAsync(); + + + var updateRepository = GetRepository(options); + var crust = await updateRepository.GetFirstAsync(); + crust.Name = "Super Thin"; + updateRepository.Update(crust); + await updateRepository.SaveAsync(); + + + var readRepository = GetRepository(options); + var updatedCrust = await readRepository.GetFirstAsync(); + + Assert.AreEqual("Super Thin", updatedCrust.Name); + Assert.AreEqual(2.5, updatedCrust.AdditionalCost); + } + + [TestMethod] + public async Task UpdateManyTest() + { + var options = GetOptions(); + + var topping1 = new Topping + { + Name = "Sausage", + AdditionalCost = 0.5 + }; + var topping2 = new Topping + { + Name = "Bacon", + AdditionalCost = 0.75 + }; + var topping3 = new Topping + { + Name = "Canadian Bacon", + AdditionalCost = 0.75 + }; + var topping4 = new Topping + { + Name = "Olives", + AdditionalCost = 1.25 + }; + + var toppings = new List { topping1, topping2, topping3, topping4 }; + + var createRepository = GetRepository(options); + createRepository.Create(toppings); + await createRepository.SaveAsync(); + + + var updateRepository = GetRepository(options); + var updateToppings = await updateRepository.GetAllAsync(); + updateToppings.ForEach(x => x.Name = "Tuna"); + updateRepository.Update(updateToppings); + await updateRepository.SaveAsync(); + + + var readRepository = GetRepository(options); + var result = (await readRepository.GetAllAsync()).ToList(); + + Assert.AreEqual(4, result.Count); + Assert.AreEqual(2, result.Count(x => Math.Abs(x.AdditionalCost - 0.75) < 0.001)); + Assert.IsTrue(result.All(x => x.Name.Equals("Tuna"))); + Assert.IsFalse(result.First(x => Math.Abs(x.AdditionalCost - 0.5) < 0.001).ModifiedDate.Equals(topping1.ModifiedDate)); + } + + [TestMethod] + public async Task UpdateManyNoActiveOrDateChangeTest() + { + var options = GetOptions(); + + var crust1 = new Crust + { + Name = "Thin", + AdditionalCost = 0.5 + }; + var crust2 = new Crust + { + Name = "Crispy", + AdditionalCost = 0.75 + + }; + var crust3 = new Crust + { + Name = "Thin & Crispy", + AdditionalCost = 0.75 + }; + var crust4 = new Crust + { + Name = "Pan", + AdditionalCost = 1.25 + }; + + var crusts = new List { crust1, crust2, crust3, crust4 }; + + var createRepository = GetRepository(options); + createRepository.Create(crusts); + await createRepository.SaveAsync(); + + + var updateRepository = GetRepository(options); + var updateCrust = await updateRepository.GetAllAsync(); + updateCrust.ForEach(x => x.AdditionalCost = 0); + updateRepository.Update(updateCrust); + await updateRepository.SaveAsync(); + + + var readRepository = GetRepository(options); + var result = (await readRepository.GetAllAsync()).ToList(); + + Assert.AreEqual(4, result.Count); + Assert.AreEqual(4, result.Count(x => Math.Abs(x.AdditionalCost) < 0.001)); + Assert.AreEqual(1, result.Count(x => x.Name.Equals("Pan"))); + } + } +} diff --git a/src/CSESoftware.Repository.EntityFrameworkCore/CSESoftware.Repository.EntityFrameworkCore.csproj b/src/CSESoftware.Repository.EntityFrameworkCore/CSESoftware.Repository.EntityFrameworkCore.csproj index 079d384..5b8118c 100644 --- a/src/CSESoftware.Repository.EntityFrameworkCore/CSESoftware.Repository.EntityFrameworkCore.csproj +++ b/src/CSESoftware.Repository.EntityFrameworkCore/CSESoftware.Repository.EntityFrameworkCore.csproj @@ -6,7 +6,7 @@ CSESoftware.Repository.EntityFrameworkCore CSE Software, Inc. 2020 CSE Software, Inc. - 1.0.1 + 2.0.0 packageIcon.png The Entity Framework Core implementation of CSESoftware.Repository. @@ -18,11 +18,11 @@ - - - - - + + + + + diff --git a/src/CSESoftware.Repository.EntityFrameworkCore/ReadOnlyRepository.cs b/src/CSESoftware.Repository.EntityFrameworkCore/ReadOnlyRepository.cs index a982c56..a263979 100644 --- a/src/CSESoftware.Repository.EntityFrameworkCore/ReadOnlyRepository.cs +++ b/src/CSESoftware.Repository.EntityFrameworkCore/ReadOnlyRepository.cs @@ -20,7 +20,7 @@ public ReadOnlyRepository(TContext context) } protected IQueryable GetQueryable(IQuery filter) - where TEntity : class, IBaseEntity + where TEntity : class, IEntity { if (filter == null) filter = new Query { @@ -47,7 +47,7 @@ protected IQueryable GetQueryable(IQuery filter) } protected IQueryable GetQueryableSelect(IQuery filter = null) - where TEntity : class, IBaseEntity + where TEntity : class, IEntity { var query = GetQueryable(filter); @@ -56,45 +56,65 @@ protected IQueryable GetQueryableSelect(IQuery filter return query; } - - public virtual async Task> GetAllAsync(IQuery filter = null) - where TEntity : class, IBaseEntity + public virtual async Task> GetAllAsync(IQuery filter) + where TEntity : class, IEntity { return await GetQueryable(filter).ToListAsync(); } - public virtual async Task> GetAllAsync(Expression> filter) - where TEntity : class, IBaseEntity - { - return await GetQueryable(new QueryBuilder().Where(filter).Build()).ToListAsync(); - } - - public virtual async Task> GetAllWithSelectAsync(IQuery filter = null) - where TEntity : class, IBaseEntity + public virtual async Task> GetAllAsync(Expression> filter = null) + where TEntity : class, IEntity { - return await GetQueryableSelect(filter).ToListAsync(); + return await GetAllAsync(new QueryBuilder().Where(filter).Build()); } - public virtual async Task GetFirstAsync(IQuery filter = null) - where TEntity : class, IBaseEntity + public virtual async Task GetFirstAsync(IQuery filter) + where TEntity : class, IEntity { return await GetQueryable(new QueryBuilder() .Where(filter?.Predicate) .Include(filter?.Include) .OrderBy(filter?.OrderBy) - .Build()).FirstOrDefaultAsync(); + .Build()) + .FirstOrDefaultAsync(); + } + + public virtual async Task GetFirstAsync(Expression> filter = null) + where TEntity : class, IEntity + { + return await GetFirstAsync(new QueryBuilder() + .Where(filter) + .Build()); + } + + public virtual Task GetCountAsync(IQuery filter) + where TEntity : class, IEntity + { + return GetQueryable(filter).CountAsync(); } public virtual Task GetCountAsync(Expression> filter = null) - where TEntity : class, IBaseEntity + where TEntity : class, IEntity + { + return GetCountAsync(new QueryBuilder().Where(filter).Build()); + } + + public virtual Task GetExistsAsync(IQuery filter) + where TEntity : class, IEntity { - return GetQueryable(new QueryBuilder().Where(filter).Build()).CountAsync(); + return GetQueryable(filter).AnyAsync(); } public virtual Task GetExistsAsync(Expression> filter = null) - where TEntity : class, IBaseEntity + where TEntity : class, IEntity { return GetQueryable(new QueryBuilder().Where(filter).Build()).AnyAsync(); } + + public virtual async Task> GetAllWithSelectAsync(IQuery filter = null) + where TEntity : class, IEntity + { + return await GetQueryableSelect(filter).Select(x => (TOut)x).ToListAsync(); + } } } \ No newline at end of file diff --git a/src/CSESoftware.Repository.EntityFrameworkCore/Repository.cs b/src/CSESoftware.Repository.EntityFrameworkCore/Repository.cs index 7e2d463..bf08b5a 100644 --- a/src/CSESoftware.Repository.EntityFrameworkCore/Repository.cs +++ b/src/CSESoftware.Repository.EntityFrameworkCore/Repository.cs @@ -1,4 +1,7 @@ using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; using System.Threading.Tasks; using CSESoftware.Core.Entity; using Microsoft.EntityFrameworkCore; @@ -12,30 +15,50 @@ public Repository(TContext context) : base(context) } public virtual void Create(TEntity entity) - where TEntity : class, IBaseEntity + where TEntity : class, IEntity { - entity.IsActive = true; - entity.CreatedDate = DateTime.UtcNow; + entity.CreateSetup(); Context.Set().Add(entity); } + public virtual void Create(List entities) + where TEntity : class, IEntity + { + foreach (var entity in entities) + entity.CreateSetup(); + + Context.Set().AddRange(entities); + } + public virtual void Update(TEntity entity) - where TEntity : class, IBaseEntity + where TEntity : class, IEntity { - entity.ModifiedDate = DateTime.UtcNow; + entity.UpdateSetup(); Context.Set().Attach(entity); Context.Entry(entity).State = EntityState.Modified; } - public virtual void Delete(object id) - where TEntity : class, IBaseEntity + public virtual void Update(List entities) + where TEntity : class, IEntity + { + Context.Set().AttachRange(entities); + + foreach (var entity in entities) + { + entity.UpdateSetup(); + Context.Entry(entity).State = EntityState.Modified; + } + } + + public virtual void Delete(T id) + where TEntity : class, IEntityWithId { var entity = Context.Set().Find(id); Delete(entity); } public virtual void Delete(TEntity entity) - where TEntity : class, IBaseEntity + where TEntity : class, IEntity { var dbSet = Context.Set(); if (Context.Entry(entity).State == EntityState.Detached) @@ -45,6 +68,21 @@ public virtual void Delete(TEntity entity) dbSet.Remove(entity); } + public virtual void Delete(List entities) + where TEntity : class, IEntity + { + Context.Set().AttachRange( + entities.Where(x => Context.Entry(x).State == EntityState.Detached)); + + Context.Set().RemoveRange(entities); + } + + public virtual void Delete(Expression> filter) + where TEntity : class, IEntity + { + Context.Set().RemoveRange(Context.Set().Where(filter)); + } + public virtual Task SaveAsync() { return Context.SaveChangesAsync();