From 4973e1efe4a4ce1d1a1b20cff9c582512584415c Mon Sep 17 00:00:00 2001 From: Kaisinel Date: Thu, 19 Sep 2019 20:19:38 +0300 Subject: [PATCH 1/8] UI tests project dependency changed to .NET Core 3.0 --- tests/CSInn.Application.Tests/CSInn.Application.Tests.csproj | 4 ++-- tests/CSInn.UI.Tests/CSInn.UI.Tests.csproj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/CSInn.Application.Tests/CSInn.Application.Tests.csproj b/tests/CSInn.Application.Tests/CSInn.Application.Tests.csproj index ff862c8..af89a5d 100644 --- a/tests/CSInn.Application.Tests/CSInn.Application.Tests.csproj +++ b/tests/CSInn.Application.Tests/CSInn.Application.Tests.csproj @@ -1,7 +1,7 @@ - + - netcoreapp2.1 + netcoreapp2.2 false diff --git a/tests/CSInn.UI.Tests/CSInn.UI.Tests.csproj b/tests/CSInn.UI.Tests/CSInn.UI.Tests.csproj index 7bd7edf..5d79b5b 100644 --- a/tests/CSInn.UI.Tests/CSInn.UI.Tests.csproj +++ b/tests/CSInn.UI.Tests/CSInn.UI.Tests.csproj @@ -1,7 +1,7 @@ - + - netcoreapp2.1 + netcoreapp3.0 false From 8f824084d149f6d0f4f2e495396b5781ee181c21 Mon Sep 17 00:00:00 2001 From: Kaisinel Date: Sun, 29 Sep 2019 09:06:49 +0300 Subject: [PATCH 2/8] Lesson Search: Filtering Specification pattern to have filters coming from domain; Visitor pattern to visit expression based filters specificially for EF; Combo filters using expression trees; Lesson has internal id visible to infrastructure domain only; LessonEntity tags and authors changed to string to ease storing in database collection; CSInnDbContext with initial table- lessons. --- .../Extensions/IEnumerableExtensions.cs | 15 ++++ .../Extensions/SpecificationExtensions.cs | 25 ++++++ .../ILessonsRepository.cs | 4 +- .../Repositories/ILessonsRepository.cs | 15 ++++ .../Repositories/IRepository.cs | 18 +++++ .../Specifications/Base/AndSpecification.cs | 22 ++++++ .../Specifications/Base/ISpecification.cs | 12 +++ .../Base/ISpecificationVisitor.cs | 9 +++ .../Specifications/Base/NotSpecification.cs | 16 ++++ .../Specifications/Base/OrSpecification.cs | 17 ++++ .../Specifications/Lesson/AuthorLike.cs | 25 ++++++ .../Lesson/ILessonSpecificationVisitor.cs | 14 ++++ .../Specifications/Lesson/TagMatches.cs | 21 +++++ .../Specifications/Lesson/TitleLike.cs | 23 ++++++ .../CSInn.Infrastructure.Repositories.csproj | 13 +++- .../CSInnDbContext.cs | 19 +++++ .../Entities/LessonEntity.cs | 16 ++++ .../Expressions/ExpressionVisitor.cs | 43 +++++++++++ .../Expressions/LessonExpressionVisitor.cs | 24 ++++++ .../Extensions/LessonExtensions.cs | 46 +++++++++++ .../Repositories/LessonsRepository.cs | 77 +++++++++++++++++++ src/CSInn.Models/Lesson.cs | 15 ++-- .../CSInn.Application.Tests.csproj | 11 ++- .../CSInn.Discord.Authentication.Tests.csproj | 11 ++- ...n.Infrastructure.Repositories.Tests.csproj | 14 ++-- .../Input/LessonsFixture.cs | 24 ++++++ .../Input/UoWFixture.cs | 37 +++++++++ .../LessonSpecificationTests.cs | 27 +++++++ 28 files changed, 590 insertions(+), 23 deletions(-) create mode 100644 src/CSInn.Domain.Repositories/Extensions/IEnumerableExtensions.cs create mode 100644 src/CSInn.Domain.Repositories/Extensions/SpecificationExtensions.cs create mode 100644 src/CSInn.Domain.Repositories/Repositories/ILessonsRepository.cs create mode 100644 src/CSInn.Domain.Repositories/Repositories/IRepository.cs create mode 100644 src/CSInn.Domain.Repositories/Specifications/Base/AndSpecification.cs create mode 100644 src/CSInn.Domain.Repositories/Specifications/Base/ISpecification.cs create mode 100644 src/CSInn.Domain.Repositories/Specifications/Base/ISpecificationVisitor.cs create mode 100644 src/CSInn.Domain.Repositories/Specifications/Base/NotSpecification.cs create mode 100644 src/CSInn.Domain.Repositories/Specifications/Base/OrSpecification.cs create mode 100644 src/CSInn.Domain.Repositories/Specifications/Lesson/AuthorLike.cs create mode 100644 src/CSInn.Domain.Repositories/Specifications/Lesson/ILessonSpecificationVisitor.cs create mode 100644 src/CSInn.Domain.Repositories/Specifications/Lesson/TagMatches.cs create mode 100644 src/CSInn.Domain.Repositories/Specifications/Lesson/TitleLike.cs create mode 100644 src/CSInn.Infrastructure.Repositories/CSInnDbContext.cs create mode 100644 src/CSInn.Infrastructure.Repositories/Entities/LessonEntity.cs create mode 100644 src/CSInn.Infrastructure.Repositories/Expressions/ExpressionVisitor.cs create mode 100644 src/CSInn.Infrastructure.Repositories/Expressions/LessonExpressionVisitor.cs create mode 100644 src/CSInn.Infrastructure.Repositories/Extensions/LessonExtensions.cs create mode 100644 src/CSInn.Infrastructure.Repositories/Repositories/LessonsRepository.cs create mode 100644 tests/CSInn.Infrastructure.Repositories.Tests/Input/LessonsFixture.cs create mode 100644 tests/CSInn.Infrastructure.Repositories.Tests/Input/UoWFixture.cs create mode 100644 tests/CSInn.Infrastructure.Repositories.Tests/LessonSpecificationTests.cs diff --git a/src/CSInn.Domain.Repositories/Extensions/IEnumerableExtensions.cs b/src/CSInn.Domain.Repositories/Extensions/IEnumerableExtensions.cs new file mode 100644 index 0000000..725bc7b --- /dev/null +++ b/src/CSInn.Domain.Repositories/Extensions/IEnumerableExtensions.cs @@ -0,0 +1,15 @@ +//using System.Linq; +//using CSInn.Domain.Repositories.Specifications.Base; + +//namespace CSInn.Domain.Repositories.Extensions +//{ +// public static class EnumerableExtensions +// { +// public static IQueryable Where(this IQueryable entities, Specification specification) +// => entities.Where(specification.ToExpression()); + +// public static T FirstOrDefault(this IQueryable entities, Specification specification) +// => entities.FirstOrDefault(specification.ToExpression()); + +// } +//} diff --git a/src/CSInn.Domain.Repositories/Extensions/SpecificationExtensions.cs b/src/CSInn.Domain.Repositories/Extensions/SpecificationExtensions.cs new file mode 100644 index 0000000..89172d2 --- /dev/null +++ b/src/CSInn.Domain.Repositories/Extensions/SpecificationExtensions.cs @@ -0,0 +1,25 @@ +using CSInn.Domain.Repositories.Specifications.Base; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CSInn.Domain.Repositories.Extensions +{ + public static class SpecificationExtensions + { + public static ISpecification And(this ISpecification left, ISpecification right) where TVisitor : ISpecificationVisitor + { + return new AndSpecification(left, right); + } + + public static ISpecification Or(this ISpecification left, ISpecification right) where TVisitor : ISpecificationVisitor + { + return new OrSpecification(left, right); + } + + public static ISpecification Not(this ISpecification specification) where TVisitor : ISpecificationVisitor + { + return new NotSpecification(specification); + } + } +} diff --git a/src/CSInn.Domain.Repositories/ILessonsRepository.cs b/src/CSInn.Domain.Repositories/ILessonsRepository.cs index 874bdc2..ef380e4 100644 --- a/src/CSInn.Domain.Repositories/ILessonsRepository.cs +++ b/src/CSInn.Domain.Repositories/ILessonsRepository.cs @@ -1,6 +1,6 @@ -using CSInn.Domain.Models.Content; -using System; +using System; using System.Collections.Generic; +using CSInn.Models; namespace CSInn.Domain.Repositories { diff --git a/src/CSInn.Domain.Repositories/Repositories/ILessonsRepository.cs b/src/CSInn.Domain.Repositories/Repositories/ILessonsRepository.cs new file mode 100644 index 0000000..a330cc0 --- /dev/null +++ b/src/CSInn.Domain.Repositories/Repositories/ILessonsRepository.cs @@ -0,0 +1,15 @@ +using CSInn.Domain.Repositories.Specifications.Lesson; +using CSInn.Models; + +namespace CSInn.Domain.Repositories.Repositories +{ + public interface ILessonsRepository : IRepository + { + //// TODO: Specification pattern so we can mix criterias. + //IEnumerable GetByTags(params string[] tags); + //IEnumerable GetByName(string name); + //IEnumerable GetByAuthors(params string[] authors); + //IEnumerable GetByDate(DateTime from, DateTime to); + //IEnumerable Get(Specification specification); + } +} diff --git a/src/CSInn.Domain.Repositories/Repositories/IRepository.cs b/src/CSInn.Domain.Repositories/Repositories/IRepository.cs new file mode 100644 index 0000000..1a3a707 --- /dev/null +++ b/src/CSInn.Domain.Repositories/Repositories/IRepository.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using CSInn.Domain.Repositories.Specifications.Base; + +namespace CSInn.Domain.Repositories.Repositories +{ + public interface IRepository + where TModel : class + where TVisitor : ISpecificationVisitor + { + void Create(TModel model); + void Update(TModel model); + void Delete(int key); + IEnumerable Get(); + TModel Get(int id); + IEnumerable Get(ISpecification specification); + TModel Find(ISpecification specification); + } +} diff --git a/src/CSInn.Domain.Repositories/Specifications/Base/AndSpecification.cs b/src/CSInn.Domain.Repositories/Specifications/Base/AndSpecification.cs new file mode 100644 index 0000000..5492bc9 --- /dev/null +++ b/src/CSInn.Domain.Repositories/Specifications/Base/AndSpecification.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CSInn.Domain.Repositories.Specifications.Base +{ + public class AndSpecification : ISpecification + where TVisitor : ISpecificationVisitor + { + public ISpecification Left { get; } + public ISpecification Right { get; } + + public AndSpecification(ISpecification left, ISpecification right) + { + this.Left = left; + this.Right = right; + } + + public void Accept(TVisitor visitor) => visitor.Visit(this); + public bool IsSatisfiedBy(T obj) => Left.IsSatisfiedBy(obj) && Right.IsSatisfiedBy(obj); + } +} diff --git a/src/CSInn.Domain.Repositories/Specifications/Base/ISpecification.cs b/src/CSInn.Domain.Repositories/Specifications/Base/ISpecification.cs new file mode 100644 index 0000000..a54a694 --- /dev/null +++ b/src/CSInn.Domain.Repositories/Specifications/Base/ISpecification.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace CSInn.Domain.Repositories.Specifications.Base +{ + public interface ISpecification where TVisitor : ISpecificationVisitor + { + bool IsSatisfiedBy(T item); + void Accept(TVisitor visitor); + } +} diff --git a/src/CSInn.Domain.Repositories/Specifications/Base/ISpecificationVisitor.cs b/src/CSInn.Domain.Repositories/Specifications/Base/ISpecificationVisitor.cs new file mode 100644 index 0000000..3193e3c --- /dev/null +++ b/src/CSInn.Domain.Repositories/Specifications/Base/ISpecificationVisitor.cs @@ -0,0 +1,9 @@ +namespace CSInn.Domain.Repositories.Specifications.Base +{ + public interface ISpecificationVisitor where TVisitor : ISpecificationVisitor + { + void Visit(AndSpecification spec); + void Visit(OrSpecification spec); + void Visit(NotSpecification spec); + } +} \ No newline at end of file diff --git a/src/CSInn.Domain.Repositories/Specifications/Base/NotSpecification.cs b/src/CSInn.Domain.Repositories/Specifications/Base/NotSpecification.cs new file mode 100644 index 0000000..3029fbe --- /dev/null +++ b/src/CSInn.Domain.Repositories/Specifications/Base/NotSpecification.cs @@ -0,0 +1,16 @@ +namespace CSInn.Domain.Repositories.Specifications.Base +{ + public class NotSpecification : ISpecification + where TVisitor : ISpecificationVisitor + { + public ISpecification Specification { get; } + + public NotSpecification(ISpecification specification) + { + Specification = specification; + } + + public void Accept(TVisitor visitor) => visitor.Visit(this); + public bool IsSatisfiedBy(T item) => !Specification.IsSatisfiedBy(item); + } +} \ No newline at end of file diff --git a/src/CSInn.Domain.Repositories/Specifications/Base/OrSpecification.cs b/src/CSInn.Domain.Repositories/Specifications/Base/OrSpecification.cs new file mode 100644 index 0000000..cf626fe --- /dev/null +++ b/src/CSInn.Domain.Repositories/Specifications/Base/OrSpecification.cs @@ -0,0 +1,17 @@ +namespace CSInn.Domain.Repositories.Specifications.Base +{ + public class OrSpecification : ISpecification + where TVisitor : ISpecificationVisitor + { + public ISpecification Left { get;} + public ISpecification Right { get;} + public OrSpecification(ISpecification left, ISpecification right) + { + Left = left; + Right = right; + } + + public void Accept(TVisitor visitor) => visitor.Visit(this); + public bool IsSatisfiedBy(T item) => Left.IsSatisfiedBy(item) || Right.IsSatisfiedBy(item); + } +} \ No newline at end of file diff --git a/src/CSInn.Domain.Repositories/Specifications/Lesson/AuthorLike.cs b/src/CSInn.Domain.Repositories/Specifications/Lesson/AuthorLike.cs new file mode 100644 index 0000000..a6694c1 --- /dev/null +++ b/src/CSInn.Domain.Repositories/Specifications/Lesson/AuthorLike.cs @@ -0,0 +1,25 @@ +using System; +using System.Linq; +using System.Linq.Expressions; +using CSInn.Domain.Repositories.Specifications.Base; +using CSInn.Models; + +namespace CSInn.Domain.Repositories.Specifications.Lesson +{ + public class AuthorLike : ISpecification + { + public string Author { get; } + + public AuthorLike(string author) + { + Author = author; + } + + public void Accept(ILessonSpecificationVisitor visitor) + { + visitor.Visit(this); + } + + public bool IsSatisfiedBy(CSInn.Models.Lesson lesson) => lesson.Authors.Any(a => a.Contains(Author)); + } +} diff --git a/src/CSInn.Domain.Repositories/Specifications/Lesson/ILessonSpecificationVisitor.cs b/src/CSInn.Domain.Repositories/Specifications/Lesson/ILessonSpecificationVisitor.cs new file mode 100644 index 0000000..36aadd7 --- /dev/null +++ b/src/CSInn.Domain.Repositories/Specifications/Lesson/ILessonSpecificationVisitor.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; +using CSInn.Domain.Repositories.Specifications.Base; + +namespace CSInn.Domain.Repositories.Specifications.Lesson +{ + public interface ILessonSpecificationVisitor: ISpecificationVisitor + { + void Visit(TitleLike spec); + void Visit(TagMatches spec); + void Visit(AuthorLike authorLike); + } +} diff --git a/src/CSInn.Domain.Repositories/Specifications/Lesson/TagMatches.cs b/src/CSInn.Domain.Repositories/Specifications/Lesson/TagMatches.cs new file mode 100644 index 0000000..5eee667 --- /dev/null +++ b/src/CSInn.Domain.Repositories/Specifications/Lesson/TagMatches.cs @@ -0,0 +1,21 @@ +using CSInn.Domain.Repositories.Specifications.Base; + +namespace CSInn.Domain.Repositories.Specifications.Lesson +{ + public class TagMatches : ISpecification + { + public string Tag { get; } + + public TagMatches(string tag) + { + Tag = tag; + } + + public void Accept(ILessonSpecificationVisitor visitor) + { + visitor.Visit(this); + } + + public bool IsSatisfiedBy(CSInn.Models.Lesson lesson) => lesson.Tags.Contains(Tag); + } +} \ No newline at end of file diff --git a/src/CSInn.Domain.Repositories/Specifications/Lesson/TitleLike.cs b/src/CSInn.Domain.Repositories/Specifications/Lesson/TitleLike.cs new file mode 100644 index 0000000..9075efb --- /dev/null +++ b/src/CSInn.Domain.Repositories/Specifications/Lesson/TitleLike.cs @@ -0,0 +1,23 @@ +using System; +using System.Linq.Expressions; +using CSInn.Domain.Repositories.Specifications.Base; + +namespace CSInn.Domain.Repositories.Specifications.Lesson +{ + public class TitleLike : ISpecification + { + public string Title { get; } + + public TitleLike(string title) + { + Title = title; + } + + public bool IsSatisfiedBy(CSInn.Models.Lesson lesson) => lesson.Title.Contains(Title); + + public void Accept(ILessonSpecificationVisitor visitor) + { + visitor.Visit(this); + } + } +} diff --git a/src/CSInn.Infrastructure.Repositories/CSInn.Infrastructure.Repositories.csproj b/src/CSInn.Infrastructure.Repositories/CSInn.Infrastructure.Repositories.csproj index 11f86a1..771d47f 100644 --- a/src/CSInn.Infrastructure.Repositories/CSInn.Infrastructure.Repositories.csproj +++ b/src/CSInn.Infrastructure.Repositories/CSInn.Infrastructure.Repositories.csproj @@ -1,11 +1,22 @@  - netcoreapp2.2 + netcoreapp3.0 + + + + + + + + + + + diff --git a/src/CSInn.Infrastructure.Repositories/CSInnDbContext.cs b/src/CSInn.Infrastructure.Repositories/CSInnDbContext.cs new file mode 100644 index 0000000..ce51265 --- /dev/null +++ b/src/CSInn.Infrastructure.Repositories/CSInnDbContext.cs @@ -0,0 +1,19 @@ +using CSInn.Infrastructure.Repositories.Entities; +using Microsoft.EntityFrameworkCore; + +namespace CSInn.Infrastructure.Repositories +{ + public class CSInnDbContext : DbContext + { + public CSInnDbContext() + { + } + + public CSInnDbContext(DbContextOptions options) + : base(options) + { + } + + public virtual DbSet Lessons { get; set; } + } +} diff --git a/src/CSInn.Infrastructure.Repositories/Entities/LessonEntity.cs b/src/CSInn.Infrastructure.Repositories/Entities/LessonEntity.cs new file mode 100644 index 0000000..c9233b4 --- /dev/null +++ b/src/CSInn.Infrastructure.Repositories/Entities/LessonEntity.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace CSInn.Infrastructure.Repositories.Entities +{ + public class LessonEntity + { + public int Id { get; set; } + public string Title { get; set; } + public string Description { get; set; } + public string Video { get; set; } + public string Slides { get; set; } + // EF doesn't like a list- fair point, but how do we solve this? + public string Tags { get; set; } + public string Authors { get; set; } + } +} diff --git a/src/CSInn.Infrastructure.Repositories/Expressions/ExpressionVisitor.cs b/src/CSInn.Infrastructure.Repositories/Expressions/ExpressionVisitor.cs new file mode 100644 index 0000000..5a8f2f1 --- /dev/null +++ b/src/CSInn.Infrastructure.Repositories/Expressions/ExpressionVisitor.cs @@ -0,0 +1,43 @@ +using System; +using System.Linq; +using System.Linq.Expressions; +using CSInn.Domain.Repositories.Specifications.Base; + +// TODO: References should be internals visible to this one. +namespace CSInn.Infrastructure.Repositories.Expressions +{ + public abstract class ExpressionVisitor + where TVisitor : ISpecificationVisitor + { + public Expression> Expression { get; protected set; } + + public abstract Expression> ConvertSpecToExpression(ISpecification spec); + + public void Visit(AndSpecification spec) + { + var leftExpr = ConvertSpecToExpression(spec.Left); + var rightExpr = ConvertSpecToExpression(spec.Right); + + var exprBody = System.Linq.Expressions.Expression.AndAlso(leftExpr.Body, rightExpr.Body); + Expression = System.Linq.Expressions.Expression.Lambda>(exprBody, leftExpr.Parameters.Single()); + } + + public void Visit(OrSpecification spec) + { + var leftExpr = ConvertSpecToExpression(spec.Left); + var rightExpr = ConvertSpecToExpression(spec.Right); + + var exprBody = System.Linq.Expressions.Expression.Or(leftExpr.Body, rightExpr.Body); + Expression = System.Linq.Expressions.Expression.Lambda>(exprBody, leftExpr.Parameters.Single()); + } + + public void Visit(NotSpecification spec) + { + var specExpr = ConvertSpecToExpression(spec.Specification); + + var exprBody = System.Linq.Expressions.Expression.Not(specExpr.Body); + Expression = System.Linq.Expressions.Expression.Lambda>(exprBody, specExpr.Parameters.Single()); + } + } +} + diff --git a/src/CSInn.Infrastructure.Repositories/Expressions/LessonExpressionVisitor.cs b/src/CSInn.Infrastructure.Repositories/Expressions/LessonExpressionVisitor.cs new file mode 100644 index 0000000..72eb07b --- /dev/null +++ b/src/CSInn.Infrastructure.Repositories/Expressions/LessonExpressionVisitor.cs @@ -0,0 +1,24 @@ +using System; +using System.Linq; +using System.Linq.Expressions; +using CSInn.Domain.Repositories.Specifications.Base; +using CSInn.Domain.Repositories.Specifications.Lesson; +using CSInn.Infrastructure.Repositories.Entities; +using CSInn.Models; + +namespace CSInn.Infrastructure.Repositories.Expressions +{ + class LessonEntityExpressionVisitor : ExpressionVisitor, ILessonSpecificationVisitor + { + public override Expression> ConvertSpecToExpression(ISpecification spec) + { + var visitor = new LessonEntityExpressionVisitor(); + spec.Accept(visitor); + return visitor.Expression; + } + + public void Visit(TitleLike spec) => Expression = e => e.Title.Contains(spec.Title); + public void Visit(TagMatches spec) => Expression = e => e.Tags.Contains(spec.Tag); + public void Visit(AuthorLike authorLike) => Expression = e => e.Authors.Contains(authorLike.Author); + } +} diff --git a/src/CSInn.Infrastructure.Repositories/Extensions/LessonExtensions.cs b/src/CSInn.Infrastructure.Repositories/Extensions/LessonExtensions.cs new file mode 100644 index 0000000..0abf873 --- /dev/null +++ b/src/CSInn.Infrastructure.Repositories/Extensions/LessonExtensions.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using System.Linq; +using CSInn.Infrastructure.Repositories.Entities; +using CSInn.Models; +using Microsoft.EntityFrameworkCore; + +namespace CSInn.Infrastructure.Repositories.Extensions +{ + public static class LessonExtensions + { + public static Lesson ToModel(this LessonEntity entity) + { + // call to automapper + var lesson = new Lesson(entity.Title, entity.Description, entity.Tags.Split(","), entity.Authors.Split(",")) + { + Id = entity.Id, + }; + + return lesson; + } + + public static IEnumerable ToModels(this IEnumerable entities) + { + // call to automapper + var experiments = entities.Select(e => e.ToModel()); + + return experiments; + } + + public static LessonEntity ToEntity(this Lesson entity) + { + // call to automapper + var experiment = new LessonEntity() + { + Id = entity.Id, + Title = entity.Title, + Authors = string.Join(',', entity.Authors), + Description = entity.Description, + Slides = entity.Slides, + Tags = string.Join(',',entity.Tags) + }; + + return experiment; + } + } +} diff --git a/src/CSInn.Infrastructure.Repositories/Repositories/LessonsRepository.cs b/src/CSInn.Infrastructure.Repositories/Repositories/LessonsRepository.cs new file mode 100644 index 0000000..b73b0c6 --- /dev/null +++ b/src/CSInn.Infrastructure.Repositories/Repositories/LessonsRepository.cs @@ -0,0 +1,77 @@ +using System.Collections.Generic; +using System.Linq; +using CSInn.Domain.Repositories.Repositories; +using CSInn.Domain.Repositories.Specifications.Base; +using CSInn.Domain.Repositories.Specifications.Lesson; +using CSInn.Infrastructure.Repositories.Entities; +using CSInn.Infrastructure.Repositories.Expressions; +using CSInn.Infrastructure.Repositories.Extensions; +using CSInn.Models; +using Microsoft.EntityFrameworkCore; + +namespace CSInn.Infrastructure.Repositories.Repositories +{ + namespace CSInn.Experimental.EF.Repositories + { + public class LessonsRepository: ILessonsRepository + { + private readonly CSInnDbContext _context; + private DbSet dbSet => _context.Lessons; + + public LessonsRepository(CSInnDbContext context) + { + _context = context; + } + + public IEnumerable Get() => dbSet.ToModels(); + public IEnumerable Get(string title) => dbSet.Where(e => e.Title == title).Select(e => e.ToModel()); + public void Create(Lesson model) => dbSet.Add(model.ToEntity()); + + public void Update(Lesson model) + { + var entity = dbSet.Find(model.Id); + + if (entity == null) + { + return; + } + + _context.Entry(entity).CurrentValues.SetValues(model); + } + + public void Delete(Lesson model) + { + var entity = dbSet.Find(model.Id); + dbSet.Remove(entity); + } + + public void Delete(int key) + { + throw new System.NotImplementedException(); + } + + public Lesson Get(int id) + { + throw new System.NotImplementedException(); + } + + public Lesson Find(ISpecification specification) + { + var visitor = new LessonEntityExpressionVisitor(); + specification.Accept(visitor); + var expression = visitor.Expression; + + return dbSet.FirstOrDefault(expression).ToModel(); + } + + public IEnumerable Get(ISpecification specification) + { + var visitor = new LessonEntityExpressionVisitor(); + specification.Accept(visitor); + var expression = visitor.Expression; + + return dbSet.Where(expression.Compile()).AsEnumerable().ToModels(); + } + } + } +} diff --git a/src/CSInn.Models/Lesson.cs b/src/CSInn.Models/Lesson.cs index 6f5bea6..889deaa 100644 --- a/src/CSInn.Models/Lesson.cs +++ b/src/CSInn.Models/Lesson.cs @@ -1,19 +1,20 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; -using System.Text; +using System.Runtime.CompilerServices; using CSInn.Domain.Models.Content.Exceptions; -namespace CSInn.Domain.Models.Content +[assembly: InternalsVisibleTo("CSInn.Infrastructure.Repositories")] +namespace CSInn.Models { public class Lesson { - public string Title { get; } + internal int Id { get; set; } + public string Title { get; set; } public string Description { get; set; } public string Video { get; set; } public string Slides { get; set; } public IList Tags { get; set; } - public IList Authors { get; } + public IList Authors { get; set; } public Lesson(string title, string description, IEnumerable tags, IEnumerable authors) { @@ -25,7 +26,7 @@ public Lesson(string title, string description, IEnumerable tags, IEnume var isTitleEmpty = string.IsNullOrEmpty(title); var isDescriptionEmpty = string.IsNullOrEmpty(description); var areNoTags = Tags == null || !Tags.Any(); - var areNoAuthors = Authors == null || Authors.Any(); + var areNoAuthors = Authors == null || !Authors.Any(); if (isTitleEmpty || isDescriptionEmpty || areNoTags || areNoAuthors) { diff --git a/tests/CSInn.Application.Tests/CSInn.Application.Tests.csproj b/tests/CSInn.Application.Tests/CSInn.Application.Tests.csproj index af89a5d..8938f3d 100644 --- a/tests/CSInn.Application.Tests/CSInn.Application.Tests.csproj +++ b/tests/CSInn.Application.Tests/CSInn.Application.Tests.csproj @@ -7,10 +7,13 @@ - - - - + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/tests/CSInn.Discord.Authentication.Tests/CSInn.Discord.Authentication.Tests.csproj b/tests/CSInn.Discord.Authentication.Tests/CSInn.Discord.Authentication.Tests.csproj index 9116020..1bf18b5 100644 --- a/tests/CSInn.Discord.Authentication.Tests/CSInn.Discord.Authentication.Tests.csproj +++ b/tests/CSInn.Discord.Authentication.Tests/CSInn.Discord.Authentication.Tests.csproj @@ -7,10 +7,13 @@ - - - - + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/tests/CSInn.Infrastructure.Repositories.Tests/CSInn.Infrastructure.Repositories.Tests.csproj b/tests/CSInn.Infrastructure.Repositories.Tests/CSInn.Infrastructure.Repositories.Tests.csproj index 7bb3c40..1315505 100644 --- a/tests/CSInn.Infrastructure.Repositories.Tests/CSInn.Infrastructure.Repositories.Tests.csproj +++ b/tests/CSInn.Infrastructure.Repositories.Tests/CSInn.Infrastructure.Repositories.Tests.csproj @@ -1,15 +1,19 @@ - + - netcoreapp2.2 + netcoreapp3.0 false - - - + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/tests/CSInn.Infrastructure.Repositories.Tests/Input/LessonsFixture.cs b/tests/CSInn.Infrastructure.Repositories.Tests/Input/LessonsFixture.cs new file mode 100644 index 0000000..5a99c45 --- /dev/null +++ b/tests/CSInn.Infrastructure.Repositories.Tests/Input/LessonsFixture.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using System.Linq; +using CSInn.Models; + +namespace CSInn.Infrastructure.Repositories.Tests.Input +{ + public class LessonsFixture + { + public readonly IQueryable Lessons; + + public LessonsFixture() + { + var lessons = new[] + { + new Lesson("Lesson 1: Class", "What is a class, object", new List() {"OOP"}, + new List() {"Almantas Karpavičius"}), + new Lesson("Lesson 2: SOLID", "SOLID", new List() {"OOP"}, new List() {"Kaisinel"}), + new Lesson("Lesson 3: Delegates", "Delegate vs callback", new List() {"FP"}, + new List() {"Lethern"}) + }; + Lessons = new EnumerableQuery(lessons); + } + } +} diff --git a/tests/CSInn.Infrastructure.Repositories.Tests/Input/UoWFixture.cs b/tests/CSInn.Infrastructure.Repositories.Tests/Input/UoWFixture.cs new file mode 100644 index 0000000..a84bfcb --- /dev/null +++ b/tests/CSInn.Infrastructure.Repositories.Tests/Input/UoWFixture.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Text; +using CSInn.Infrastructure.Repositories.Extensions; +using CSInn.Infrastructure.Repositories.Repositories.CSInn.Experimental.EF.Repositories; +using CSInn.Models; +using Microsoft.EntityFrameworkCore; + +namespace CSInn.Infrastructure.Repositories.Tests.Input +{ + public class LessonsRepositoryFixture + { + public LessonsRepository Repo { get; set; } + public LessonsRepositoryFixture() + { + var options = new DbContextOptionsBuilder() + .UseInMemoryDatabase(databaseName: "database_name") + .Options; + + var context = new CSInnDbContext(options); + + context.Lessons.Add( + new Lesson("Lesson 1: Class", "What is a class, object", new List() { "OOP" }, + new List() { "Almantas Karpavičius" }).ToEntity()); + + context.Lessons.Add(new Lesson("Lesson 2: SOLID", "SOLID", new List() { "OOP" }, + new List() { "Kaisinel" }).ToEntity()); + + context.Lessons.Add( + new Lesson("Lesson 3: Delegates", "Delegate vs callback", new List() { "FP" }, + new List() { "Lethern" }).ToEntity()); + context.SaveChanges(); + + Repo = new LessonsRepository(context); + } + } +} diff --git a/tests/CSInn.Infrastructure.Repositories.Tests/LessonSpecificationTests.cs b/tests/CSInn.Infrastructure.Repositories.Tests/LessonSpecificationTests.cs new file mode 100644 index 0000000..67c598b --- /dev/null +++ b/tests/CSInn.Infrastructure.Repositories.Tests/LessonSpecificationTests.cs @@ -0,0 +1,27 @@ +using CSInn.Domain.Repositories.Extensions; +using CSInn.Domain.Repositories.Repositories; +using CSInn.Domain.Repositories.Specifications.Lesson; +using CSInn.Infrastructure.Repositories.Tests.Input; +using Xunit; + +namespace CSInn.Infrastructure.Repositories.Tests +{ + public class LessonSpecificationTests: IClassFixture + { + private readonly ILessonsRepository _repository; + + public LessonSpecificationTests(LessonsRepositoryFixture fixture) + { + _repository = fixture.Repo; + } + + [Fact] + public void Composite_Specification_Ok() + { + var filter = new TitleLike("Lesson 2"); + + var result = _repository.Get(filter); + Assert.Single(result); + } + } +} From 745172482075462e65d35cecf404187609ecc840 Mon Sep 17 00:00:00 2001 From: Kaisinel Date: Sun, 29 Sep 2019 11:32:43 +0300 Subject: [PATCH 3/8] Fixed composite expression trees Using ExpressionVisitor to unify parameter from both composite expression sides; Expressions-based stuff moved to Repository.Infrastructure; Expressions-based stuff made internal; OrElse, Not composite expressions. --- .../Extensions/IEnumerableExtensions.cs | 15 ------ .../ILessonsRepository.cs | 15 ------ src/CSInn.Domain.Repositories/IRepository.cs | 15 ------ .../Specifications/Base/AndSpecification.cs | 4 +- .../Specifications/Base/ISpecification.cs | 3 ++ .../CSInn.Infrastructure.Repositories.csproj | 1 - .../Expressions/ExpressionExtensions.cs | 49 +++++++++++++++++++ .../Expressions/ExpressionVisitor.cs | 21 ++++---- .../Expressions/LessonExpressionVisitor.cs | 2 +- .../Expressions/ReplaceExpressionVisitor.cs | 22 +++++++++ .../Repositories/LessonsRepository.cs | 3 +- .../LessonSpecificationTests.cs | 12 +++-- 12 files changed, 97 insertions(+), 65 deletions(-) delete mode 100644 src/CSInn.Domain.Repositories/Extensions/IEnumerableExtensions.cs delete mode 100644 src/CSInn.Domain.Repositories/ILessonsRepository.cs delete mode 100644 src/CSInn.Domain.Repositories/IRepository.cs create mode 100644 src/CSInn.Infrastructure.Repositories/Expressions/ExpressionExtensions.cs create mode 100644 src/CSInn.Infrastructure.Repositories/Expressions/ReplaceExpressionVisitor.cs diff --git a/src/CSInn.Domain.Repositories/Extensions/IEnumerableExtensions.cs b/src/CSInn.Domain.Repositories/Extensions/IEnumerableExtensions.cs deleted file mode 100644 index 725bc7b..0000000 --- a/src/CSInn.Domain.Repositories/Extensions/IEnumerableExtensions.cs +++ /dev/null @@ -1,15 +0,0 @@ -//using System.Linq; -//using CSInn.Domain.Repositories.Specifications.Base; - -//namespace CSInn.Domain.Repositories.Extensions -//{ -// public static class EnumerableExtensions -// { -// public static IQueryable Where(this IQueryable entities, Specification specification) -// => entities.Where(specification.ToExpression()); - -// public static T FirstOrDefault(this IQueryable entities, Specification specification) -// => entities.FirstOrDefault(specification.ToExpression()); - -// } -//} diff --git a/src/CSInn.Domain.Repositories/ILessonsRepository.cs b/src/CSInn.Domain.Repositories/ILessonsRepository.cs deleted file mode 100644 index ef380e4..0000000 --- a/src/CSInn.Domain.Repositories/ILessonsRepository.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using CSInn.Models; - -namespace CSInn.Domain.Repositories -{ - public interface ILessonsRepository : IRepository - { - // TODO: Specification pattern so we can mix criterias. - IEnumerable GetByTags(params string[] tags); - IEnumerable GetByName(string name); - IEnumerable GetByAuthors(params string[] authors); - IEnumerable GetByDate(DateTime from, DateTime to); - } -} diff --git a/src/CSInn.Domain.Repositories/IRepository.cs b/src/CSInn.Domain.Repositories/IRepository.cs deleted file mode 100644 index f4f6192..0000000 --- a/src/CSInn.Domain.Repositories/IRepository.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace CSInn.Domain.Repositories -{ - public interface IRepository where TModel : class - { - void Create(TModel model); - void Update(TModel model); - void Delete(int key); - IEnumerable Get(); - TModel Get(int id); - } -} diff --git a/src/CSInn.Domain.Repositories/Specifications/Base/AndSpecification.cs b/src/CSInn.Domain.Repositories/Specifications/Base/AndSpecification.cs index 5492bc9..8b40e05 100644 --- a/src/CSInn.Domain.Repositories/Specifications/Base/AndSpecification.cs +++ b/src/CSInn.Domain.Repositories/Specifications/Base/AndSpecification.cs @@ -12,8 +12,8 @@ public class AndSpecification : ISpecification public AndSpecification(ISpecification left, ISpecification right) { - this.Left = left; - this.Right = right; + Left = left; + Right = right; } public void Accept(TVisitor visitor) => visitor.Visit(this); diff --git a/src/CSInn.Domain.Repositories/Specifications/Base/ISpecification.cs b/src/CSInn.Domain.Repositories/Specifications/Base/ISpecification.cs index a54a694..0360ec1 100644 --- a/src/CSInn.Domain.Repositories/Specifications/Base/ISpecification.cs +++ b/src/CSInn.Domain.Repositories/Specifications/Base/ISpecification.cs @@ -6,6 +6,9 @@ namespace CSInn.Domain.Repositories.Specifications.Base { public interface ISpecification where TVisitor : ISpecificationVisitor { + /// + /// Does the item meet specification? + /// bool IsSatisfiedBy(T item); void Accept(TVisitor visitor); } diff --git a/src/CSInn.Infrastructure.Repositories/CSInn.Infrastructure.Repositories.csproj b/src/CSInn.Infrastructure.Repositories/CSInn.Infrastructure.Repositories.csproj index 771d47f..f72c062 100644 --- a/src/CSInn.Infrastructure.Repositories/CSInn.Infrastructure.Repositories.csproj +++ b/src/CSInn.Infrastructure.Repositories/CSInn.Infrastructure.Repositories.csproj @@ -10,7 +10,6 @@ - diff --git a/src/CSInn.Infrastructure.Repositories/Expressions/ExpressionExtensions.cs b/src/CSInn.Infrastructure.Repositories/Expressions/ExpressionExtensions.cs new file mode 100644 index 0000000..51d29f8 --- /dev/null +++ b/src/CSInn.Infrastructure.Repositories/Expressions/ExpressionExtensions.cs @@ -0,0 +1,49 @@ +using System; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using CSInn.Domain.Repositories.ExpressionVisitors; + +namespace CSInn.Infrastructure.Repositories.Expressions +{ + internal static class ExpressionExtensions + { + public static Expression> AndAlso( + this Expression> expr1, + Expression> expr2) + { + var parameter = Expression.Parameter(typeof(T)); + + var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter); + var left = leftVisitor.Visit(expr1.Body); + + var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter); + var right = rightVisitor.Visit(expr2.Body); + + return Expression.Lambda>( + Expression.AndAlso(left, right), parameter); + } + + public static Expression> OrElse( + this Expression> expr1, + Expression> expr2) + { + var parameter = Expression.Parameter(typeof(T)); + + var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter); + var left = leftVisitor.Visit(expr1.Body); + + var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter); + var right = rightVisitor.Visit(expr2.Body); + + return Expression.Lambda>( + Expression.OrElse(left, right), parameter); + } + + public static Expression> Not( + this Expression> epxr) + { + var exprBody = System.Linq.Expressions.Expression.Not(epxr.Body); + return Expression.Lambda>(exprBody, epxr.Parameters); + } + } +} diff --git a/src/CSInn.Infrastructure.Repositories/Expressions/ExpressionVisitor.cs b/src/CSInn.Infrastructure.Repositories/Expressions/ExpressionVisitor.cs index 5a8f2f1..cabe97a 100644 --- a/src/CSInn.Infrastructure.Repositories/Expressions/ExpressionVisitor.cs +++ b/src/CSInn.Infrastructure.Repositories/Expressions/ExpressionVisitor.cs @@ -1,12 +1,13 @@ using System; using System.Linq; using System.Linq.Expressions; +using CSInn.Domain.Repositories.Extensions; using CSInn.Domain.Repositories.Specifications.Base; // TODO: References should be internals visible to this one. namespace CSInn.Infrastructure.Repositories.Expressions { - public abstract class ExpressionVisitor + internal abstract class ExpressionVisitor where TVisitor : ISpecificationVisitor { public Expression> Expression { get; protected set; } @@ -15,28 +16,24 @@ public abstract class ExpressionVisitor public void Visit(AndSpecification spec) { - var leftExpr = ConvertSpecToExpression(spec.Left); - var rightExpr = ConvertSpecToExpression(spec.Right); + var left = ConvertSpecToExpression(spec.Left); + var right = ConvertSpecToExpression(spec.Right); - var exprBody = System.Linq.Expressions.Expression.AndAlso(leftExpr.Body, rightExpr.Body); - Expression = System.Linq.Expressions.Expression.Lambda>(exprBody, leftExpr.Parameters.Single()); + Expression = left.AndAlso(right); } public void Visit(OrSpecification spec) { - var leftExpr = ConvertSpecToExpression(spec.Left); - var rightExpr = ConvertSpecToExpression(spec.Right); + var left = ConvertSpecToExpression(spec.Left); + var right = ConvertSpecToExpression(spec.Right); - var exprBody = System.Linq.Expressions.Expression.Or(leftExpr.Body, rightExpr.Body); - Expression = System.Linq.Expressions.Expression.Lambda>(exprBody, leftExpr.Parameters.Single()); + Expression = left.OrElse(right); } public void Visit(NotSpecification spec) { var specExpr = ConvertSpecToExpression(spec.Specification); - - var exprBody = System.Linq.Expressions.Expression.Not(specExpr.Body); - Expression = System.Linq.Expressions.Expression.Lambda>(exprBody, specExpr.Parameters.Single()); + Expression = specExpr.Not(); } } } diff --git a/src/CSInn.Infrastructure.Repositories/Expressions/LessonExpressionVisitor.cs b/src/CSInn.Infrastructure.Repositories/Expressions/LessonExpressionVisitor.cs index 72eb07b..a4d0900 100644 --- a/src/CSInn.Infrastructure.Repositories/Expressions/LessonExpressionVisitor.cs +++ b/src/CSInn.Infrastructure.Repositories/Expressions/LessonExpressionVisitor.cs @@ -8,7 +8,7 @@ namespace CSInn.Infrastructure.Repositories.Expressions { - class LessonEntityExpressionVisitor : ExpressionVisitor, ILessonSpecificationVisitor + internal class LessonEntityExpressionVisitor : ExpressionVisitor, ILessonSpecificationVisitor { public override Expression> ConvertSpecToExpression(ISpecification spec) { diff --git a/src/CSInn.Infrastructure.Repositories/Expressions/ReplaceExpressionVisitor.cs b/src/CSInn.Infrastructure.Repositories/Expressions/ReplaceExpressionVisitor.cs new file mode 100644 index 0000000..2f5d6ac --- /dev/null +++ b/src/CSInn.Infrastructure.Repositories/Expressions/ReplaceExpressionVisitor.cs @@ -0,0 +1,22 @@ +using System.Linq.Expressions; + +namespace CSInn.Domain.Repositories.ExpressionVisitors +{ + internal class ReplaceExpressionVisitor + : ExpressionVisitor + { + private readonly Expression _oldValue; + private readonly Expression _newValue; + + public ReplaceExpressionVisitor(Expression oldValue, Expression newValue) + { + _oldValue = oldValue; + _newValue = newValue; + } + + public override Expression Visit(Expression node) + { + return node == _oldValue ? _newValue : base.Visit(node); + } + } +} diff --git a/src/CSInn.Infrastructure.Repositories/Repositories/LessonsRepository.cs b/src/CSInn.Infrastructure.Repositories/Repositories/LessonsRepository.cs index b73b0c6..d24393f 100644 --- a/src/CSInn.Infrastructure.Repositories/Repositories/LessonsRepository.cs +++ b/src/CSInn.Infrastructure.Repositories/Repositories/LessonsRepository.cs @@ -70,7 +70,8 @@ public IEnumerable Get(ISpecification + public class LessonSpecificationTests : IClassFixture { private readonly ILessonsRepository _repository; @@ -18,9 +22,11 @@ public LessonSpecificationTests(LessonsRepositoryFixture fixture) [Fact] public void Composite_Specification_Ok() { - var filter = new TitleLike("Lesson 2"); + var filter = new TitleLike("Lesson 2") + .Or(new TitleLike("Lesson 1")).Not(); var result = _repository.Get(filter); + Assert.Single(result); } } From ca9e667cad4f7507e62536333e4165b1f0a591ca Mon Sep 17 00:00:00 2001 From: Kaisinel Date: Sun, 29 Sep 2019 13:20:03 +0300 Subject: [PATCH 4/8] UnitOfWork & Async support UnitOfWork to encapsulate repositories with lazy loading and shared transaction; Solution file location adjustment; Generic repository interface for async. --- .../Repositories/ILessonsRepository.cs | 3 +- .../Repositories/IRepository.cs | 2 - .../Repositories/IRepositoryAsync.cs | 19 +++++ .../UnitOfWork/CSInnUnitOfWork.cs | 15 ++++ .../Repositories/LessonsRepository.cs | 76 ++++++++++++++----- .../{ => UoW}/CSInnDbContext.cs | 2 +- .../UoW/CSInnUnitOfWork.cs | 66 ++++++++++++++++ .../Input/UoWFixture.cs | 1 + .../LessonSpecificationTests.cs | 12 +++ 9 files changed, 172 insertions(+), 24 deletions(-) create mode 100644 src/CSInn.Domain.Repositories/Repositories/IRepositoryAsync.cs create mode 100644 src/CSInn.Domain.Repositories/UnitOfWork/CSInnUnitOfWork.cs rename src/CSInn.Infrastructure.Repositories/{ => UoW}/CSInnDbContext.cs (88%) create mode 100644 src/CSInn.Infrastructure.Repositories/UoW/CSInnUnitOfWork.cs diff --git a/src/CSInn.Domain.Repositories/Repositories/ILessonsRepository.cs b/src/CSInn.Domain.Repositories/Repositories/ILessonsRepository.cs index a330cc0..3cd8e13 100644 --- a/src/CSInn.Domain.Repositories/Repositories/ILessonsRepository.cs +++ b/src/CSInn.Domain.Repositories/Repositories/ILessonsRepository.cs @@ -3,7 +3,8 @@ namespace CSInn.Domain.Repositories.Repositories { - public interface ILessonsRepository : IRepository + public interface ILessonsRepository : IRepository, + IRepositoryAsync { //// TODO: Specification pattern so we can mix criterias. //IEnumerable GetByTags(params string[] tags); diff --git a/src/CSInn.Domain.Repositories/Repositories/IRepository.cs b/src/CSInn.Domain.Repositories/Repositories/IRepository.cs index 1a3a707..0a310de 100644 --- a/src/CSInn.Domain.Repositories/Repositories/IRepository.cs +++ b/src/CSInn.Domain.Repositories/Repositories/IRepository.cs @@ -9,9 +9,7 @@ public interface IRepository { void Create(TModel model); void Update(TModel model); - void Delete(int key); IEnumerable Get(); - TModel Get(int id); IEnumerable Get(ISpecification specification); TModel Find(ISpecification specification); } diff --git a/src/CSInn.Domain.Repositories/Repositories/IRepositoryAsync.cs b/src/CSInn.Domain.Repositories/Repositories/IRepositoryAsync.cs new file mode 100644 index 0000000..97120d4 --- /dev/null +++ b/src/CSInn.Domain.Repositories/Repositories/IRepositoryAsync.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using CSInn.Domain.Repositories.Specifications.Base; + +namespace CSInn.Domain.Repositories.Repositories +{ + public interface IRepositoryAsync + where TModel : class + where TVisitor : ISpecificationVisitor + { + Task CreateAsync(TModel model, CancellationToken token = default); + Task UpdateAsync(TModel model); + Task DeleteAsync(TModel key); + Task> GetAsync(CancellationToken token = default); + Task> GetAsync(ISpecification specification, CancellationToken token = default); + Task FindAsync(ISpecification specification, CancellationToken token = default); + } +} \ No newline at end of file diff --git a/src/CSInn.Domain.Repositories/UnitOfWork/CSInnUnitOfWork.cs b/src/CSInn.Domain.Repositories/UnitOfWork/CSInnUnitOfWork.cs new file mode 100644 index 0000000..076f899 --- /dev/null +++ b/src/CSInn.Domain.Repositories/UnitOfWork/CSInnUnitOfWork.cs @@ -0,0 +1,15 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using CSInn.Domain.Repositories.Repositories; + +namespace CSInn.Domain.Repositories.UnitOfWork +{ + public interface ICSInnUnitOfWork: IDisposable + { + ILessonsRepository Lessons { get; } + + void Save(); + Task SaveAsync(CancellationToken token = default); + } +} diff --git a/src/CSInn.Infrastructure.Repositories/Repositories/LessonsRepository.cs b/src/CSInn.Infrastructure.Repositories/Repositories/LessonsRepository.cs index d24393f..ffc36cc 100644 --- a/src/CSInn.Infrastructure.Repositories/Repositories/LessonsRepository.cs +++ b/src/CSInn.Infrastructure.Repositories/Repositories/LessonsRepository.cs @@ -1,8 +1,11 @@ using System.Collections.Generic; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using CSInn.Domain.Repositories.Repositories; using CSInn.Domain.Repositories.Specifications.Base; using CSInn.Domain.Repositories.Specifications.Lesson; +using CSInn.Infrastructure.Repositories.Context; using CSInn.Infrastructure.Repositories.Entities; using CSInn.Infrastructure.Repositories.Expressions; using CSInn.Infrastructure.Repositories.Extensions; @@ -16,20 +19,19 @@ namespace CSInn.Experimental.EF.Repositories public class LessonsRepository: ILessonsRepository { private readonly CSInnDbContext _context; - private DbSet dbSet => _context.Lessons; + private DbSet DbSet => _context.Lessons; public LessonsRepository(CSInnDbContext context) { _context = context; } - public IEnumerable Get() => dbSet.ToModels(); - public IEnumerable Get(string title) => dbSet.Where(e => e.Title == title).Select(e => e.ToModel()); - public void Create(Lesson model) => dbSet.Add(model.ToEntity()); + public IEnumerable Get() => DbSet.ToModels(); + public void Create(Lesson model) => DbSet.Add(model.ToEntity()); public void Update(Lesson model) { - var entity = dbSet.Find(model.Id); + var entity = DbSet.Find(model.Id); if (entity == null) { @@ -41,37 +43,71 @@ public void Update(Lesson model) public void Delete(Lesson model) { - var entity = dbSet.Find(model.Id); - dbSet.Remove(entity); + var entity = DbSet.Find(model.Id); + DbSet.Remove(entity); } - public void Delete(int key) + public Lesson Find(ISpecification specification) { - throw new System.NotImplementedException(); + var visitor = new LessonEntityExpressionVisitor(); + specification.Accept(visitor); + var expression = visitor.Expression; + + return DbSet.FirstOrDefault(expression).ToModel(); } - public Lesson Get(int id) + public IEnumerable Get(ISpecification specification) { - throw new System.NotImplementedException(); + var visitor = new LessonEntityExpressionVisitor(); + var expression = visitor.ConvertSpecToExpression(specification); + + return DbSet.Where(expression).AsEnumerable().ToModels(); } - public Lesson Find(ISpecification specification) + public async Task CreateAsync(Lesson model, CancellationToken token = default) => await DbSet.AddAsync(model.ToEntity(), token); + + public async Task UpdateAsync(Lesson model) + { + var entity = await DbSet.FindAsync(model.Id); + + if (entity == null) + { + return; + } + + _context.Entry(entity).CurrentValues.SetValues(model); + } + + public async Task DeleteAsync(Lesson model) + { + var entity = await DbSet.FindAsync(model.Id); + DbSet.Remove(entity); + } + + public async Task> GetAsync(CancellationToken token = default) + { + var entities = await DbSet.ToListAsync(token); + return entities.ToModels(); + } + + public async Task> GetAsync(ISpecification specification, CancellationToken token = default) { var visitor = new LessonEntityExpressionVisitor(); - specification.Accept(visitor); - var expression = visitor.Expression; + var expression = visitor.ConvertSpecToExpression(specification); + + var entities = await DbSet.Where(expression).ToListAsync(token); - return dbSet.FirstOrDefault(expression).ToModel(); + return entities.ToModels(); } - public IEnumerable Get(ISpecification specification) + public async Task FindAsync(ISpecification specification, CancellationToken token = default) { var visitor = new LessonEntityExpressionVisitor(); - specification.Accept(visitor); - var expression = visitor.Expression; + var expression = visitor.ConvertSpecToExpression(specification); + + var entity = await DbSet.FirstOrDefaultAsync(expression, token); - var smth = expression.Compile(); - return dbSet.Where(smth).AsEnumerable().ToModels(); + return entity.ToModel(); } } } diff --git a/src/CSInn.Infrastructure.Repositories/CSInnDbContext.cs b/src/CSInn.Infrastructure.Repositories/UoW/CSInnDbContext.cs similarity index 88% rename from src/CSInn.Infrastructure.Repositories/CSInnDbContext.cs rename to src/CSInn.Infrastructure.Repositories/UoW/CSInnDbContext.cs index ce51265..052a25e 100644 --- a/src/CSInn.Infrastructure.Repositories/CSInnDbContext.cs +++ b/src/CSInn.Infrastructure.Repositories/UoW/CSInnDbContext.cs @@ -1,7 +1,7 @@ using CSInn.Infrastructure.Repositories.Entities; using Microsoft.EntityFrameworkCore; -namespace CSInn.Infrastructure.Repositories +namespace CSInn.Infrastructure.Repositories.Context { public class CSInnDbContext : DbContext { diff --git a/src/CSInn.Infrastructure.Repositories/UoW/CSInnUnitOfWork.cs b/src/CSInn.Infrastructure.Repositories/UoW/CSInnUnitOfWork.cs new file mode 100644 index 0000000..030c945 --- /dev/null +++ b/src/CSInn.Infrastructure.Repositories/UoW/CSInnUnitOfWork.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using CSInn.Domain.Repositories.Repositories; +using CSInn.Domain.Repositories.UnitOfWork; +using CSInn.Infrastructure.Repositories.Context; +using CSInn.Infrastructure.Repositories.Repositories.CSInn.Experimental.EF.Repositories; + +namespace CSInn.Infrastructure.Repositories.UoW +{ + public class CSInnUnitOfWork: ICSInnUnitOfWork + { + private readonly CSInnDbContext _context; + + private ILessonsRepository _lessons; + + public ILessonsRepository Lessons + { + get + { + if (_lessons == null) + { + _lessons = new LessonsRepository(_context); + } + + return _lessons; + } + private set => _lessons = value; + } + + public CSInnUnitOfWork(CSInnDbContext context) + { + _context = context; + } + + public void Save() => _context.SaveChanges(); + + + public Task SaveAsync(CancellationToken token = default) =>_context.SaveChangesAsync(); + + + #region IDisposable pattern + private bool disposed; + + protected virtual void Dispose(bool disposing) + { + if (!disposed) + { + if (disposing) + { + _context.Dispose(); + } + } + disposed = true; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + #endregion + } +} diff --git a/tests/CSInn.Infrastructure.Repositories.Tests/Input/UoWFixture.cs b/tests/CSInn.Infrastructure.Repositories.Tests/Input/UoWFixture.cs index a84bfcb..d7c4d3b 100644 --- a/tests/CSInn.Infrastructure.Repositories.Tests/Input/UoWFixture.cs +++ b/tests/CSInn.Infrastructure.Repositories.Tests/Input/UoWFixture.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text; +using CSInn.Infrastructure.Repositories.Context; using CSInn.Infrastructure.Repositories.Extensions; using CSInn.Infrastructure.Repositories.Repositories.CSInn.Experimental.EF.Repositories; using CSInn.Models; diff --git a/tests/CSInn.Infrastructure.Repositories.Tests/LessonSpecificationTests.cs b/tests/CSInn.Infrastructure.Repositories.Tests/LessonSpecificationTests.cs index 3c7e3d3..82daee0 100644 --- a/tests/CSInn.Infrastructure.Repositories.Tests/LessonSpecificationTests.cs +++ b/tests/CSInn.Infrastructure.Repositories.Tests/LessonSpecificationTests.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Linq.Expressions; +using System.Threading.Tasks; using CSInn.Domain.Repositories.Extensions; using CSInn.Domain.Repositories.Repositories; using CSInn.Domain.Repositories.Specifications.Lesson; @@ -29,5 +30,16 @@ public void Composite_Specification_Ok() Assert.Single(result); } + + [Fact] + public async Task Composite_Specification_Async_Ok() + { + var filter = new TitleLike("Lesson 2") + .Or(new TitleLike("Lesson 1")).Not(); + + var result = await _repository.GetAsync(filter); + + Assert.Single(result); + } } } From cd052ae85f456ffb688676cb05dd21dd19194787 Mon Sep 17 00:00:00 2001 From: Kaisinel Date: Sun, 29 Sep 2019 13:49:02 +0300 Subject: [PATCH 5/8] UoW in tests --- .../UoW/CSInnUnitOfWork.cs | 2 -- ...n.Infrastructure.Repositories.Tests.csproj | 1 + .../Input/UoWFixture.cs | 29 +++++++------------ .../LessonSpecificationTests.cs | 20 +++++-------- 4 files changed, 20 insertions(+), 32 deletions(-) diff --git a/src/CSInn.Infrastructure.Repositories/UoW/CSInnUnitOfWork.cs b/src/CSInn.Infrastructure.Repositories/UoW/CSInnUnitOfWork.cs index 030c945..500b926 100644 --- a/src/CSInn.Infrastructure.Repositories/UoW/CSInnUnitOfWork.cs +++ b/src/CSInn.Infrastructure.Repositories/UoW/CSInnUnitOfWork.cs @@ -37,10 +37,8 @@ public CSInnUnitOfWork(CSInnDbContext context) public void Save() => _context.SaveChanges(); - public Task SaveAsync(CancellationToken token = default) =>_context.SaveChangesAsync(); - #region IDisposable pattern private bool disposed; diff --git a/tests/CSInn.Infrastructure.Repositories.Tests/CSInn.Infrastructure.Repositories.Tests.csproj b/tests/CSInn.Infrastructure.Repositories.Tests/CSInn.Infrastructure.Repositories.Tests.csproj index 1315505..a793555 100644 --- a/tests/CSInn.Infrastructure.Repositories.Tests/CSInn.Infrastructure.Repositories.Tests.csproj +++ b/tests/CSInn.Infrastructure.Repositories.Tests/CSInn.Infrastructure.Repositories.Tests.csproj @@ -17,6 +17,7 @@ + diff --git a/tests/CSInn.Infrastructure.Repositories.Tests/Input/UoWFixture.cs b/tests/CSInn.Infrastructure.Repositories.Tests/Input/UoWFixture.cs index d7c4d3b..e0a849e 100644 --- a/tests/CSInn.Infrastructure.Repositories.Tests/Input/UoWFixture.cs +++ b/tests/CSInn.Infrastructure.Repositories.Tests/Input/UoWFixture.cs @@ -1,38 +1,31 @@ -using System; -using System.Collections.Generic; -using System.Text; +using System.Collections.Generic; using CSInn.Infrastructure.Repositories.Context; -using CSInn.Infrastructure.Repositories.Extensions; -using CSInn.Infrastructure.Repositories.Repositories.CSInn.Experimental.EF.Repositories; +using CSInn.Infrastructure.Repositories.UoW; using CSInn.Models; using Microsoft.EntityFrameworkCore; namespace CSInn.Infrastructure.Repositories.Tests.Input { - public class LessonsRepositoryFixture + public class UoWFixture { - public LessonsRepository Repo { get; set; } - public LessonsRepositoryFixture() + public CSInnUnitOfWork UoW { get; set; } + public UoWFixture() { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(databaseName: "database_name") .Options; var context = new CSInnDbContext(options); + UoW = new CSInnUnitOfWork(context); - context.Lessons.Add( - new Lesson("Lesson 1: Class", "What is a class, object", new List() { "OOP" }, - new List() { "Almantas Karpavičius" }).ToEntity()); + UoW.Lessons.Create(new Lesson("Lesson 2: SOLID", "SOLID", new List() { "OOP" }, + new List() { "Kaisinel" })); - context.Lessons.Add(new Lesson("Lesson 2: SOLID", "SOLID", new List() { "OOP" }, - new List() { "Kaisinel" }).ToEntity()); - - context.Lessons.Add( + UoW.Lessons.Create( new Lesson("Lesson 3: Delegates", "Delegate vs callback", new List() { "FP" }, - new List() { "Lethern" }).ToEntity()); - context.SaveChanges(); + new List() { "Lethern" })); - Repo = new LessonsRepository(context); + UoW.Save(); } } } diff --git a/tests/CSInn.Infrastructure.Repositories.Tests/LessonSpecificationTests.cs b/tests/CSInn.Infrastructure.Repositories.Tests/LessonSpecificationTests.cs index 82daee0..27db221 100644 --- a/tests/CSInn.Infrastructure.Repositories.Tests/LessonSpecificationTests.cs +++ b/tests/CSInn.Infrastructure.Repositories.Tests/LessonSpecificationTests.cs @@ -1,23 +1,19 @@ -using System; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; +using System.Threading.Tasks; using CSInn.Domain.Repositories.Extensions; -using CSInn.Domain.Repositories.Repositories; using CSInn.Domain.Repositories.Specifications.Lesson; -using CSInn.Infrastructure.Repositories.Entities; +using CSInn.Domain.Repositories.UnitOfWork; using CSInn.Infrastructure.Repositories.Tests.Input; using Xunit; namespace CSInn.Infrastructure.Repositories.Tests { - public class LessonSpecificationTests : IClassFixture + public class LessonSpecificationTests : IClassFixture { - private readonly ILessonsRepository _repository; + private readonly ICSInnUnitOfWork _uow; - public LessonSpecificationTests(LessonsRepositoryFixture fixture) + public LessonSpecificationTests(UoWFixture fixture) { - _repository = fixture.Repo; + _uow = fixture.UoW; } [Fact] @@ -26,7 +22,7 @@ public void Composite_Specification_Ok() var filter = new TitleLike("Lesson 2") .Or(new TitleLike("Lesson 1")).Not(); - var result = _repository.Get(filter); + var result = _uow.Lessons.Get(filter); Assert.Single(result); } @@ -37,7 +33,7 @@ public async Task Composite_Specification_Async_Ok() var filter = new TitleLike("Lesson 2") .Or(new TitleLike("Lesson 1")).Not(); - var result = await _repository.GetAsync(filter); + var result = await _uow.Lessons.GetAsync(filter); Assert.Single(result); } From 683393e960b76d7d60cd7d1f740a50f73d8032f5 Mon Sep 17 00:00:00 2001 From: Kaisinel Date: Sun, 29 Sep 2019 16:23:21 +0300 Subject: [PATCH 6/8] Generic Repository with specifications --- .../CSInn.Infrastructure.Repositories.csproj | 1 - .../Entities/LessonEntity.cs | 4 +- .../Expressions/ExpressionExtensions.cs | 1 - .../Expressions/ExpressionVisitor.cs | 4 +- .../Expressions/LessonExpressionVisitor.cs | 3 +- .../Extensions/LessonExtensions.cs | 1 - .../Repositories/LessonsRepository.cs | 225 +++++++++++------- .../Repositories/Repository.cs | 111 +++++++++ .../UoW/CSInnUnitOfWork.cs | 2 - .../Exceptions/InvalidLessonException.cs | 1 - .../LessonSpecificationTests.cs | 6 +- 11 files changed, 256 insertions(+), 103 deletions(-) create mode 100644 src/CSInn.Infrastructure.Repositories/Repositories/Repository.cs diff --git a/src/CSInn.Infrastructure.Repositories/CSInn.Infrastructure.Repositories.csproj b/src/CSInn.Infrastructure.Repositories/CSInn.Infrastructure.Repositories.csproj index f72c062..577b046 100644 --- a/src/CSInn.Infrastructure.Repositories/CSInn.Infrastructure.Repositories.csproj +++ b/src/CSInn.Infrastructure.Repositories/CSInn.Infrastructure.Repositories.csproj @@ -11,7 +11,6 @@ - diff --git a/src/CSInn.Infrastructure.Repositories/Entities/LessonEntity.cs b/src/CSInn.Infrastructure.Repositories/Entities/LessonEntity.cs index c9233b4..4478a63 100644 --- a/src/CSInn.Infrastructure.Repositories/Entities/LessonEntity.cs +++ b/src/CSInn.Infrastructure.Repositories/Entities/LessonEntity.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace CSInn.Infrastructure.Repositories.Entities +namespace CSInn.Infrastructure.Repositories.Entities { public class LessonEntity { diff --git a/src/CSInn.Infrastructure.Repositories/Expressions/ExpressionExtensions.cs b/src/CSInn.Infrastructure.Repositories/Expressions/ExpressionExtensions.cs index 51d29f8..75e19fd 100644 --- a/src/CSInn.Infrastructure.Repositories/Expressions/ExpressionExtensions.cs +++ b/src/CSInn.Infrastructure.Repositories/Expressions/ExpressionExtensions.cs @@ -1,6 +1,5 @@ using System; using System.Linq.Expressions; -using System.Runtime.CompilerServices; using CSInn.Domain.Repositories.ExpressionVisitors; namespace CSInn.Infrastructure.Repositories.Expressions diff --git a/src/CSInn.Infrastructure.Repositories/Expressions/ExpressionVisitor.cs b/src/CSInn.Infrastructure.Repositories/Expressions/ExpressionVisitor.cs index cabe97a..06d00ac 100644 --- a/src/CSInn.Infrastructure.Repositories/Expressions/ExpressionVisitor.cs +++ b/src/CSInn.Infrastructure.Repositories/Expressions/ExpressionVisitor.cs @@ -1,13 +1,11 @@ using System; -using System.Linq; using System.Linq.Expressions; -using CSInn.Domain.Repositories.Extensions; using CSInn.Domain.Repositories.Specifications.Base; // TODO: References should be internals visible to this one. namespace CSInn.Infrastructure.Repositories.Expressions { - internal abstract class ExpressionVisitor + public abstract class ExpressionVisitor where TVisitor : ISpecificationVisitor { public Expression> Expression { get; protected set; } diff --git a/src/CSInn.Infrastructure.Repositories/Expressions/LessonExpressionVisitor.cs b/src/CSInn.Infrastructure.Repositories/Expressions/LessonExpressionVisitor.cs index a4d0900..5e69ac9 100644 --- a/src/CSInn.Infrastructure.Repositories/Expressions/LessonExpressionVisitor.cs +++ b/src/CSInn.Infrastructure.Repositories/Expressions/LessonExpressionVisitor.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using System.Linq.Expressions; using CSInn.Domain.Repositories.Specifications.Base; using CSInn.Domain.Repositories.Specifications.Lesson; @@ -8,7 +7,7 @@ namespace CSInn.Infrastructure.Repositories.Expressions { - internal class LessonEntityExpressionVisitor : ExpressionVisitor, ILessonSpecificationVisitor + public class LessonEntityExpressionVisitor : ExpressionVisitor, ILessonSpecificationVisitor { public override Expression> ConvertSpecToExpression(ISpecification spec) { diff --git a/src/CSInn.Infrastructure.Repositories/Extensions/LessonExtensions.cs b/src/CSInn.Infrastructure.Repositories/Extensions/LessonExtensions.cs index 0abf873..1ddf9f4 100644 --- a/src/CSInn.Infrastructure.Repositories/Extensions/LessonExtensions.cs +++ b/src/CSInn.Infrastructure.Repositories/Extensions/LessonExtensions.cs @@ -2,7 +2,6 @@ using System.Linq; using CSInn.Infrastructure.Repositories.Entities; using CSInn.Models; -using Microsoft.EntityFrameworkCore; namespace CSInn.Infrastructure.Repositories.Extensions { diff --git a/src/CSInn.Infrastructure.Repositories/Repositories/LessonsRepository.cs b/src/CSInn.Infrastructure.Repositories/Repositories/LessonsRepository.cs index ffc36cc..658f15a 100644 --- a/src/CSInn.Infrastructure.Repositories/Repositories/LessonsRepository.cs +++ b/src/CSInn.Infrastructure.Repositories/Repositories/LessonsRepository.cs @@ -1,114 +1,165 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; +//using System.Collections.Generic; +//using System.Linq; +//using System.Threading; +//using System.Threading.Tasks; +//using CSInn.Domain.Repositories.Repositories; +//using CSInn.Domain.Repositories.Specifications.Base; +//using CSInn.Domain.Repositories.Specifications.Lesson; +//using CSInn.Infrastructure.Repositories.Context; +//using CSInn.Infrastructure.Repositories.Entities; +//using CSInn.Infrastructure.Repositories.Expressions; +//using CSInn.Infrastructure.Repositories.Extensions; +//using CSInn.Models; +//using Microsoft.EntityFrameworkCore; + +//namespace CSInn.Infrastructure.Repositories.Repositories +//{ +// namespace CSInn.Experimental.EF.Repositories +// { +// public class LessonsRepository: ILessonsRepository +// { +// private readonly CSInnDbContext _context; +// private DbSet DbSet => _context.Lessons; + +// public LessonsRepository(CSInnDbContext context) +// { +// _context = context; +// } + +// public IEnumerable Get() => DbSet.ToModels(); +// public void Create(Lesson model) => DbSet.Add(model.ToEntity()); + +// public void Update(Lesson model) +// { +// var entity = DbSet.Find(model.Id); + +// if (entity == null) +// { +// return; +// } + +// _context.Entry(entity).CurrentValues.SetValues(model); +// } + +// public void Delete(Lesson model) +// { +// var entity = DbSet.Find(model.Id); +// DbSet.Remove(entity); +// } + +// public Lesson Find(ISpecification specification) +// { +// var visitor = new LessonEntityExpressionVisitor(); +// specification.Accept(visitor); +// var expression = visitor.Expression; + +// return DbSet.FirstOrDefault(expression).ToModel(); +// } + +// public IEnumerable Get(ISpecification specification) +// { +// var visitor = new LessonEntityExpressionVisitor(); +// var expression = visitor.ConvertSpecToExpression(specification); + +// return DbSet.Where(expression).AsEnumerable().ToModels(); +// } + +// public async Task CreateAsync(Lesson model, CancellationToken token = default) => await DbSet.AddAsync(model.ToEntity(), token); + +// public async Task UpdateAsync(Lesson model) +// { +// var entity = await DbSet.FindAsync(model.Id); + +// if (entity == null) +// { +// return; +// } + +// _context.Entry(entity).CurrentValues.SetValues(model); +// } + +// public async Task DeleteAsync(Lesson model) +// { +// var entity = await DbSet.FindAsync(model.Id); +// DbSet.Remove(entity); +// } + +// public async Task> GetAsync(CancellationToken token = default) +// { +// var entities = await DbSet.ToListAsync(token); +// return entities.ToModels(); +// } + +// public async Task> GetAsync(ISpecification specification, CancellationToken token = default) +// { +// var visitor = new LessonEntityExpressionVisitor(); +// var expression = visitor.ConvertSpecToExpression(specification); + +// var entities = await DbSet.Where(expression).ToListAsync(token); + +// return entities.ToModels(); +// } + +// public async Task FindAsync(ISpecification specification, CancellationToken token = default) +// { +// var visitor = new LessonEntityExpressionVisitor(); +// var expression = visitor.ConvertSpecToExpression(specification); + +// var entity = await DbSet.FirstOrDefaultAsync(expression, token); + +// return entity.ToModel(); +// } +// } +// } +//} + using CSInn.Domain.Repositories.Repositories; -using CSInn.Domain.Repositories.Specifications.Base; using CSInn.Domain.Repositories.Specifications.Lesson; using CSInn.Infrastructure.Repositories.Context; using CSInn.Infrastructure.Repositories.Entities; using CSInn.Infrastructure.Repositories.Expressions; -using CSInn.Infrastructure.Repositories.Extensions; using CSInn.Models; -using Microsoft.EntityFrameworkCore; namespace CSInn.Infrastructure.Repositories.Repositories { namespace CSInn.Experimental.EF.Repositories { - public class LessonsRepository: ILessonsRepository + public class LessonsRepository : Repository, + ILessonsRepository { - private readonly CSInnDbContext _context; - private DbSet DbSet => _context.Lessons; - - public LessonsRepository(CSInnDbContext context) + public LessonsRepository(CSInnDbContext context) : base(context) { - _context = context; } - public IEnumerable Get() => DbSet.ToModels(); - public void Create(Lesson model) => DbSet.Add(model.ToEntity()); - - public void Update(Lesson model) + protected override Lesson ToModel(LessonEntity entity) { - var entity = DbSet.Find(model.Id); - - if (entity == null) + // call to automapper + var lesson = new Lesson(entity.Title, entity.Description, entity.Tags.Split(","), entity.Authors.Split(",")) { - return; - } - - _context.Entry(entity).CurrentValues.SetValues(model); - } - - public void Delete(Lesson model) - { - var entity = DbSet.Find(model.Id); - DbSet.Remove(entity); - } - - public Lesson Find(ISpecification specification) - { - var visitor = new LessonEntityExpressionVisitor(); - specification.Accept(visitor); - var expression = visitor.Expression; - - return DbSet.FirstOrDefault(expression).ToModel(); - } - - public IEnumerable Get(ISpecification specification) - { - var visitor = new LessonEntityExpressionVisitor(); - var expression = visitor.ConvertSpecToExpression(specification); + Id = entity.Id, + }; - return DbSet.Where(expression).AsEnumerable().ToModels(); + return lesson; } - public async Task CreateAsync(Lesson model, CancellationToken token = default) => await DbSet.AddAsync(model.ToEntity(), token); - - public async Task UpdateAsync(Lesson model) + protected override LessonEntity ToEntity(Lesson model) { - var entity = await DbSet.FindAsync(model.Id); - - if (entity == null) + // call to automapper + var lesson = new LessonEntity() { - return; - } - - _context.Entry(entity).CurrentValues.SetValues(model); - } - - public async Task DeleteAsync(Lesson model) - { - var entity = await DbSet.FindAsync(model.Id); - DbSet.Remove(entity); - } - - public async Task> GetAsync(CancellationToken token = default) - { - var entities = await DbSet.ToListAsync(token); - return entities.ToModels(); - } - - public async Task> GetAsync(ISpecification specification, CancellationToken token = default) - { - var visitor = new LessonEntityExpressionVisitor(); - var expression = visitor.ConvertSpecToExpression(specification); - - var entities = await DbSet.Where(expression).ToListAsync(token); - - return entities.ToModels(); + Id = model.Id, + Title = model.Title, + Authors = string.Join(',', model.Authors), + Description = model.Description, + Slides = model.Slides, + Tags = string.Join(',', model.Tags) + }; + + return lesson; } - public async Task FindAsync(ISpecification specification, CancellationToken token = default) - { - var visitor = new LessonEntityExpressionVisitor(); - var expression = visitor.ConvertSpecToExpression(specification); - - var entity = await DbSet.FirstOrDefaultAsync(expression, token); - - return entity.ToModel(); - } + protected override int ExtractKey(Lesson model) => model.Id; } } } + diff --git a/src/CSInn.Infrastructure.Repositories/Repositories/Repository.cs b/src/CSInn.Infrastructure.Repositories/Repositories/Repository.cs new file mode 100644 index 0000000..02bb220 --- /dev/null +++ b/src/CSInn.Infrastructure.Repositories/Repositories/Repository.cs @@ -0,0 +1,111 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using CSInn.Domain.Repositories.Repositories; +using CSInn.Domain.Repositories.Specifications.Base; +using CSInn.Infrastructure.Repositories.Context; +using CSInn.Infrastructure.Repositories.Expressions; +using Microsoft.EntityFrameworkCore; + +namespace CSInn.Infrastructure.Repositories.Repositories +{ + public abstract class Repository : IRepository, IRepositoryAsync + where TModel : class + where TEntity : class + where TDomainVisitor : ISpecificationVisitor + where TEFVisitor : ExpressionVisitor, new() + { + private readonly TEFVisitor _visitor; + + private readonly CSInnDbContext _context; + private readonly DbSet _dbSet; + + protected Repository(CSInnDbContext context) + { + _context = context; + _visitor = new TEFVisitor(); + _dbSet = _context.Set(); + } + + protected abstract TModel ToModel(TEntity entity); + protected abstract TEntity ToEntity(TModel model); + protected abstract int ExtractKey(TModel model); + + public IEnumerable Get() => _dbSet.Select(e => ToModel(e)); + public void Create(TModel model) => _dbSet.Add(ToEntity(model)); + + public void Update(TModel model) + { + var entity = _dbSet.Find(ExtractKey(model)); + + if (entity == null) + { + return; + } + + _context.Entry(entity).CurrentValues.SetValues(model); + } + + public void Delete(TModel model) + { + var entity = _dbSet.Find(ExtractKey(model)); + _dbSet.Remove(entity); + } + + public TModel Find(ISpecification specification) + { + var expression = _visitor.ConvertSpecToExpression(specification); + return ToModel(_dbSet.FirstOrDefault(expression)); + } + + public IEnumerable Get(ISpecification specification) + { + var expression = _visitor.ConvertSpecToExpression(specification); + return _dbSet.Where(expression).Select(e => ToModel(e)); + } + + public async Task CreateAsync(TModel model, CancellationToken token = default) => await _dbSet.AddAsync(ToEntity(model), token); + + public async Task UpdateAsync(TModel model) + { + var entity = await _dbSet.FindAsync(ExtractKey(model)); + + if (entity == null) + { + return; + } + + _context.Entry(entity).CurrentValues.SetValues(model); + } + + public async Task DeleteAsync(TModel model) + { + var entity = await _dbSet.FindAsync(ExtractKey(model)); + _dbSet.Remove(entity); + } + + public async Task> GetAsync(CancellationToken token = default) + { + var entities = await _dbSet.ToListAsync(token); + return entities.Select(ToModel); + } + + public async Task> GetAsync(ISpecification specification, CancellationToken token = default) + { + var expression = _visitor.ConvertSpecToExpression(specification); + var entities = await _dbSet.Where(expression).ToListAsync(token); + + return entities.Select(ToModel); + } + + public async Task FindAsync(ISpecification specification, CancellationToken token = default) + { + var expression = _visitor.ConvertSpecToExpression(specification); + var entity = await _dbSet.FirstOrDefaultAsync(expression, token); + + return ToModel(entity); + } + + } +} diff --git a/src/CSInn.Infrastructure.Repositories/UoW/CSInnUnitOfWork.cs b/src/CSInn.Infrastructure.Repositories/UoW/CSInnUnitOfWork.cs index 500b926..a8d2fd2 100644 --- a/src/CSInn.Infrastructure.Repositories/UoW/CSInnUnitOfWork.cs +++ b/src/CSInn.Infrastructure.Repositories/UoW/CSInnUnitOfWork.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Threading; using System.Threading.Tasks; using CSInn.Domain.Repositories.Repositories; diff --git a/src/CSInn.Models/Exceptions/InvalidLessonException.cs b/src/CSInn.Models/Exceptions/InvalidLessonException.cs index a215143..ff41c40 100644 --- a/src/CSInn.Models/Exceptions/InvalidLessonException.cs +++ b/src/CSInn.Models/Exceptions/InvalidLessonException.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; namespace CSInn.Domain.Models.Content.Exceptions { diff --git a/tests/CSInn.Infrastructure.Repositories.Tests/LessonSpecificationTests.cs b/tests/CSInn.Infrastructure.Repositories.Tests/LessonSpecificationTests.cs index 27db221..be31442 100644 --- a/tests/CSInn.Infrastructure.Repositories.Tests/LessonSpecificationTests.cs +++ b/tests/CSInn.Infrastructure.Repositories.Tests/LessonSpecificationTests.cs @@ -1,8 +1,10 @@ -using System.Threading.Tasks; +using System.Linq; +using System.Threading.Tasks; using CSInn.Domain.Repositories.Extensions; using CSInn.Domain.Repositories.Specifications.Lesson; using CSInn.Domain.Repositories.UnitOfWork; using CSInn.Infrastructure.Repositories.Tests.Input; +using CSInn.Models; using Xunit; namespace CSInn.Infrastructure.Repositories.Tests @@ -23,7 +25,7 @@ public void Composite_Specification_Ok() .Or(new TitleLike("Lesson 1")).Not(); var result = _uow.Lessons.Get(filter); - + Assert.Single(result); } From 6e554514dd7c6b07378ce7f1055396286143b937 Mon Sep 17 00:00:00 2001 From: Kaisinel Date: Sun, 29 Sep 2019 16:54:43 +0300 Subject: [PATCH 7/8] Commented out repository removed --- .../Repositories/LessonsRepository.cs | 117 +----------------- 1 file changed, 1 insertion(+), 116 deletions(-) diff --git a/src/CSInn.Infrastructure.Repositories/Repositories/LessonsRepository.cs b/src/CSInn.Infrastructure.Repositories/Repositories/LessonsRepository.cs index 658f15a..afb4b3c 100644 --- a/src/CSInn.Infrastructure.Repositories/Repositories/LessonsRepository.cs +++ b/src/CSInn.Infrastructure.Repositories/Repositories/LessonsRepository.cs @@ -1,119 +1,4 @@ -//using System.Collections.Generic; -//using System.Linq; -//using System.Threading; -//using System.Threading.Tasks; -//using CSInn.Domain.Repositories.Repositories; -//using CSInn.Domain.Repositories.Specifications.Base; -//using CSInn.Domain.Repositories.Specifications.Lesson; -//using CSInn.Infrastructure.Repositories.Context; -//using CSInn.Infrastructure.Repositories.Entities; -//using CSInn.Infrastructure.Repositories.Expressions; -//using CSInn.Infrastructure.Repositories.Extensions; -//using CSInn.Models; -//using Microsoft.EntityFrameworkCore; - -//namespace CSInn.Infrastructure.Repositories.Repositories -//{ -// namespace CSInn.Experimental.EF.Repositories -// { -// public class LessonsRepository: ILessonsRepository -// { -// private readonly CSInnDbContext _context; -// private DbSet DbSet => _context.Lessons; - -// public LessonsRepository(CSInnDbContext context) -// { -// _context = context; -// } - -// public IEnumerable Get() => DbSet.ToModels(); -// public void Create(Lesson model) => DbSet.Add(model.ToEntity()); - -// public void Update(Lesson model) -// { -// var entity = DbSet.Find(model.Id); - -// if (entity == null) -// { -// return; -// } - -// _context.Entry(entity).CurrentValues.SetValues(model); -// } - -// public void Delete(Lesson model) -// { -// var entity = DbSet.Find(model.Id); -// DbSet.Remove(entity); -// } - -// public Lesson Find(ISpecification specification) -// { -// var visitor = new LessonEntityExpressionVisitor(); -// specification.Accept(visitor); -// var expression = visitor.Expression; - -// return DbSet.FirstOrDefault(expression).ToModel(); -// } - -// public IEnumerable Get(ISpecification specification) -// { -// var visitor = new LessonEntityExpressionVisitor(); -// var expression = visitor.ConvertSpecToExpression(specification); - -// return DbSet.Where(expression).AsEnumerable().ToModels(); -// } - -// public async Task CreateAsync(Lesson model, CancellationToken token = default) => await DbSet.AddAsync(model.ToEntity(), token); - -// public async Task UpdateAsync(Lesson model) -// { -// var entity = await DbSet.FindAsync(model.Id); - -// if (entity == null) -// { -// return; -// } - -// _context.Entry(entity).CurrentValues.SetValues(model); -// } - -// public async Task DeleteAsync(Lesson model) -// { -// var entity = await DbSet.FindAsync(model.Id); -// DbSet.Remove(entity); -// } - -// public async Task> GetAsync(CancellationToken token = default) -// { -// var entities = await DbSet.ToListAsync(token); -// return entities.ToModels(); -// } - -// public async Task> GetAsync(ISpecification specification, CancellationToken token = default) -// { -// var visitor = new LessonEntityExpressionVisitor(); -// var expression = visitor.ConvertSpecToExpression(specification); - -// var entities = await DbSet.Where(expression).ToListAsync(token); - -// return entities.ToModels(); -// } - -// public async Task FindAsync(ISpecification specification, CancellationToken token = default) -// { -// var visitor = new LessonEntityExpressionVisitor(); -// var expression = visitor.ConvertSpecToExpression(specification); - -// var entity = await DbSet.FirstOrDefaultAsync(expression, token); - -// return entity.ToModel(); -// } -// } -// } -//} - -using CSInn.Domain.Repositories.Repositories; +using CSInn.Domain.Repositories.Repositories; using CSInn.Domain.Repositories.Specifications.Lesson; using CSInn.Infrastructure.Repositories.Context; using CSInn.Infrastructure.Repositories.Entities; From 3e4fe2c19093f7a0d02b6ff17faf67c8133c9c66 Mon Sep 17 00:00:00 2001 From: lethern Date: Fri, 18 Oct 2019 02:35:43 +0200 Subject: [PATCH 8/8] new project - Console App for testing automapper and stuff........ --- CSInn.sln | 197 ++++++++------- ConsoleApp1/ConsoleApp1.csproj | 41 +++ ConsoleApp1/Program - Copy.cs | 34 +++ ConsoleApp1/Program.cs | 58 +++++ ConsoleApp1/allthestuff.cs | 238 ++++++++++++++++++ ConsoleApp1/appsettings.Development.json | 9 + ConsoleApp1/appsettings.json | 13 + .../Entities/LessonEntity.cs | 3 + .../UoW/CSInnDbContext.cs | 1 - src/CSInn.Models/Lesson.cs | 4 + 10 files changed, 502 insertions(+), 96 deletions(-) create mode 100644 ConsoleApp1/ConsoleApp1.csproj create mode 100644 ConsoleApp1/Program - Copy.cs create mode 100644 ConsoleApp1/Program.cs create mode 100644 ConsoleApp1/allthestuff.cs create mode 100644 ConsoleApp1/appsettings.Development.json create mode 100644 ConsoleApp1/appsettings.json diff --git a/CSInn.sln b/CSInn.sln index 4506e73..ab87340 100644 --- a/CSInn.sln +++ b/CSInn.sln @@ -1,95 +1,102 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29306.81 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{186B0C3C-02E4-416C-9CD1-067E7662A53E}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{399E33DB-3837-48F0-BFFA-B756D995BD2D}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSInn.UI", "src\CSInn.UI\CSInn.UI.csproj", "{B6E0D216-88C8-4BA1-A71C-7C6BDB4247BB}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSInn.Discord.Authentication", "src\CSInn.Discord.Authentication\CSInn.Discord.Authentication.csproj", "{4BA8C0D8-A2CA-4E84-B018-644C2859F5A5}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSInn.Application.Tests", "tests\CSInn.Application.Tests\CSInn.Application.Tests.csproj", "{B92834CE-81D2-4E82-86CA-4940CFB8A102}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSInn.Domain.Repositories", "src\CSInn.Domain.Repositories\CSInn.Domain.Repositories.csproj", "{C0BCA68B-7CD7-4037-8DEF-3C1DFA0FFA64}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSInn.Infrastructure.Repositories", "src\CSInn.Infrastructure.Repositories\CSInn.Infrastructure.Repositories.csproj", "{68360ABC-FFEC-4A14-94EC-DCA6B96C044E}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSInn.Application", "src\CSInn.Application\CSInn.Application.csproj", "{9FC69081-2A6A-4990-ACC3-4D1A1D08AA73}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSInn.Discord.Authentication.Tests", "tests\CSInn.Discord.Authentication.Tests\CSInn.Discord.Authentication.Tests.csproj", "{989E16D6-A255-4AC5-BAE1-3D78B38EE258}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSInn.UI.Tests", "tests\CSInn.UI.Tests\CSInn.UI.Tests.csproj", "{2159A971-3858-48F0-99B4-6F40C4F50863}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSInn.Infrastructure.Repositories.Tests", "tests\CSInn.Infrastructure.Repositories.Tests\CSInn.Infrastructure.Repositories.Tests.csproj", "{1B2CDA69-B45A-4F95-B5C9-7AC3F834A7B7}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSInn.Models", "src\CSInn.Models\CSInn.Models.csproj", "{A4D369D6-4937-4BB4-8F90-89DBCADE1481}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B6E0D216-88C8-4BA1-A71C-7C6BDB4247BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B6E0D216-88C8-4BA1-A71C-7C6BDB4247BB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B6E0D216-88C8-4BA1-A71C-7C6BDB4247BB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B6E0D216-88C8-4BA1-A71C-7C6BDB4247BB}.Release|Any CPU.Build.0 = Release|Any CPU - {4BA8C0D8-A2CA-4E84-B018-644C2859F5A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4BA8C0D8-A2CA-4E84-B018-644C2859F5A5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4BA8C0D8-A2CA-4E84-B018-644C2859F5A5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4BA8C0D8-A2CA-4E84-B018-644C2859F5A5}.Release|Any CPU.Build.0 = Release|Any CPU - {B92834CE-81D2-4E82-86CA-4940CFB8A102}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B92834CE-81D2-4E82-86CA-4940CFB8A102}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B92834CE-81D2-4E82-86CA-4940CFB8A102}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B92834CE-81D2-4E82-86CA-4940CFB8A102}.Release|Any CPU.Build.0 = Release|Any CPU - {C0BCA68B-7CD7-4037-8DEF-3C1DFA0FFA64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C0BCA68B-7CD7-4037-8DEF-3C1DFA0FFA64}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C0BCA68B-7CD7-4037-8DEF-3C1DFA0FFA64}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C0BCA68B-7CD7-4037-8DEF-3C1DFA0FFA64}.Release|Any CPU.Build.0 = Release|Any CPU - {68360ABC-FFEC-4A14-94EC-DCA6B96C044E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {68360ABC-FFEC-4A14-94EC-DCA6B96C044E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {68360ABC-FFEC-4A14-94EC-DCA6B96C044E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {68360ABC-FFEC-4A14-94EC-DCA6B96C044E}.Release|Any CPU.Build.0 = Release|Any CPU - {9FC69081-2A6A-4990-ACC3-4D1A1D08AA73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9FC69081-2A6A-4990-ACC3-4D1A1D08AA73}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9FC69081-2A6A-4990-ACC3-4D1A1D08AA73}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9FC69081-2A6A-4990-ACC3-4D1A1D08AA73}.Release|Any CPU.Build.0 = Release|Any CPU - {989E16D6-A255-4AC5-BAE1-3D78B38EE258}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {989E16D6-A255-4AC5-BAE1-3D78B38EE258}.Debug|Any CPU.Build.0 = Debug|Any CPU - {989E16D6-A255-4AC5-BAE1-3D78B38EE258}.Release|Any CPU.ActiveCfg = Release|Any CPU - {989E16D6-A255-4AC5-BAE1-3D78B38EE258}.Release|Any CPU.Build.0 = Release|Any CPU - {2159A971-3858-48F0-99B4-6F40C4F50863}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2159A971-3858-48F0-99B4-6F40C4F50863}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2159A971-3858-48F0-99B4-6F40C4F50863}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2159A971-3858-48F0-99B4-6F40C4F50863}.Release|Any CPU.Build.0 = Release|Any CPU - {1B2CDA69-B45A-4F95-B5C9-7AC3F834A7B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1B2CDA69-B45A-4F95-B5C9-7AC3F834A7B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1B2CDA69-B45A-4F95-B5C9-7AC3F834A7B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1B2CDA69-B45A-4F95-B5C9-7AC3F834A7B7}.Release|Any CPU.Build.0 = Release|Any CPU - {A4D369D6-4937-4BB4-8F90-89DBCADE1481}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A4D369D6-4937-4BB4-8F90-89DBCADE1481}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A4D369D6-4937-4BB4-8F90-89DBCADE1481}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A4D369D6-4937-4BB4-8F90-89DBCADE1481}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {B6E0D216-88C8-4BA1-A71C-7C6BDB4247BB} = {186B0C3C-02E4-416C-9CD1-067E7662A53E} - {4BA8C0D8-A2CA-4E84-B018-644C2859F5A5} = {186B0C3C-02E4-416C-9CD1-067E7662A53E} - {B92834CE-81D2-4E82-86CA-4940CFB8A102} = {399E33DB-3837-48F0-BFFA-B756D995BD2D} - {C0BCA68B-7CD7-4037-8DEF-3C1DFA0FFA64} = {186B0C3C-02E4-416C-9CD1-067E7662A53E} - {68360ABC-FFEC-4A14-94EC-DCA6B96C044E} = {186B0C3C-02E4-416C-9CD1-067E7662A53E} - {9FC69081-2A6A-4990-ACC3-4D1A1D08AA73} = {186B0C3C-02E4-416C-9CD1-067E7662A53E} - {989E16D6-A255-4AC5-BAE1-3D78B38EE258} = {399E33DB-3837-48F0-BFFA-B756D995BD2D} - {2159A971-3858-48F0-99B4-6F40C4F50863} = {399E33DB-3837-48F0-BFFA-B756D995BD2D} - {1B2CDA69-B45A-4F95-B5C9-7AC3F834A7B7} = {399E33DB-3837-48F0-BFFA-B756D995BD2D} - {A4D369D6-4937-4BB4-8F90-89DBCADE1481} = {186B0C3C-02E4-416C-9CD1-067E7662A53E} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {6BBE2057-901B-474F-9140-0D045DF18046} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29306.81 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{186B0C3C-02E4-416C-9CD1-067E7662A53E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{399E33DB-3837-48F0-BFFA-B756D995BD2D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSInn.UI", "src\CSInn.UI\CSInn.UI.csproj", "{B6E0D216-88C8-4BA1-A71C-7C6BDB4247BB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSInn.Discord.Authentication", "src\CSInn.Discord.Authentication\CSInn.Discord.Authentication.csproj", "{4BA8C0D8-A2CA-4E84-B018-644C2859F5A5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSInn.Application.Tests", "tests\CSInn.Application.Tests\CSInn.Application.Tests.csproj", "{B92834CE-81D2-4E82-86CA-4940CFB8A102}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSInn.Domain.Repositories", "src\CSInn.Domain.Repositories\CSInn.Domain.Repositories.csproj", "{C0BCA68B-7CD7-4037-8DEF-3C1DFA0FFA64}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSInn.Infrastructure.Repositories", "src\CSInn.Infrastructure.Repositories\CSInn.Infrastructure.Repositories.csproj", "{68360ABC-FFEC-4A14-94EC-DCA6B96C044E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSInn.Application", "src\CSInn.Application\CSInn.Application.csproj", "{9FC69081-2A6A-4990-ACC3-4D1A1D08AA73}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSInn.Discord.Authentication.Tests", "tests\CSInn.Discord.Authentication.Tests\CSInn.Discord.Authentication.Tests.csproj", "{989E16D6-A255-4AC5-BAE1-3D78B38EE258}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSInn.UI.Tests", "tests\CSInn.UI.Tests\CSInn.UI.Tests.csproj", "{2159A971-3858-48F0-99B4-6F40C4F50863}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSInn.Infrastructure.Repositories.Tests", "tests\CSInn.Infrastructure.Repositories.Tests\CSInn.Infrastructure.Repositories.Tests.csproj", "{1B2CDA69-B45A-4F95-B5C9-7AC3F834A7B7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSInn.Models", "src\CSInn.Models\CSInn.Models.csproj", "{A4D369D6-4937-4BB4-8F90-89DBCADE1481}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApp1", "ConsoleApp1\ConsoleApp1.csproj", "{EBBFB2B3-35F1-4B87-B9D1-7260A3825CDC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B6E0D216-88C8-4BA1-A71C-7C6BDB4247BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B6E0D216-88C8-4BA1-A71C-7C6BDB4247BB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B6E0D216-88C8-4BA1-A71C-7C6BDB4247BB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B6E0D216-88C8-4BA1-A71C-7C6BDB4247BB}.Release|Any CPU.Build.0 = Release|Any CPU + {4BA8C0D8-A2CA-4E84-B018-644C2859F5A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4BA8C0D8-A2CA-4E84-B018-644C2859F5A5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4BA8C0D8-A2CA-4E84-B018-644C2859F5A5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4BA8C0D8-A2CA-4E84-B018-644C2859F5A5}.Release|Any CPU.Build.0 = Release|Any CPU + {B92834CE-81D2-4E82-86CA-4940CFB8A102}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B92834CE-81D2-4E82-86CA-4940CFB8A102}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B92834CE-81D2-4E82-86CA-4940CFB8A102}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B92834CE-81D2-4E82-86CA-4940CFB8A102}.Release|Any CPU.Build.0 = Release|Any CPU + {C0BCA68B-7CD7-4037-8DEF-3C1DFA0FFA64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C0BCA68B-7CD7-4037-8DEF-3C1DFA0FFA64}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C0BCA68B-7CD7-4037-8DEF-3C1DFA0FFA64}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C0BCA68B-7CD7-4037-8DEF-3C1DFA0FFA64}.Release|Any CPU.Build.0 = Release|Any CPU + {68360ABC-FFEC-4A14-94EC-DCA6B96C044E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {68360ABC-FFEC-4A14-94EC-DCA6B96C044E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {68360ABC-FFEC-4A14-94EC-DCA6B96C044E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {68360ABC-FFEC-4A14-94EC-DCA6B96C044E}.Release|Any CPU.Build.0 = Release|Any CPU + {9FC69081-2A6A-4990-ACC3-4D1A1D08AA73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9FC69081-2A6A-4990-ACC3-4D1A1D08AA73}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9FC69081-2A6A-4990-ACC3-4D1A1D08AA73}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9FC69081-2A6A-4990-ACC3-4D1A1D08AA73}.Release|Any CPU.Build.0 = Release|Any CPU + {989E16D6-A255-4AC5-BAE1-3D78B38EE258}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {989E16D6-A255-4AC5-BAE1-3D78B38EE258}.Debug|Any CPU.Build.0 = Debug|Any CPU + {989E16D6-A255-4AC5-BAE1-3D78B38EE258}.Release|Any CPU.ActiveCfg = Release|Any CPU + {989E16D6-A255-4AC5-BAE1-3D78B38EE258}.Release|Any CPU.Build.0 = Release|Any CPU + {2159A971-3858-48F0-99B4-6F40C4F50863}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2159A971-3858-48F0-99B4-6F40C4F50863}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2159A971-3858-48F0-99B4-6F40C4F50863}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2159A971-3858-48F0-99B4-6F40C4F50863}.Release|Any CPU.Build.0 = Release|Any CPU + {1B2CDA69-B45A-4F95-B5C9-7AC3F834A7B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1B2CDA69-B45A-4F95-B5C9-7AC3F834A7B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1B2CDA69-B45A-4F95-B5C9-7AC3F834A7B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1B2CDA69-B45A-4F95-B5C9-7AC3F834A7B7}.Release|Any CPU.Build.0 = Release|Any CPU + {A4D369D6-4937-4BB4-8F90-89DBCADE1481}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A4D369D6-4937-4BB4-8F90-89DBCADE1481}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A4D369D6-4937-4BB4-8F90-89DBCADE1481}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A4D369D6-4937-4BB4-8F90-89DBCADE1481}.Release|Any CPU.Build.0 = Release|Any CPU + {EBBFB2B3-35F1-4B87-B9D1-7260A3825CDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EBBFB2B3-35F1-4B87-B9D1-7260A3825CDC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EBBFB2B3-35F1-4B87-B9D1-7260A3825CDC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EBBFB2B3-35F1-4B87-B9D1-7260A3825CDC}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {B6E0D216-88C8-4BA1-A71C-7C6BDB4247BB} = {186B0C3C-02E4-416C-9CD1-067E7662A53E} + {4BA8C0D8-A2CA-4E84-B018-644C2859F5A5} = {186B0C3C-02E4-416C-9CD1-067E7662A53E} + {B92834CE-81D2-4E82-86CA-4940CFB8A102} = {399E33DB-3837-48F0-BFFA-B756D995BD2D} + {C0BCA68B-7CD7-4037-8DEF-3C1DFA0FFA64} = {186B0C3C-02E4-416C-9CD1-067E7662A53E} + {68360ABC-FFEC-4A14-94EC-DCA6B96C044E} = {186B0C3C-02E4-416C-9CD1-067E7662A53E} + {9FC69081-2A6A-4990-ACC3-4D1A1D08AA73} = {186B0C3C-02E4-416C-9CD1-067E7662A53E} + {989E16D6-A255-4AC5-BAE1-3D78B38EE258} = {399E33DB-3837-48F0-BFFA-B756D995BD2D} + {2159A971-3858-48F0-99B4-6F40C4F50863} = {399E33DB-3837-48F0-BFFA-B756D995BD2D} + {1B2CDA69-B45A-4F95-B5C9-7AC3F834A7B7} = {399E33DB-3837-48F0-BFFA-B756D995BD2D} + {A4D369D6-4937-4BB4-8F90-89DBCADE1481} = {186B0C3C-02E4-416C-9CD1-067E7662A53E} + {EBBFB2B3-35F1-4B87-B9D1-7260A3825CDC} = {186B0C3C-02E4-416C-9CD1-067E7662A53E} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6BBE2057-901B-474F-9140-0D045DF18046} + EndGlobalSection +EndGlobal diff --git a/ConsoleApp1/ConsoleApp1.csproj b/ConsoleApp1/ConsoleApp1.csproj new file mode 100644 index 0000000..fb4ca05 --- /dev/null +++ b/ConsoleApp1/ConsoleApp1.csproj @@ -0,0 +1,41 @@ + + + + Exe + netcoreapp3.0 + + + + + + + + + + PreserveNewest + true + PreserveNewest + + + PreserveNewest + true + PreserveNewest + + + + + + + + + + + + + + + + + + + diff --git a/ConsoleApp1/Program - Copy.cs b/ConsoleApp1/Program - Copy.cs new file mode 100644 index 0000000..c3032dc --- /dev/null +++ b/ConsoleApp1/Program - Copy.cs @@ -0,0 +1,34 @@ +using CSInn.Infrastructure.Repositories.Entities; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ConsoleApp1 +{ + class Program_old + { + static void Main_old(string[] args) + { + /* + List test_list = new List() + { + new LessonEntity() { entity_test = "1"}, + new LessonEntity() { entity_test = "2"}, + new LessonEntity() { entity_test = "3"}, + new LessonEntity() { entity_test = "4"}, + new LessonEntity() { entity_test = "5"}, + }; + + + IQueryable EntitySet = (from p in test_list select p).AsQueryable(); + + var dao = new LessonDao(EntitySet); + var dto = new LessonDto(dao); + + var result = dto.findByTest("2"); + Console.WriteLine($"cout: {result.Count}"); + Console.WriteLine($"model_test: {result[0].model_test}"); + */ + } + } +} diff --git a/ConsoleApp1/Program.cs b/ConsoleApp1/Program.cs new file mode 100644 index 0000000..aac2b17 --- /dev/null +++ b/ConsoleApp1/Program.cs @@ -0,0 +1,58 @@ +using CSInn.Infrastructure.Repositories.Context; +using CSInn.Infrastructure.Repositories.Entities; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using CSInn.Infrastructure.Repositories.UoW; +using CSInn.Models; +using Microsoft.Extensions.Configuration; +using System.Configuration; +using System.IO; + +namespace ConsoleApp1 +{ + class Program + { + + static void Main(string[] args) + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); + + IConfigurationRoot configuration = builder.Build(); + + var options = new DbContextOptionsBuilder() + .UseSqlServer(configuration.GetConnectionString("CSINN")) + .Options; + + // .UseInMemoryDatabase(databaseName: "database_name") + // .Options; + // + + var context = new CSInnDbContext(options); + var dbSet = context.Set(); + + if (dbSet.Any()) + Console.WriteLine("has stuff"); + else + { + for (int i = 0; i < 5; ++i) + dbSet.Add(new LessonEntity() { entity_test = "" + i }); + + context.SaveChanges(); + } + + IQueryable EntitySet = dbSet; + var dao = new LessonDao(EntitySet); + var dto = new LessonDto(dao); + + var result = dto.findByTest("2", "tmp"); + + Console.WriteLine($"cout: {result.Count}"); + foreach(var r in result) + Console.WriteLine($"model_test: {r.model_test}"); + } + } +} diff --git a/ConsoleApp1/allthestuff.cs b/ConsoleApp1/allthestuff.cs new file mode 100644 index 0000000..0dd038d --- /dev/null +++ b/ConsoleApp1/allthestuff.cs @@ -0,0 +1,238 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using AutoMapper; +using CSInn.Models; +using CSInn.Infrastructure.Repositories.Entities; + +namespace ConsoleApp1 +{ + public static class Mapper + { + #region basic + public static readonly Dictionary> classMemberMapping = new Dictionary>(); + private static IMapper mapper; + + static Mapper() + { + var config = new AutoMapper.MapperConfiguration(cfg => { + map(cfg) + .bind(src => src.entity_test, dest => dest.model_test); + }); + + mapper = config.CreateMapper(); + } + + private class Mapping + { + public AutoMapper.IMappingExpression map1; + public AutoMapper.IMappingExpression map2; + + private string getMemberName(Expression> ex) + { + if (ex.Body is MemberExpression) + return (ex.Body as MemberExpression).Member.Name; + else if (ex.Body is UnaryExpression) + return ((ex.Body as UnaryExpression).Operand as MemberExpression).Member.Name; + else + return null; + } + public Mapping bind(Expression> left, Expression> right) + { + string leftMember = getMemberName(left); + string rightMember = getMemberName(right); + Dictionary memberMapping = classMemberMapping[typeof(TSource).Name]; + memberMapping[leftMember] = rightMember; + + map2.ForMember(left, opt => opt.MapFrom(right)); + map1.ForMember(right, opt => opt.MapFrom(left)); + return this; + } + } + + private static Mapping map(AutoMapper.IMapperConfigurationExpression cfg) + { + classMemberMapping[typeof(TSource).Name] = new Dictionary(); + + return new Mapping() + { + map1 = cfg.CreateMap(), + map2 = cfg.CreateMap() + }; + } + #endregion + + + public static TModel mapEntityToModel(TEntity entity) where TModel : new() + { + TModel model = new TModel(); + mapper.Map(entity, model); + return model; + } + + public static List mapListEntityToModel(List listEntities) where TModel : new() + { + List listModel = new List(); + foreach (TEntity entity in listEntities) + { + TModel model = mapEntityToModel(entity); + listModel.Add(model); + } + return listModel; + } + + public static TEntity mapModelToEntity(TModel model) where TEntity : new() + { + TEntity entity = new TEntity(); + mapper.Map(model, entity); + return entity; + } + } + + public static class DaoLambdaExtension + { + public static string GetCacheKey() + { + return string.Concat(typeof(TSource).FullName, typeof(TDest).FullName); + } + + public static IEnumerable createMemberBinding(ParameterExpression parameterExpression) + { + string[] SourceExcludedMembers = { "Tags", "Authors" }; + string[] DestExcludedMembers = { "Tags", "Authors" }; + var sourceProperties = typeof(TSource).GetProperties().Where(src => !SourceExcludedMembers.Contains(src.Name)); + var destinationProperties = typeof(TDest).GetProperties().Where(dest => dest.CanWrite & !DestExcludedMembers.Contains(dest.Name)); + Dictionary memberMapping = Mapper.classMemberMapping[typeof(TSource).Name]; + + var bindings = destinationProperties + .Select(destinationProperty => BuildBinding(memberMapping, parameterExpression, destinationProperty, sourceProperties)) + .Where(d => d != null); + + return bindings; + } + + private static bool propertiesEqual(Dictionary memberMapping, string dest, string src) + { + string mapp; + if (!memberMapping.TryGetValue(src, out mapp)) + return false; + return dest == mapp; + } + + private static MemberAssignment BuildBinding(Dictionary memberMapping, + Expression parameterExpression, MemberInfo destinationProperty, IEnumerable sourceProperties) + { + var sourceProperty = sourceProperties.FirstOrDefault(src => src.Name.ToLower() == destinationProperty.Name.ToLower() + || (propertiesEqual(memberMapping, destinationProperty.Name, src.Name))); + + if (sourceProperty == null) + return null; + + return Expression.Bind(destinationProperty, Expression.Property(parameterExpression, sourceProperty)); + } + } + + + public class ProjectionExpression + { + private readonly IQueryable _source; + + private static readonly Dictionary ExpressionCache = new Dictionary(); + + public ProjectionExpression(IQueryable source) + { + _source = source; + } + + public IQueryable To() + { + var queryExpression = GetCachedExpression() ?? BuildExpression(); + + return _source.Select(queryExpression); + } + + private static Expression> GetCachedExpression() + { + var key = DaoLambdaExtension.GetCacheKey(); + + return ExpressionCache.ContainsKey(key) ? ExpressionCache[key] as Expression> : null; + } + + private static Expression> BuildExpression() + { + var parameterExpression = Expression.Parameter(typeof(TSource), "src"); + var bindings = DaoLambdaExtension.createMemberBinding(parameterExpression); + + var expression = Expression.Lambda>(Expression.MemberInit(Expression.New(typeof(TDest)), bindings), parameterExpression); + + var key = DaoLambdaExtension.GetCacheKey(); + ExpressionCache.Add(key, expression); + + return expression; + } + } + + public static class ObjectSetExtensions + { + public static ProjectionExpression Project( + this IQueryable source) + { + return new ProjectionExpression(source); + } + } + + public abstract class BasicDao + { + protected string entitySetName; + public IQueryable EntitySet; + } + + public class LessonDao : BasicDao + { + public LessonDao(IQueryable EntitySet) + { + this.EntitySet = EntitySet; + } + } + + + public abstract class BasicDto + where TEntity : new() + where TModel : new() + { + protected BasicDao dao; + + public BasicDto(BasicDao dao) { this.dao = dao; } + + public IQueryable get() + { + return dao.EntitySet.Project().To(); + } + } + + public class LessonDto : BasicDto + { + public LessonDto(BasicDao dao) + : base(dao) + { } + + public List findByTest(string test, string desc) + { + var lessonModelList = + /* + (from p in get() + where !p.model_test.Equals(test) + select p) + */ + get() + .Where(p => !p.model_test.Equals(test)) + .Where(p => p.Description == desc) + .ToList(); + + + return lessonModelList; + } + } +} diff --git a/ConsoleApp1/appsettings.Development.json b/ConsoleApp1/appsettings.Development.json new file mode 100644 index 0000000..e203e94 --- /dev/null +++ b/ConsoleApp1/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/ConsoleApp1/appsettings.json b/ConsoleApp1/appsettings.json new file mode 100644 index 0000000..3267a7c --- /dev/null +++ b/ConsoleApp1/appsettings.json @@ -0,0 +1,13 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "CSINN": "Server=.;Database=CSINN;Trusted_Connection=True;MultipleActiveResultSets=true" + } +} diff --git a/src/CSInn.Infrastructure.Repositories/Entities/LessonEntity.cs b/src/CSInn.Infrastructure.Repositories/Entities/LessonEntity.cs index 4478a63..17d06a4 100644 --- a/src/CSInn.Infrastructure.Repositories/Entities/LessonEntity.cs +++ b/src/CSInn.Infrastructure.Repositories/Entities/LessonEntity.cs @@ -2,6 +2,7 @@ { public class LessonEntity { + public int Id { get; set; } public string Title { get; set; } public string Description { get; set; } @@ -10,5 +11,7 @@ public class LessonEntity // EF doesn't like a list- fair point, but how do we solve this? public string Tags { get; set; } public string Authors { get; set; } + + public string entity_test { get; set; } } } diff --git a/src/CSInn.Infrastructure.Repositories/UoW/CSInnDbContext.cs b/src/CSInn.Infrastructure.Repositories/UoW/CSInnDbContext.cs index 052a25e..8daa0fb 100644 --- a/src/CSInn.Infrastructure.Repositories/UoW/CSInnDbContext.cs +++ b/src/CSInn.Infrastructure.Repositories/UoW/CSInnDbContext.cs @@ -13,7 +13,6 @@ public CSInnDbContext(DbContextOptions options) : base(options) { } - public virtual DbSet Lessons { get; set; } } } diff --git a/src/CSInn.Models/Lesson.cs b/src/CSInn.Models/Lesson.cs index 889deaa..4967c7b 100644 --- a/src/CSInn.Models/Lesson.cs +++ b/src/CSInn.Models/Lesson.cs @@ -16,6 +16,8 @@ public class Lesson public IList Tags { get; set; } public IList Authors { get; set; } + public string model_test { get; set; } + public Lesson(string title, string description, IEnumerable tags, IEnumerable authors) { Title = title; @@ -34,6 +36,8 @@ public Lesson(string title, string description, IEnumerable tags, IEnume } } + + public Lesson() { } } }