Showing posts with label .net core. Show all posts
Showing posts with label .net core. Show all posts

Tuesday, August 6, 2019

Using EntityFramework in a .Net Core Project

Introduction

While I work with EntityFramework every day, I do not add an EntityFramework (EF) to an existing project every day. Time and again, I have to create a new project or add EntityFramework to the existing project and I have to re-do many steps that are hard to remember. I decided to document the steps in the form of this article.

In this article, I am going to show how to get started with EntityFramework. I will start a project without support to EntityFramework and add the EF support to the project and use a migration to update the database. The code related to this article is available at GitHub. The sample project uses in Visual Studio 2017 IDE using C#, MS SQL Server with SQL Server Management Studio (SSMS) and EntityFrameworkCore with .Net Core SDK 2.2.


Create Solution and Project

I started with the creation of an empty solution and added a WebAPI project targetting .NetCore 2.2.





As I am using MS SQL Server, I added a reference to

Microsoft.EntityFrameworkCore.SqlServer and Microsoft.EntityFrameworkCore.Design using Nuget Package Manager.


I updated the appsetting.json file for the connectionstring by adding the following property:

 "ConnectionStrings": { 
     "DefaultConnection":
        "Server=(localdb)\\mssqllocaldb;Database=ApplicationDb;Trusted_Connection=True;" }



Configure Database and Models

I have created models for Customer, Contact, and CustomerContact. The code example has these models in DBContext folder. In addition, there are classes for IAuidtable and Audit and some enums which can be ignored for now. Customer and Contact entity have many to many relationships and thus I added CustomerContact entity to store the relationship.


public class Customer : IAuditable
    {
        public Guid Id { get; set; }
        public String AccountNumber { get; set; }
        public String Name { get; set; }
        public DateTime? CreatedDate { get; set; }
        public DateTime? ModifiedDate { get; set; }
        public String LastModifiedBy { get; set; }
        public bool IsInactive { get; set; }
        public ICollection<CustomerContact> CustomerContacts { get; set; }
    }

    public class Contact : IAuditable
    {
        public Guid Id { get; set; }
        public String Name { get; set; }
        public String Title { get; set; }
        public String Phone { get; set; }
        public String Email { get; set; }
        public ContactTypeEnum ContactType { get; set; }
        public String Note { get; set; }
        public DateTime? CreatedDate { get; set; }
        public DateTime? ModifiedDate { get; set; }
        public String LastModifiedBy { get; set; }
        public bool IsInactive { get; set; }
        public ICollection<CustomerContact> CustomerContacts { get; set; }

    }

    public class CustomerContact:IAuditable
    {
        public Guid Id { get; set; }
        public DateTime? CreatedDate { get; set; }
        public DateTime? ModifiedDate { get; set; }
        public string LastModifiedBy { get; set; }
        public bool IsInactive { get; set; }
        public Guid CustomerId { get; set; }
        public Customer Customer { get; set; }
        public Guid ContactId { get; set; }
        public Contact Contact { get; set; }
    }

    public class Audit
    {
        public Guid Id { get; set; }
        public Guid? EntityId { get; set; }
        public string User { get; set; }
        public String Entity { get; set; }
        public DateTime DateTime { get; set; }
        public string ColumnName { get; set; }
        public String OldValue { get; set; }
        public String NewValue { get; set; }
        public EntityStateChangeTypeEnum ChangeType { get; set; }

    }
    /// <summary>
    /// This interface determines what will be automatically tracked.
    /// </summary>
    interface IAuditable
    {
        Guid Id { get; set; }
        DateTime? CreatedDate { get; set; }
        DateTime? ModifiedDate { get; set; }
        String LastModifiedBy { get; set; }
        bool IsInactive { get; set; }
    }

    public enum EntityStateChangeTypeEnum
    {
        Added,
        Deleted,
        Modified,
    }

    public enum ContactTypeEnum
    {
        Primary,
        Secondary,
        Emergency,
    }


With these changes, I am ready to set up code-first logic for creating database tables. I am going to create a class ApplicationDbContext that derives from DbContext of EntityFrameworkCore. In this class, I create DbSet objects of the type of Customer, Contact, CustomerContact, and Audit. This is what makes EF aware of the tables I need to create.


using Microsoft.EntityFrameworkCore;

