Learn ASP.NET Core 3.0 : MVC, Razor Pages, Web API, Entity Framework Core, and Blazor.
Microsoft's official documentation can be found here. Looking for professional online training courses? Next weekend batches are starting in November 2019. More details here.

CRUD using gRPC, EF Core, and ASP.NET Core (Part - 2)

In Part -1 of this article you created EmployeeCRUD service definition using Protocol Buffer language. The EmployeeCRUD.proto file and EF Core model is now ready. In this part you will create the EmployeeCRUD service based on the structure defined in the .proto file.

To begin, open the project created in Part -1 and expand the Services folder. Rename the service file to EmployeeCRUDService.cs. At this point the Solutions Explorer will look like this:

Now open the EmployeeCRUDService.cs file and modify the class to resemble this:

...
using DataAccess = GrpcService1.DataAccess;

public class EmployeeCRUDService : 
EmployeeCRUD.EmployeeCRUDBase
{
    private DataAccess.AppDbContext db = null;

    public EmployeeCRUDService(
DataAccess.AppDbContext db)
    {
        this.db = db;
    }
}

As you can see, EmployeeCRUDService class inherits from EmployeeCRUD.EmployeeCRUDBase. This base class is automatically generated for you based on the .proto file you created earlier. The EmployeeCRUDService class needs to override the service methods such as SelectAll() and SelectByID(). You will do that shortly.

Since the service method implementation requires access to EF Core data context and entity classes, AppDbContext is injected in the constructor of the service class. Note that in my case there are two Employee classes - one obtained from the Employee message type and the other is Employee entity class. Here in the service you need to use both Employee classes. Therefore, DataAccess namespace alias is used to correctly identify both.

Next, add SelectAll() method to the service class as shown below:

public override Task<Employees> SelectAll
(Empty requestData, ServerCallContext context)
{
    Employees responseData = new Employees();
    var query = from emp in db.Employees
                select new Employee()
                {
                    EmployeeID = emp.EmployeeID,
                    FirstName = emp.FirstName,
                    LastName = emp.LastName
                };
    responseData.Items.AddRange(query.ToArray());
    return Task.FromResult(responseData);
}

The SelectAll() method is an async method and takes two parameters. The first requestData parameter contains data received along with the request. In this case it's an Empty object. The second parameter is ServerCallContext object that represents server-side context for the RPC call being made. The ServerCallContext is automatically made available to all the service methods. In this case we don't use it in our processing.

Inside, we create Employees object. Recollect that Employees message type is a collection of Employee objects. A LINQ query then projects DataAccess.Employee into Employee. Actual Employee data is added to Employees object using its Items collection and AddRange() method.

The Employees object thus created is returned to the caller.

Now add SelectByID() method as shown below:

public override Task<Employee> SelectByID
(EmployeeFilter requestData,
 ServerCallContext context)
{
    var data = db.Employees.Find
(requestData.EmployeeID);
    Employee emp = new Employee()
    {
        EmployeeID = data.EmployeeID,
        FirstName = data.FirstName,
        LastName = data.LastName
    };
    return Task.FromResult(emp);
}

The SelectByID() method receives EmployeeFilter object containing EmployeeID. Inside, it fetches the matching Employee object using Find() method and returns it to the caller.

Then add Insert() method as shown below:

public override Task<Empty> Insert
(Employee requestData,
 ServerCallContext context)
{
    db.Employees.Add(new DataAccess.Employee()
    {
        EmployeeID = requestData.EmployeeID,
        FirstName = requestData.FirstName,
        LastName = requestData.LastName
    });
    db.SaveChanges();
    return Task.FromResult(new Empty());
}

The Insert() method receives an Employee object containing details of the new employee to be added. Inside, it adds it to the Employees DbSet using Add() method. The data is saved to the database using SaveChanges() method. Recollect that Insert(), Update(), and Delete() actions return Empty object to the caller. So, an Empty object is returned to the caller.

Similarly, add the Update() method as shown below:

public override Task<Empty> 
Update(Employee requestData,
 ServerCallContext context)
{
    db.Employees.Update(new DataAccess.Employee()
    {
        EmployeeID = requestData.EmployeeID,
        FirstName = requestData.FirstName,
        LastName = requestData.LastName
    });
    db.SaveChanges();
    return Task.FromResult(new Empty());
}

Update() method is similar to Insert() in that it also receives Employee object. Inside, it updates an existing Employee using Update() method and saves the changes using SaveChanges() method.

Finally, add the Delete() method as shown below:

public override Task<Empty>
 Delete(EmployeeFilter requestData,
 ServerCallContext context)
{
    var data = db.Employees.Find(requestData.EmployeeID);
    db.Employees.Remove(new DataAccess.Employee()
    {
        EmployeeID = data.EmployeeID,
        FirstName = data.FirstName,
        LastName = data.LastName
    });
    db.SaveChanges();
    return Task.FromResult(new Empty());
}

The Delete() method receives an EmployeeFilter object containing EmployeeID of an employee to be deleted. Inside, it finds that Employee using Find() method and then removes it from Employee DbSet using Remove() method. The changes are persisted in the database by calling SaveChanges() method.

This complete the EmployeeCRUDService. One final step is to modify Configure() in the Startup class as shown below:

public void Configure(IApplicationBuilder app,
 IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    app.UseRouting();
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapGrpcService<EmployeeCRUDService>();
        ...
    });
}

Change the type mentioned in the MapGrpcService() method call to EmployeeCRUDService. You can now compile the project to ensure that there are no compilation errors. If you run the application at this stage, you will see a console window opened as shown below:

As you can see, the service is available at https://localhost:5001. You can now consume it in a client application. You will build a client application in the next part of this article.

That's it for now! Keep coding!!


"Taming the mind by the goad of Prana -- that wonderful game called meditation."
#AjapaYogaByBipinJoshi

Bipin Joshi is an independent software consultant, trainer, author, yoga mentor, and meditation teacher. He has been programming, meditating, and teaching for 24+ years. He conducts instructor-led online training courses in ASP.NET family of technologies for individuals and small groups. He is a published author and has authored or co-authored books for Apress and Wrox press. Having embraced the Yoga way of life he also teaches Ajapa Yoga to interested individuals. To know more about him click here.

Get article updates : Facebook  Twitter  LinkedIn

Posted On : 28 October 2019


Tags : ASP.NET ASP.NET Core Data Access SQL Server C# Visual Studio


Subscribe to our newsletter

Get monthly email updates about new articles, tutorials, code samples, and how-tos getting added to our knowledge base.

  

Receive Weekly Updates