Showing posts with label Entity framework core. Show all posts
Showing posts with label Entity framework core. Show all posts

Saturday, August 15, 2020

The Repository-Service Pattern with DI and ASP.NET Core - Very good 15 min task

 I do a lot of application design and architecture, and a rewrite project I'm heading up needed an architecture sufficiently designed to handle a set of complex requirements. What I was came up with is not new, and has been demoed and used many times before, but after a coworker told me he'd never heard of it, it occurred to me that I hadn't written a post about it yet, so here we are.

In this post, we'll be discussing the Repository-Service Pattern, a name I applied to a software architecture pattern that has been in use for quite some time. We'll take a look at how this pattern might be implemented in a real app, and discuss some advantages and one big disadvantage the pattern has.  

Let's get started!

Overview

The Repository-Service pattern breaks up the business layer of the app into two distinct layers.

  • The lower layer is the Repositories. These classes handle getting data into and out of our data store, with the important caveat that each Repository only works against a single Model class. So, if your models are Dogs, Cats, and Rats, you would have a Repository for each, the DogRepository would not call anything in the CatRepository, and so on.
  • The upper layer is the Services. These classes can query multiple Repository classes and combine their data to form new, more complex business objects. Further, they introduce a layer of abstraction between the web application and the Repositories so that they can change more independently.

The Sample App Concept

Let's pretend we will model a day's sales and profits at a local movie theatre.

A woman sits alone in a darkened movie theatre.
Either a terrible movie or the best movie ever. Photo by Karen Zhao / Unsplash

Movie theatres make money from two major sources: ticket sales and food sales. We want to build an app that can both display the tickets and food items sold, as well as generate some simple statistics about how much sales we had that day.

The Goals

By the end of this post, we will have a sample application which can do the following:

  • Display all food items sold.
  • Display all tickets sold.
  • Display the average profit per ticket and average profit per food item on every page of the app.

The Sample Project

As with many of my blog posts, this one has a sample project over on GitHub that shows the complete code used. Check it out!

exceptionnotfound/RespositoryServicePatternDemo
Contribute to exceptionnotfound/RespositoryServicePatternDemo development by creating an account on GitHub.

Building the Models

NOTE: This project is built in ASP.NET Core 3.0 using MVC architecture. All code samples in this post have been simplified. For the full code, check out the sample project on GitHub.

First, let's understand what kind of models we want to work with. Here's the sample model objects for a food item and a ticket:

public class FoodItem
{
    public int ID { get; set; }
    public string Name { get; set; }
    public decimal SalePrice { get; set; }
    public decimal UnitPrice { get; set; }
    public int Quantity { get; set; }
    public decimal Profit
    {
        get
        {
            return (SalePrice * Quantity) - (UnitPrice * Quantity);
        }
    }
}
public class Ticket
{
    public int ID { get; set; }
    public string MovieName { get; set; }
    public decimal SalePrice { get; set; }
    public decimal StudioCutPercentage { get; set; }
    public int Quantity { get; set; }
    public decimal Profit
    {
        get
        {
            return (Quantity * SalePrice) 
                   - (StudioCutPercentage * (Quantity * SalePrice));
        }
    }
    public decimal ProfitPerItem
    {
        get
        {
            return SalePrice - (StudioCutPercentage * SalePrice);
        }
    }
}

We will also need a simple model to represent the financial statistics:

public class FinancialStats
{
    public decimal AverageTicketProfit { get; set; }
    public decimal AverageFoodItemProfit { get; set; }
}

With these models in place, we can start building the lowest layer of this pattern: the Repository layer.

Building the Repositories

The Repositories are intended to deal with operations for a single business model. This commonly includes CRUD functionality, and might also include more complex methods (e.g. querying for a collection of objects, or running stats against said collection).  

It follows that because we have two business models, we need two repositories. Here's the repository for FoodItem:

public interface IFoodRepository
{
    List<FoodItem> GetAllSold();
}