namespace AutotrackEntityChange.DBContext
{
    public class ApplicationDbContext: DbContext
    {
        //Define DbSets
        public DbSet<Customer> Customers { get; set; }
        public DbSet<Contact> Contacts { get; set; }

        public DbSet<CustomerContact> CustomerContacts { get; set; }
        public DbSet<Audit> Audits { get; set; }

        public ApplicationDbContext(
          DbContextOptions<ApplicationDbContext> options)
            : base(options) {}
    }
}


Now I need to add service in the Startup.cs class to use ApplicationDbContext within the application. The service can be added inside the ConfigureService method using the following code:


 services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration
            .GetConnectionString("DefaultConnection")));


Note that the service addition above uses the same DefaultConnection that was defined in the appsetting.json earlier.


Migration and Database Update

I can run the 'Add-Migration 'InitialCreate'' command in the Package Manager Console within the Visual Studio to generate necessary migration scripts.


This will create necessary Migration files in the solution under Migration folder.


Now I can run 'Update-Database' command in the Package Manager Console to apply the migration. 

Note that at this time, the migration code will generate the database and the tables inside 'ApplicationDb' database. I can now open SSMS and navigate to the database and see the tables.



Summary

In this article, I showed steps to add EntityFramework support to a .Net Core project using Code-First approach and migrations. I provided an example using a WebAPI project with SQL Server, but the steps mentioned here can be used for other database and other project types. The migration I used was really for creating the database and entities, but a similar approach can be used to update the entities. am hoping this will help someone and I welcome comments and suggestions.

References

Wednesday, March 20, 2019

How to handle 'Which has a higher version than referenced assembly' error (Error CS1705)

Where there are multiple projects within the solution and one project is dependent on others, and versions introduce breaking changes, we sometimes encounter package version conflict and Visual Studio resulting in build error in the solution. It may return the following error:

Error    CS1705    Assembly 'WebAPI' with identity 'WebAPI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' uses 'Microsoft.AspNetCore.Mvc.Core, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60' which has a higher version than referenced assembly 'Microsoft.AspNetCore.Mvc.Core' with identity 'Microsoft.AspNetCore.Mvc.Core, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'    WebApiTest    C:\... 

The above error can be produced on a solution that uses two projects, namely WebApi and WebApiTest. 

The error above shows that the test project and API project are using a different version of Microsoft.AspNetCore.Mvc.Core. For example, the test project is using version 2.1.0.0 while the API project is using 2.1.1.0.

We can resolve this issue by updating the package reference in either of the projects to match. For example, we can update the WebApiTest project to use the higher version of the assembly (i.e., at 2.1.1.0) or downgrade the assembly version in the WebApi project to 2.1.0.0 depending on the need. 

We can issue a command like below in the package manager console targetting the right project. For example, the code below will update the assembly reference for Microsoft.AspNetCore.Mvc.Core on WebApiTest test to use version 2.1.1.0. This will then match the version of the same assembly in the WebApi project.  The following code 
Install-Package Microsoft.AspNetCore.Mvc.Core -Version 2.1.1.0 -ProjectName WebApiTest 

The argument '- ProjectName WebApiTest' name can be omitted if the correct project is selected in the dropdown that is available in the Package Manager Console window in the Visual Studio as shown in the figure below: 


Likewise, to update the WebApi project instead, the following command will do the trick:

Install-Package Microsoft.AspNetCore.Mvc.Core -Version 2.1.1.0 -ProjectName WebApiTest 
 
 

Sunday, December 16, 2018

Document and Test API with Swagger UI

More than often developers test API, either through a browser request or using some clients such as POSTMANAdvanced Rest Client (ARC). To expose the functionality of the API, we also tend to expose the methods and descriptions, and associated data structure through some means which requires additional work. To complement, either or both of these functionalities, Swagger becomes handy which provides API documentation and API testing by configuration.

Swagger UI is a tool that can be used across API lifecycle. Swagger provides easy to navigate documentation and/or visualization of API resources and enables interaction with API possible from within the application itself making the development and testing effort, as well as end-user experience seamlessly smooth. In this article, I am going to discuss how to implement swagger in API and exemplify some use cases of Swagger UI. I will be using .Net Core 2.0 Web API application and using Visual Studio 2017 IDE. I have created a sample API application with a single controller and four methods as part of the demo which is available for download.

