Skip to content
This repository has been archived by the owner on May 23, 2024. It is now read-only.

Getting Started for Beginners with Visual Studio for Mac

Steve Smith edited this page Oct 18, 2019 · 13 revisions

Getting the Code

You can access the eShopOnWeb sample code on your machine by either downloading it as a ZIP file or cloning it using git. In either case, click on the Clone or download button on the repository's home page:

eShopOnWeb GitHub Page

Then to get the ZIP file just click Download ZIP. Extract it to a folder and open the eShopOnWeb.sln file from Visual Studio for Mac (you should just be able to double-click on the solution file):

eShopOnWeb ZIP file extracted to a folder

Open eShopOnWeb from VS for Mac

Once you've opened the solution, there's one last step you need to take before you can run the application. Go to the Web project and right-click on it to access its Options menu. Go to Run - Configurations - Default and change the ASPNETCORE_ENVIRONMENT environment variable from "Production" to "Development" as shown here:

Web Project Options Dialog

Now you should be able to run the application, which will launch in a new browser instance:

eShopOnWeb running locally

If you get an error saying you don't have the necessary SDK files, make sure to download the latest .NET Core SDK from dot.net.

Solution Structure

As applications grow in size, it can be worthwhile to break them up into several projects, usually based on their focus. This follows the Separation of Concerns principle, and common examples of focus for individual portions of an application are user interface (UI), business logic, and data access or infrastructure. The eShopOnWeb solution has been split up into three main application projects:

  • ApplicationCore
  • Infrastructure
  • Web

The business logic and domain model are kept in the ApplicationCore project. Data access and other infrastructure plumbing code belongs in the Infrastructure project. And ASP.NET Core (Web) concerns belong in the Web project. The solution also has a number of different kinds of automated tests, found in separate Test folders. Projects depend on one another and can help enforce the direction of dependencies. In this case, we don't want our business logic to depend on low level plumbing code like that found in the Infrastructure project, so we structure the dependency direction so that Infrastructure depends on Application Core. This follows the Dependency Inversion Principle (DIP), because the implementation code in Infrastructure implements interfaces defined in ApplicationCore. (Learn more about SOLID Principles for C# Developers)

ApplicationCore Project

The ApplicationCore project holds the business logic for the application. This business logic is contained in a domain model - a set of classes that represent concepts in the business domain being automated. In this case, the domain model represents online store concepts like Buyers, Orders, and Baskets. These entities have state that is stored in a data store using EF Core, but the use of EF Core is an implementation detail that is kept out of the ApplicationCore project. Groups of related entities, like a basket and its items, are grouped together as aggregates, which are retrieved and saved from persistence as a unit.

This project also holds the business-level interface abstractions used by the application and mostly implemented in the Infrastructure project. These include interfaces for performing data access (repositories) as well as various services that perform logging or send emails or define database queries (specifications). Some of these interfaces are implemented within the ApplicationCore project, as long as they don't have any dependencies on specific implementation details that would require the ApplicationCore project to take on such a dependency.

The project also holds custom exceptions, like the BasketNotFoundException, and domain services like the BasketService and OrderService which perform business logic but don't directly work with infrastructure like databases or files.

Finally, the project's specifications are used to define different kinds of queries in a testable and reusable fashion. Each specification type defines all of the logic required to run a query using a repository.

Infrastructure Project

The Infrastructure project holds the implementation details for how the application works with infrastructure concerns. Things like databases, email providers, and file systems. Low level plumbing code. Knowledge of these concerns in a separate project so they can't pollute the business/domain model that's kept in ApplicationCore, which in turns makes it much easier to write unit tests of the most valuable logic in the system, the business logic. Code that has dependencies on infrastructure can often still be tested, but usually only with integration tests, not unit tests. See Unit Test or Integration Test and Why You Should Care to learn more about this distinction.

The infrastructure concerns implemented in this project include:

  • Data Access using EF Core (see CatalogContext)
  • Identity (also using EF Core) (see AppIdentityDbContext)
  • Logging (LoggerAdapter)
  • Email Sending (EmailSender)

Web Project

The Web project has several features you're already familiar with from the video tutorials, but there are a few additions as well that are described here.

Areas in ASP.NET Core are used to organize functionality, and in this case the Identity functionality uses an Area for configuration and customization.

Extensions are helper methods that can add methods to existing objects. The Extensions folder has two classes that contain extension methods for email sending and url generation.

Health checks are an ASP.NET Core feature that is used to visualize the status of a web application so users (and IT staff) can determine whether the application is healthy.

This application also supports running the application in a docker container, which is configured by the Dockerfile in the root of the Web project folder. Learn more about hosting ASP.NET Core applications in Docker containers.

Test Projects

This solution includes several projects that include automated tests. These tests confirm that the code in the application projects works as expected. You can learn more about testing ASP.NET Core apps here.

Dependency Injection

ASP.NET Core and this application make heavy use of a technique called dependency injection. Classes that have dependencies declare them in their constructor, following the Explicit Dependencies Principle. Typically, these dependencies in Web and ApplicationCore classes will be abstractions/interfaces, but sometimes implementation types are required, too (especially in Infrastructure types). Determining how the requested interfaces will map to implementation classes is handled in the ConfigureServices method in the Web project's Startup.cs file's ConfigureServices method.

Identity and Authorization

As noted above, the Web application has authorization configured for the app using Identity. This is configured with its own EF Core database context (AppIdentityDbContext) and when you run the app you work with identity when you register or log in. It's generally a good idea to avoid tightly coupling your authorization process with your business logic, so you'll notice that ApplicationCore has no knowledge of the app's auth strategy.

When you run the application, click on the Login link in the site header to log in. Note that the sample comes with a user and password already set up for you, noted at the bottom of the login page: [email protected] / Pass@word1.

Performance Considerations

One final consideration this sample includes that many basic examples leave out is caching, which is used to optimize the site's performance and minimize inefficient calls to external data. The site's home page uses data in the form of a CatalogIndexViewModel which the Razor Page binds to in order to display data on the page. This view model is created by the CatalogViewModelService which in turn uses several repository types to get data from the site's configured data store and convert it into the structure the CatalogIndexViewModel requires. This data doesn't change often, so requesting it from the database on every request to the page is inefficient and put unnecessary load on the database server while increasing page render times for users.

A common technique to address this scenario is the add caching, so that results from getting data from the data store are stored on the web server for some period of time and reused by future web requests. In this application, the caching is done by the CachedCatalogViewModelService which acts as a decorator or wrapper around the CatalogViewModelService. You can see how it is configured for use by the application in Startup.cs:

  services.AddScoped<ICatalogViewModelService, CachedCatalogViewModelService>();

If you wanted to disable caching for the application, you would replace this line with this one that uses the CatalogViewModelService directly:

  services.AddScoped<ICatalogViewModelService, CatalogViewModelService>();

Get Help

If you need help with the sample, you can open an issue. Describe the problem you're having and the steps you've taken so far to try to resolve it, and someone in the community or from Microsoft will try to help you. If your question or problem isn't specific to this sample, you may find it better to ask it on Stackoverflow.com.

Next Steps

Clone this wiki locally