public class FoodRepository : IFoodRepository
{
    public List<FoodItem> GetAllSold()
    {
        //In a real project, this is where you 
        //would call your database/datastore for this info
        List<FoodItem> items = new List<FoodItem>()
        {
            new FoodItem()
            {
                ID = 14,
                Name = "Milk Duds",
                SalePrice = 4.99M,
                UnitPrice = 1.69M,
                Quantity = 43
            },
            new FoodItem()
            {
                ID = 3,
                Name = "Sour Gummy Worms",
                SalePrice = 4.89M,
                UnitPrice = 1.13M,
                Quantity = 319
            },
            new FoodItem()
            {
                ID = 18,
                Name = "Large Soda",
                SalePrice = 5.69M,
                UnitPrice = 0.47M,
                Quantity = 319
            },
            new FoodItem()
            {
                ID = 19,
                Name = "X-Large Soda",
                SalePrice = 6.19M,
                UnitPrice = 0.59M,
                Quantity = 252
            },
            new FoodItem()
            {
                ID = 1,
                Name = "Large Popcorn",
                SalePrice = 5.59M,
                UnitPrice = 1.12M,
                Quantity = 217
            }
        };

        return items;
    }
}

The TicketRepository looks similar:

public interface ITicketRepository
{
    List<Ticket> GetAllSold();
}

public class TicketRepository : ITicketRepository
{
    public List<Ticket> GetAllSold()
    {
        List<Ticket> tickets = new List<Ticket>()
        {
            new Ticket()
            {
                ID = 1953772,
                MovieName = "Joker",
                SalePrice = 8.99M,
                StudioCutPercentage = 0.75M,
                Quantity = 419
            },
            new Ticket()
            {
                ID = 2817721,
                MovieName = "Toy Story 4",
                SalePrice = 7.99M,
                StudioCutPercentage = 0.9M,
                Quantity = 112
            },
            new Ticket()
            {
                ID = 2177492,
                MovieName = "Hustlers",
                SalePrice = 8.49M,
                StudioCutPercentage = 0.67M,
                Quantity = 51
            },
            new Ticket()
            {
                ID = 2747119,
                MovieName = "Downton Abbey",
                SalePrice = 8.99M,
                StudioCutPercentage = 0.72M,
                Quantity = 214
            }
        };

        return tickets;
    }
}

There's nothing complex about these repositories; all they do is query the data store (in our case, the data store doesn't exist and we are mocking the results) and return objects. The real complexity starts in the next layer, where we will build the Service classes.

Building the Services

Recall that the Service classes are designed to do two things:

  1. Inherit from the Repository classes AND
  2. Implement their own functionality, which is only necessary when said functionality deals with more than one business object.

As of yet, the only functionality we have is getting the sold Tickets and Food for the day; it isn't very complicated. Consequently the TicketService and FoodService classes are very simple:

public interface IFoodService : IFoodRepository { }
public class FoodService : FoodRepository, IFoodService { }

public interface ITicketService : ITicketRepository { }
public class TicketService : TicketRepository, ITicketService { }

But we need to keep in mind our Goal #3 from earlier, which is that we want to display the average item profit for both tickets and food items on every page of the app. In order to do this, we will need a method which queries both FoodItems and Tickets, and because the Repository-Service Pattern says Repositories cannot do this, we need to create a new Service for this functionality.

To accomplish this we need a new service class, one that queries both Food and Ticket repositories and constructs a complex object. Here's the new FinancialsService class and corresponding interface:

public interface IFinancialsService
{
    FinancialStats GetStats();
}

public class FinancialsService : IFinancialsService
{
    private readonly ITicketRepository _ticketRepo;
    private readonly IFoodRepository _foodRepo;

    public FinancialsService(ITicketRepository ticketRepo,
                                IFoodRepository foodRepo)
    {
        _ticketRepo = ticketRepo;
        _foodRepo = foodRepo;
    }

    public FinancialStats GetStats()
    {
        FinancialStats stats = new FinancialStats();
        var foodSold = _foodRepo.GetAllSold();
        var ticketsSold = _ticketRepo.GetAllSold();

        //Calculate Average Stats
        stats.AverageTicketProfit = 
          ticketsSold.Sum(x => x.Profit) / ticketsSold.Sum(x => x.Quantity);
        stats.AverageFoodItemProfit = 
          foodSold.Sum(x => x.Profit) / foodSold.Sum(x => x.Quantity);

        return stats;
    }
}

That completes our Services layer! Next we will create the Controllers layer, which is to say, we will create a new ASP.NET Core Web App.

Building the Controllers

Here's a simple FoodController class (the corresponding view is on GitHub):

public class FoodController : Controller
{
    private readonly IFoodService _foodService;

    public FoodController(IFoodService foodService)
    {
        _foodService = foodService;
    }

    public IActionResult Index()
    {
        var itemsSold = _foodService.GetAllSold();
        return View(itemsSold);
    }
}

The TicketController looks very similar:

public class TicketController : Controller
{
    private readonly ITicketService _ticketService;