Swagger offers the most powerful and easiest to use tools to take full advantage of the OpenAPI Specification. 

Configuration

Wiring-up Swagger on an application is fairly minimal and can be accomplished in four easy steps namely - installation, import, registration, and endpoint enablement.

The package can be installed in Package Manager Console, or alternatively by going to the NuGet Package Manager menu.
Install-Package Swashbuckle.AspNetCore
Figure 1: Installing Swagger in the Package Manager Console.

Once installed, Swagger must be imported into Startup.cs.
using Swashbuckle.AspNetCore.Swagger;
Then the Swagger as service must be registered within ConfigureServices method of Startup.cs.
services.AddSwaggerGen(c =>
{
    c.SwaggerDoc(_version, new Info { Title = _applicationName, Version =   _version });
});
Finally, the application must enable JSON as well as UI endpoints for swagger within  Configure method of Startup.cs so that end-users can interact to API methods through the Swagger UI.
// Enable Swagger JSON endpoint.
app.UseSwagger();  
// Enable swagger-ui (HTML, JS, CSS, etc.)
app.UseSwaggerUI(c => {    
c.SwaggerEndpoint($"/swagger/{_version}/swagger.json",${_applicationName} {_version}"); 
});
The complete list of Startup.cs file is shown in Snippet 1.

Snippet 1: Content of Startup.cs

Visualization

Upon completing the above four steps, swagger is ready to go. Browse through https://localhost:5001/swagger/v1/swagger.json to get the data in JSON (either in a browser or a client such as POSTMAN, Advanced Rest Client (ARC)). The returned JSON object provides the specification of the REST methods (separated by Controllers) and objects used in API. An example response is Snippet 2.

Snippet 2: JSON Response Sample

The Swagger UI is accessible by navigating to https://localhost:5001/swagger/index.html where a user can visualize the same data that was available in JSON response in an interactive format. This UI also supports the actual execution of the rest methods.
Figure 2: Visualizing API in Swagger UI

Testing

Swagger provides functionality to test the API methods without any tools. For example, clicking the GET (first tab in Figure 2.) expands the method. By clicking 'Try it Out' and then 'Execute', swagger triggers a call to 'get' method to /api/stock. Note that there is a single controller named 'StockController' in the demo application. The results are shown in the UI which can also be downloaded. Figure 3. shows a result screen for the get method. Any method exposed in the UI can be executed in UI itself, thereby giving us the ability to test the API directly from the API itself.
Figure 3: Testing of API methods in Swagger UI

Support to Attributes 

Routing attributes, Http*attributes are supported by default. Figure 4 shows how the methods decorated with HttpGet, HttpPost, HttpDelete are reflected in the Swagger UI.

Figure 4: Swagger UI reflecting HTTP attributes

Swagger is in sync with attributes in .Net. For example, if a controller class is decorated with [Obsolete] attribute, the UI reflects the fact that the controller is 'obsolete'. Figure 5 shows that the ValuesController is marked 'Obsolete' and Swagger UI reflects the same and is not clickable. This feature becomes handy when API is phasing out certain functionality without breaking the working code.

Figure 5: Swagger UI reflecting the Obsolte attribute

Support XML Documentation

Swagger support Documentation in UI with some configuration change. By adding following lines of code in *.csproj file, the XML document is generated.
<Property Group>
....
<GenerateDocumentationFile>true</GenerateDocumentationFile> <NoWarn>$(NoWarn);1591</NoWarn>
<Property Group>
With the following lines of code in the Startup.cs, while registering, the UI shows the XML documentation.
services.AddSwaggerGen(c => {  ...  var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";  var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);  c.IncludeXmlComments(xmlPath);});
 With the change to use XML path, the Swagger exposes the XML document in the UI.

Figure 6: Swagger UI Shows Documentation.

Swagger is supported in all major browsers and works in both local or on the web environment. The look and feel of the UI are customizable.

I showed before how it works on a local machine. The Swagger UI works well on the cloud. The same application deployed to Azure, https://smartstockapi.azurewebsites.net/swagger/index.html works as it worked in my local machine.

Figure 7: Swagger UI in Application Hosted in Cloud (Azure)

Resources/Materials/References: