forked from JonPSmith/EfCore.GenericServices
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
41 changed files
with
1,498 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>netcoreapp2.0</TargetFramework> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.0.1" /> | ||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="2.0.1" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\GenericLibsBase\GenericLibsBase.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// Copyright (c) 2018 Jon P Smith, GitHub: JonPSmith, web: http://www.thereformedprogrammer.net/ | ||
// Licensed under MIT licence. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using DataLayer.EfClasses; | ||
|
||
namespace DataLayer.Dtos | ||
{ | ||
public struct OrderBooksDto | ||
{ | ||
public int BookId { get; } | ||
public Book ChosenBook { get; } | ||
public short numBooks { get; } | ||
|
||
public OrderBooksDto(int bookId, Book chosenBook, short numBooks) : this() | ||
{ | ||
BookId = bookId; | ||
ChosenBook = chosenBook ?? throw new ArgumentNullException(nameof(chosenBook)); | ||
this.numBooks = numBooks; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// Copyright (c) 2018 Jon P Smith, GitHub: JonPSmith, web: http://www.thereformedprogrammer.net/ | ||
// Licensed under MIT licence. See License.txt in the project root for license information. | ||
|
||
using System.Collections.Generic; | ||
using System.ComponentModel.DataAnnotations; | ||
|
||
namespace DataLayer.EfClasses | ||
{ | ||
public class Author | ||
{ | ||
public const int NameLength = 100; | ||
|
||
//used by EF Core | ||
private Author() { } | ||
|
||
public Author(string name) | ||
{ | ||
Name = name; | ||
} | ||
|
||
public int AuthorId { get; private set; } | ||
|
||
[Required] | ||
[MaxLength(NameLength)] | ||
public string Name { get; private set; } | ||
|
||
//------------------------------ | ||
//Relationships | ||
|
||
public ICollection<BookAuthor> | ||
BooksLink | ||
{ get; set; } | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
// Copyright (c) 2018 Jon P Smith, GitHub: JonPSmith, web: http://www.thereformedprogrammer.net/ | ||
// Licensed under MIT licence. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.ComponentModel.DataAnnotations; | ||
using System.Linq; | ||
using GenericLibsBase; | ||
using Microsoft.EntityFrameworkCore; | ||
|
||
namespace DataLayer.EfClasses | ||
{ | ||
public class Book | ||
{ | ||
public const int PromotionalTextLength = 200; | ||
private HashSet<BookAuthor> _authorsLink; | ||
|
||
//----------------------------------------------- | ||
//relationships | ||
|
||
//Use uninitialised backing fields - this means we can detect if the collection was loaded | ||
private HashSet<Review> _reviews; | ||
|
||
//----------------------------------------------- | ||
//ctors | ||
|
||
private Book() { } | ||
|
||
public Book(string title, string description, DateTime publishedOn, | ||
string publisher, decimal price, string imageUrl, ICollection<Author> authors) | ||
{ | ||
if (string.IsNullOrWhiteSpace(title)) | ||
throw new ArgumentNullException(nameof(title)); | ||
|
||
Title = title; | ||
Description = description; | ||
PublishedOn = publishedOn; | ||
Publisher = publisher; | ||
ActualPrice = price; | ||
OrgPrice = price; | ||
ImageUrl = imageUrl; | ||
_reviews = new HashSet<Review>(); //We add an empty list on create. I allows reviews to be added when building test data | ||
|
||
if (authors == null || !authors.Any()) | ||
throw new ArgumentException("You must have at least one Author for a book", nameof(authors)); | ||
byte order = 0; | ||
_authorsLink = new HashSet<BookAuthor>(authors.Select(a => new BookAuthor(this, a, order++))); | ||
} | ||
|
||
public int BookId { get; private set; } | ||
public string Title { get; private set; } | ||
public string Description { get; private set; } | ||
public DateTime PublishedOn { get; private set; } | ||
public string Publisher { get; private set; } | ||
public decimal OrgPrice { get; private set; } | ||
public decimal ActualPrice { get; private set; } | ||
|
||
[MaxLength(PromotionalTextLength)] | ||
public string PromotionalText { get; private set; } | ||
|
||
public string ImageUrl { get; private set; } | ||
|
||
public IEnumerable<Review> Reviews => _reviews?.ToList(); | ||
public IEnumerable<BookAuthor> AuthorsLink => _authorsLink?.ToList(); | ||
|
||
public void UpdatePublishedOn(DateTime newDate) | ||
{ | ||
PublishedOn = newDate; | ||
} | ||
|
||
public void AddReview(int numStars, string comment, string voterName, | ||
DbContext context = null) | ||
{ | ||
if (_reviews != null) | ||
{ | ||
_reviews.Add(new Review(numStars, comment, voterName)); | ||
} | ||
else if (context == null) | ||
{ | ||
throw new ArgumentNullException(nameof(context), | ||
"You must provide a context if the Reviews collection isn't valid."); | ||
} | ||
else if (context.Entry(this).IsKeySet) | ||
{ | ||
context.Add(new Review(numStars, comment, voterName, BookId)); | ||
} | ||
else | ||
{ | ||
throw new InvalidOperationException("Could not add a new review."); | ||
} | ||
} | ||
|
||
public void RemoveReview(Review review) | ||
{ | ||
if (_reviews == null) | ||
throw new NullReferenceException("You must use .Include(p => p.Reviews) before calling this method."); | ||
|
||
_reviews.Remove(review); | ||
} | ||
|
||
public IStatusGeneric AddPromotion(decimal newPrice, string promotionalText) | ||
{ | ||
var status = new StatusGenericHandler(); | ||
if (string.IsNullOrWhiteSpace(promotionalText)) | ||
{ | ||
status.AddError("You must provide some text to go with the promotion.", nameof(PromotionalText)); | ||
return status; | ||
} | ||
|
||
ActualPrice = newPrice; | ||
PromotionalText = promotionalText; | ||
return status; | ||
} | ||
|
||
public void RemovePromotion() | ||
{ | ||
ActualPrice = OrgPrice; | ||
PromotionalText = null; | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// Copyright (c) 2018 Jon P Smith, GitHub: JonPSmith, web: http://www.thereformedprogrammer.net/ | ||
// Licensed under MIT licence. See License.txt in the project root for license information. | ||
namespace DataLayer.EfClasses | ||
{ | ||
public class BookAuthor | ||
{ | ||
private BookAuthor() { } | ||
|
||
internal BookAuthor(Book book, Author author, byte order) | ||
{ | ||
Book = book; | ||
Author = author; | ||
Order = order; | ||
} | ||
|
||
public int BookId { get; private set; } | ||
public int AuthorId { get; private set; } | ||
public byte Order { get; private set; } | ||
|
||
//----------------------------- | ||
//Relationships | ||
|
||
public Book Book { get; private set; } | ||
public Author Author { get; private set; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
// Copyright (c) 2018 Jon P Smith, GitHub: JonPSmith, web: http://www.thereformedprogrammer.net/ | ||
// Licensed under MIT licence. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.ComponentModel.DataAnnotations; | ||
|
||
namespace DataLayer.EfClasses | ||
{ | ||
public class LineItem : IValidatableObject | ||
{ | ||
internal LineItem(short numBooks, Book chosenBook, byte lineNum) | ||
{ | ||
NumBooks = numBooks; | ||
ChosenBook = chosenBook ?? throw new ArgumentNullException(nameof(chosenBook)); | ||
BookPrice = chosenBook.ActualPrice; | ||
LineNum = lineNum; | ||
} | ||
|
||
/// <summary> | ||
/// Used by EF Core | ||
/// </summary> | ||
private LineItem() { } | ||
|
||
public int LineItemId { get; private set; } | ||
|
||
[Range(1,5, ErrorMessage = | ||
"This order is over the limit of 5 books.")] | ||
public byte LineNum { get; private set; } | ||
|
||
public short NumBooks { get; private set; } | ||
|
||
/// <summary> | ||
/// This holds a copy of the book price. We do this in case the price of the book changes, | ||
/// e.g. if the price was discounted in the future the order is still correct. | ||
/// </summary> | ||
public decimal BookPrice { get; private set; } | ||
|
||
// relationships | ||
|
||
public int OrderId { get; private set; } | ||
public int BookId { get; private set; } | ||
|
||
public Book ChosenBook { get; private set; } | ||
|
||
/// <summary> | ||
/// Extra validation rules: These are checked by using the SaveChangesWithValidation method when saving to the database | ||
/// </summary> | ||
/// <param name="validationContext"></param> | ||
/// <returns></returns> | ||
IEnumerable<ValidationResult> IValidatableObject.Validate | ||
(ValidationContext validationContext) | ||
{ | ||
if (BookPrice < 0) | ||
yield return new ValidationResult($"Sorry, the book '{ChosenBook.Title}' is not for sale."); | ||
|
||
if (NumBooks > 100) | ||
yield return new ValidationResult("If you want to order a 100 or more books"+ | ||
" please phone us on 01234-5678-90", | ||
new[] { nameof(NumBooks) }); | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
// Copyright (c) 2018 Jon P Smith, GitHub: JonPSmith, web: http://www.thereformedprogrammer.net/ | ||
// Licensed under MIT licence. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using DataLayer.Dtos; | ||
using GenericLibsBase; | ||
|
||
namespace DataLayer.EfClasses | ||
{ | ||
public class Order | ||
{ | ||
private HashSet<LineItem> _lineItems; | ||
|
||
|
||
private Order() { } | ||
|
||
public int OrderId { get; private set; } | ||
public DateTime DateOrderedUtc { get; private set; } | ||
public DateTime ExpectedDeliveryDate { get; private set; } | ||
public bool HasBeenDelivered { get; private set; } | ||
public string CustomerName { get; private set; } | ||
|
||
// relationships | ||
|
||
public IEnumerable<LineItem> LineItems => _lineItems?.ToList(); | ||
|
||
public string OrderNumber => $"SO{OrderId:D6}"; | ||
|
||
public static IStatusGeneric<Order> CreateOrderFactory( | ||
string customerName, DateTime expectedDeliveryDate, | ||
IEnumerable<OrderBooksDto> bookOrders) | ||
{ | ||
var status = new StatusGenericHandler<Order>(); | ||
var order = new Order | ||
{ | ||
CustomerName = customerName, | ||
ExpectedDeliveryDate = expectedDeliveryDate, | ||
DateOrderedUtc = DateTime.UtcNow, | ||
HasBeenDelivered = expectedDeliveryDate < DateTime.Today | ||
}; | ||
|
||
byte lineNum = 1; | ||
order._lineItems = new HashSet<LineItem>(bookOrders | ||
.Select(x => new LineItem(x.numBooks, x.ChosenBook, lineNum++))); | ||
if (!order._lineItems.Any()) | ||
status.AddError("No items in your basket."); | ||
|
||
status.Result = order; | ||
return status; | ||
} | ||
|
||
public IStatusGeneric ChangeDeliveryDate(string userId, DateTime newDeliveryDate) | ||
{ | ||
var status = new StatusGenericHandler(); | ||
if (CustomerName != userId) | ||
{ | ||
status.AddError("I'm sorry, but that order does not belong to you"); | ||
//Log a security issue | ||
return status; | ||
} | ||
|
||
if (HasBeenDelivered) | ||
{ | ||
status.AddError("I'm sorry, but that order has been delivered."); | ||
return status; | ||
} | ||
|
||
if (newDeliveryDate < DateTime.Today.AddDays(1)) | ||
{ | ||
status.AddError("I'm sorry, we cannot get the order to you that quickly. Please choose a new date.", "NewDeliveryDate"); | ||
return status; | ||
} | ||
|
||
if (newDeliveryDate.DayOfWeek == DayOfWeek.Sunday) | ||
{ | ||
status.AddError("I'm sorry, we don't deliver on Sunday. Please choose a new date.", "NewDeliveryDate"); | ||
return status; | ||
} | ||
|
||
//All Ok | ||
ExpectedDeliveryDate = newDeliveryDate; | ||
return status; | ||
} | ||
} | ||
} |
Oops, something went wrong.