    public TicketController(ITicketService ticketService)
    {
        _ticketService = ticketService;
    }

    public IActionResult Index()
    {
        var tickets = _ticketService.GetAllSold();
        return View(tickets);
    }
}

These two controllers and their actions give us a way to see the Tickets and Food Items sold, which accomplishes two of our goals. Here's a screenshot of the Food Items page:

And a Diet Coke, please.

We still have our Goal #3 to do, though. In order to see these stats on every page, we're going to create a new View Component.

Building the FinancialStatsViewComponent

A View Component in ASP.NET Core MVC consists of multiple parts. The first and most important part is a class, which implements the ViewComponent class. Our class looks like this:

public class FinancialStatsViewComponent : ViewComponent
{
    private readonly IFinancialsService _financialService;

    public FinancialStatsViewComponent(IFinancialsService financialService)
    {
        _financialService = financialService;
    }

    public Task<IViewComponentResult> InvokeAsync()
    {
        var stats = _financialService.GetStats();
        return Task.FromResult<IViewComponentResult>(View(stats));
    }
}

We also need a corresponding view, which will need to be located at ~/Views/Shared/Components/FinancialStats/Default.cshtml:

@model RepositoryServicePatternDemo.Core.Models.FinancialStats

<ul>
    <li class="d-inline-block">
        <strong>@Html.DisplayNameFor(x => x.AverageFoodItemProfit)</strong>
        @Html.DisplayFor(x => x.AverageFoodItemProfit)
    </li>
    <li class="d-inline-block">
        <strong>@Html.DisplayNameFor(x => x.AverageTicketProfit)</strong>
        @Html.DisplayFor(x => x.AverageTicketProfit)
    </li>
</ul>

Finally, we need to invoke this component on the _Layout view:

...
<div class="container">
    <main role="main" class="pb-3">
        @await Component.InvokeAsync("FinancialStats")
        @RenderBody()
    </main>
</div>
...

All of this results in the stats being visible on every page in the app, such as the Ticket Sales page:

That red box is there precisely so you won't notice the ASCII dragon until it's too late.

Ta-da!  We have accomplished out goals!  Time to celebrate with some movie candy!

Close up of sour gummy worms, all brightly colored.
First they're sour, then they're sweet, then they're stolen by my kids. Photo by Sylvanus Urban / Unsplash

Architecture Diagram

Here's the architecture diagram for the project we ended up building:

The diagram points out the major benefit to using this pattern: clear and consistent separation between the layers of the architecture. This gives us the ability to change one layer with minimal impact to the others, and with clear direction as to what each layer contains, we can do so quickly and with a minimum of code.

Drawback of This Pattern

The Repository-Service Pattern is a great pattern for situations in which you need to query for data from a complex data store or need some layers of separation between what happens for single models vs combinations of models. That said, it has one primary drawback that needs to be taken into account.

That drawback is simply this: it's a LOT of code, some of which might be totally unnecessary. The TicketService and FoodService classes from earlier do nothing except inherit from their corresponding Repositories. You could just as easily remove these classes and have the Repositories injected into the Controllers.

I personally will argue that any real-world app will be sufficiently complicated so as to warrant the additional Service layer, but it's not a hill I'll die on.

Summary

The Repository-Service Pattern is a great way to architect a real-world, complex application. Each of the layers (Repository and Service) have a well defined set of concerns and abilities, and by keeping the layers intact we can create an easily-modified, maintainable program architecture. There is one major drawback, but in my opinion it doesn't impact the pattern enough to stop using it.

That's all for this post! Don't forget to check out the sample project over on GitHub! Also, feel free to ask questions or submit improvements either on the comments in this post or on the project repository. I would love to hear my dear readers' opinions on this pattern and how, or if, they are using it in their real-world apps.

Finally, if this post helped you learn about the usage of the Repository-Service pattern, please consider buying me a coffee. Your support funds all of my projects and helps me keep traditional ads off this site. Thank you very much!

Happy Coding!

Thursday, August 13, 2020

EF Core Relationships in ASP .NET Core 3.1

 

This is the fifth of a new series of posts on ASP .NET Core 3.1 for 2020. In this series, we’ll cover 26 topics over a span of 26 weeks from January through June 2020, titled ASP .NET Core A-Z! To differentiate from the 2019 series, the 2020 series will mostly focus on a growing single codebase (NetLearner!) instead of new unrelated code snippets week.

Previous post:

NetLearner on GitHub:

In this Article:

E is for EF Core Relationships

In my 2018 series, we covered EF Core Migrations to explain how to add, remove and apply Entity Framework Core Migrations in an ASP .NET Core web application project. In this article, we’ll continue to look at the newer 2020 NetLearner project, to identify entities represented by C# model classes and the relationships between them.

NOTE: Please note that NetLearner is a work in progress as of this writing, so its code is subject to change. The UI web apps still needs work (and will be updated at a later date) but the current version has the following models with the relationships shown below:

NetLearner database diagram
NetLearner database diagram

Classes and Relationships

The heart of the application is the LearningResource class. This represents any online learning resource, such as a blog post, single video, podcast episode, ebook, etc that can be accessed with a unique URL.

public class LearningResource
{
    public int Id { get; set; }

    [DisplayName("Resource")]
    public string Name { get; set; }


    [DisplayName("URL")]
    [DataType(DataType.Url)]
    public string Url { get; set; }

    public int ResourceListId { get; set; }
    [DisplayName("In List")]
    public ResourceList ResourceList { get; set; }

    public ContentFeed ContentFeed { get; set; }

    public List<LearningResourceTopicTag> LearningResourceTopicTags { get; set; }
} 

The ContentFeed class represents the RSS Feed (or channel information) for an online resource, a URL that can be used to retrieve more information about the online resource, if available.

public class ContentFeed
{
    public int Id { get; set; }

    [DisplayName("Feed URL")]
    public string FeedUrl { get; set; }

    public int LearningResourceId { get; set; }
    public LearningResource LearningResource { get; set; }
}

The ResourceList class represents a logical container for learning resources in the system. It is literally a list of items, where the items are your learning resources.

public class ResourceList
{
    public int Id { get; set; }

    public string Name { get; set; }

    public List<LearningResource> LearningResources { get; set; }
} 

The TopicTag class represents a single “tag” value that can be used to categorize online resources. Possibly values could be “.NET Core”, “ASP.NET Core” and so on.

public class TopicTag
{
    public int Id { get; set; }

    [DisplayName("Tag")]
    public string TagValue { get; set; }

    public List<LearningResourceTopicTag> LearningResourceTopicTags { get; set; }
} 

At this point, you may have noticed both the LearningResource and TopicTag classes contain a List<T> property of LearningResourceTopicTag. If you browse the database diagram, you will notice that this table appears as a connection between the two aforementioned tables, to establish a many-to-many relationship. (more on this later)

The following diagram shows an example of how the a LearningResource (e.g. link to a doc/video) is a part of a ResourceList, while each LearningResource also has a link back to its root site, channel or RSS feed (via ContentFeed).

One to One

Having looked through the above entities and relationships, we can see that each LearningResource has a ContentFeed. This is an example of a 1-to-1 relationship. For example:

  • Learning Resource = Wake Up and Code! blog site
  • Content Feed = RSS Feed for blog site

In the two classes, we see the following code:

public class LearningResource
{
public int Id { get; set; }

[DisplayName("Resource")]
public string Name { get; set; }


[DisplayName("URL")]
[DataType(DataType.Url)]
public string Url { get; set; }

public int ResourceListId { get; set; }
[DisplayName("In List")]
public ResourceList ResourceList { get; set; }

public ContentFeed ContentFeed { get; set; }

public List<LearningResourceTopicTag> LearningResourceTopicTags { get; set; }
}
public class ContentFeed
{
public int Id { get; set; }

[DisplayName("Feed URL")]
public string FeedUrl { get; set; }

public int LearningResourceId { get; set; }
public LearningResource LearningResource { get; set; }
}

Each Learning Resource has a corresponding Content Feed, so the LearningResource class has a property for ContentFeed. That’s pretty simple. But in the  ContentFeed class, you don’t necessarily need a property pointing back to the  LearningResource . In fact, all you need is a  LearningResourceId property. EF Core will ensure that LearningResource.Id points to ContentFeed.LearningResourceId in the database. But to help with object-property navigation in your code, it is useful to include an actual LearningResource object in the ContentFeed class to point back to LearningResource.

One to One Relationship
One to One Relationship

Another way of looking at the One-to-One relationship is to view the constraints of each database entity in the visuals below. Note that both tables have an Id field that is a Primary Key (inferred by EF Core) while the ContentFeeds table also has a Foreign Key for the LearningResourceId field used for the constraint in the relationship.

LearningResources table
LearningResources table
ContentFeeds table
ContentFeeds table
One to One Relationship
One to One Relationship

One to Many

Next, let’s take a look at the One-to-Many relationship for each ResourceList that has zero or more LearningResources. For example:

  • Resource List = ASP .NET Core Blogs (parent container)
  • Learning Resource = ASP .NET Core A-Z Blog Series (single URL)

In the two classes, we see the following code:

public class ResourceList
{
public int Id { get; set; }

public string Name { get; set; }

public List<LearningResource> LearningResources { get; set; }
}
public class LearningResource
{
public int Id { get; set; }

[DisplayName("Resource")]
public string Name { get; set; }


[DisplayName("URL")]
[DataType(DataType.Url)]
public string Url { get; set; }

public int ResourceListId { get; set; }
[DisplayName("In List")]
public ResourceList ResourceList { get; set; }

public ContentFeed ContentFeed { get; set; }

public List<LearningResourceTopicTag> LearningResourceTopicTags { get; set; }
}

Each Resource List has zero or more Learning Resources, so the ResourceList class has a List<T> property for LearningResources. This is even simpler than the previously described 1-to-1 relationship. In the LearningResource class, you don’t necessarily need a property pointing back to the ResourceList. But once again, to help with object-property navigation in your code, it is useful to include an actual ResourceList object in the LearningResource class to point back to ResourceList.

One to Many Relationship
One to Many Relationship

Another way of looking at the One-to-Many relationship is to view the constraints of each database entity in the visuals below. Note that both tables have an Id field that is a Primary Key (once again, inferred by EF Core) while the ResourceLists table also has a Foreign Key for the  ResourceListsId field used for the constraint in the relationship.

One to Many Constraint
One to Many Constraint

Many to Many

Finally, let’s also take a look at a Many-to-Many relationship, for each TopicTag and LearningResource, either of which can have many of the other. For example:

  • Topic Tag = “ASP .NET Core” (tag as a text description)
  • Learning Resource = Specific blog post on site (single URL)

This relationship is a little more complicated than all of the above, as we will need a “join table” to connect the two tables in question. Not only that, we will have to describe the entity in the C# code with connections to both tables we would like to connect with this relationship.

In the two classes we would like to connect, we see the following code:

public class TopicTag
{
    public int Id { get; set; }

    [DisplayName("Tag")]
    public string TagValue { get; set; }

    public List<LearningResourceTopicTag> LearningResourceTopicTags { get; set; }
} 
public class LearningResource
{
public int Id { get; set; }

[DisplayName("Resource")]
public string Name { get; set; }


[DisplayName("URL")]
[DataType(DataType.Url)]
public string Url { get; set; }

public int ResourceListId { get; set; }
[DisplayName("In List")]
public ResourceList ResourceList { get; set; }

public ContentFeed ContentFeed { get; set; }

public List<LearningResourceTopicTag> LearningResourceTopicTags { get; set; }
}

Next, we have the LearningResourceTopicTag class as a “join entity” to connect the above:

public class LearningResourceTopicTag
{
    public int LearningResourceId { get; set; }
    public LearningResource LearningResource { get; set; }

    public int TopicTagId { get; set; }
    public TopicTag TopicTag { get; set; }

}

This special class has the following properties:

  • LearningResourceId: integer value, pointing back to LearningResource.Id
  • LearningResource: optional “navigation” property, reference back to connected LearningResource entity
  • TopicTagId: integer value, pointing back to TopicTag.Id
  • TopicTag:  optional “navigation” property, reference back to connected TopicTag entity

To learn more about navigation properties, check out the official docs at:

Many to Many Relationship
Many to Many Relationship

Another way of looking at the Many-to-Many relationship is to view the constraints of each database entity in the visuals below. Note that the two connected tables both have an Id field that is a Primary Key (yes, inferred by EF Core!) while the LearningResourceTopicTag table has a Composite Key for the TopicTagId and LearningResourceId fields used for the constraints in the relationship.

Constraints for LearningResources
Constraints for LearningResources
 Constraints for TopicTags
Constraints for TopicTags

The composite key is described in the LibDbContext class inside the OnModelCreating() method:

public class LibDbContext : IdentityDbContext
{
    ...
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
       ...
       modelBuilder.Entity<LearningResourceTopicTag>()
        .HasKey(lrtt => new { lrtt.LearningResourceId, lrtt.TopicTagId });
    }
}

Here, the HasKey() method informs EF Core that the entity LearningResourceTopicTag has a composite key defined by both LearningResourceId and TopicTagId.

References

For more information, check out the list of references below.

For detailed tutorials that include both Razor Pages and MVC, check out the official tutorials below: