In this post, we are going to convert the synchronous code to asynchronous inside ASP.NET Core. First, we are going to learn a bit about asynchronous programming and why should we write async code. Then we are going to use our project from the .NET Core series and rewrite it in an async manner.
In Part 4 of the series, we have created Generic Repository Pattern and in Part 5 and Part 6 the Controller with Actions that consumes that repository. We recommend reading those articles if you are not familiar with Generic Repository Pattern or if you find any of the concepts in this article hard to understand. Those articles will help you follow along with this article much easier because we won’t dive into its business logic.
We are going to modify the code, step by step, to show you how easy is to convert the synchronous code to an asynchronous one. Hopefully, this will help you understand how asynchronous code works and how to write it from scratch in your applications.
To download the source code for our starting project, you can visit our GitHub repo for the starting project.
For the finished project refer to our GitHub repo for the finished project.
We are going to cover the following sections in this article:
- What is Asynchronous Programming
- Async, Await Keywords and Return Types
- Overview of the IRepositoryBase Interface and the RepositoryBase Class
- Modifying the IOwnerRepository Interface and the OwnerRepository Class
- Controller Modification
- Conclusion
What is Asynchronous Programming
Async programming is a parallel programming technique, which allows the working process to run separately from the main application thread. As soon as the work completes, it informs the main thread about the result, whether it was successful or not.
By using async programming, we can avoid performance bottlenecks and enhance the responsiveness of our application.
How so?
Because we are not sending requests to the server and blocking it while waiting for the responses anymore (as long as it takes). Now, when we send a request to the server, the thread pool delegates a thread to that request. Eventually, that thread finishes its job and returns to the thread pool freeing itself for the next request. At some point, the data will be fetched from the database and the result needs to be sent to the requester. At that time, the thread pool provides another thread to handle that work. Once the work is done, a thread is going back to the thread pool.
It is very important to understand that if we send a request to an endpoint and it takes the application three or more seconds to process that request, we probably won’t be able to execute this request any faster in async mode. It is going to take the same amount of time as the sync request.
The only advantage is that in the async mode the thread won’t be blocked three or more seconds, and thus it will be able to process other requests. This is what makes our solution scalable.
Here is the visual representation of the asynchronous workflow:
Now that we cleared that out we can learn how to implement the asynchronous code in .NET Core.
Async, Await Keywords and Return Types
The async
and await
keywords play a crucial part in asynchronous programming. By using those keywords, we can easily write asynchronous methods without too much effort.
For example, if we want to create a method in an asynchronous manner, we need to add the async
keyword next to the method’s return type:
async Task<IEnumerable<Owner>> GetAllOwnersAsync()
By using the async
keyword, we are enabling the await
keyword and modifying the way of how method results are handled (from synchronous to asynchronous):
await FindAllAsync();
In asynchronous programming we have three return types:
Task<TResult>
, for an async method that returns a valueTask
, to use it for an async method that does not return a valuevoid
, which we can use for an event handler
What does this mean?
Well, we can look at this through the synchronous programming glasses. If our sync method returns int
then in the async mode, it should return Task<int>
, or if sync method returns IEnumerable<string>
then the async method should return Task<IEnumerable<string>>
.
But if our sync method returns no value (has a void
for the return type), then our async method should usually returnTask
. This means that we can use the await
keyword inside that method but without the return
keyword.
You may wonder, why not returning Task
all the time? Well, we should use void only for the asynchronous event handlers which require a void
return type. Other than that, we should always return a Task.
From C# 7.0 onward, we can specify any other return type, if that type includes GetAwaiter
method.
Now, when we have all the information, let’s do some refactoring in our completely synchronous code.
Overview of the IRepositoryBase Interface and the RepositoryBase Class
Our complete repository is interface based, so let’s take a look at our base interface. In the Contracts
project open the IRepositoryBase.cs
file. We can see different method signatures:
public interface IRepositoryBase<T> { IQueryable<T> FindAll(); IQueryable<T> FindByCondition(Expression<Func<T, bool>> expression); void Create(T entity); void Update(T entity); void Delete(T entity); }
It is important to notice the FindAll
and FindByCondition
method signatures. Both of them return IQueryable that allows us to attach async calls to them.
The Create
, Update
, and Delete
methods don’t modify any data, they just track changes to an entity and wait for the EF Core’s SaveChanges
method to execute. So, they stay unchanged as well.
Let’s just take a look at the RepositoryBase
class:
public abstract class RepositoryBase<T> : IRepositoryBase<T> where T : class { protected RepositoryContext RepositoryContext { get; set; } public RepositoryBase(RepositoryContext repositoryContext) { this.RepositoryContext = repositoryContext; } public IQueryable<T> FindAll() { return this.RepositoryContext.Set<T>().AsNoTracking(); } public IQueryable<T> FindByCondition(Expression<Func<T, bool>> expression) { return this.RepositoryContext.Set<T>() .Where(expression).AsNoTracking(); } public void Create(T entity) { this.RepositoryContext.Set<T>().Add(entity); } public void Update(T entity) { this.RepositoryContext.Set<T>().Update(entity); } public void Delete(T entity) { this.RepositoryContext.Set<T>().Remove(entity); } }
This class is the implementation of the previous interface and it is a base class for accessing the data from the database.
Modifying the IOwnerRepository Interface and the OwnerRepository Class
Now, let’s continue on the other parts of our repository. In the Contracts
project, there is also the IOwnerRepository
interface with all the synchronous method signatures which we should change too.
So let’s do that:
public interface IOwnerRepository : IRepositoryBase<Owner> { Task<IEnumerable<Owner>> GetAllOwnersAsync(); Task<Owner> GetOwnerByIdAsync(Guid ownerId); Task<Owner> GetOwnerWithDetailsAsync(Guid ownerId); void CreateOwner(Owner owner); void UpdateOwner(Owner owner); void DeleteOwner(Owner owner); }
So, we just change signatures of the first three GET methods by assigning the Task<TResult>
to the return type. We don’t have to change the other three methods because, as we said, they just change the state of the entity and wait for the SaveChanges
to execute.
Now, we have to implement this interface by using the async
and await
keywords. Using the await
keyword is not mandatory though. Of course, if we don’t use it, our async methods will execute synchronously, and that is not our goal here.
So, in accordance with the interface changes, let’s modify our OwnerRepository.cs
class, that we may find in the Repository
project:
public class OwnerRepository : RepositoryBase<Owner>, IOwnerRepository { public OwnerRepository(RepositoryContext repositoryContext) : base(repositoryContext) { } public async Task<IEnumerable<Owner>> GetAllOwnersAsync() { return await FindAll() .OrderBy(ow => ow.Name) .ToListAsync(); } public async Task<Owner> GetOwnerByIdAsync(Guid ownerId) { return await FindByCondition(owner => owner.Id.Equals(ownerId)) .FirstOrDefaultAsync(); } public async Task<Owner> GetOwnerWithDetailsAsync(Guid ownerId) { return await FindByCondition(owner => owner.Id.Equals(ownerId)) .Include(ac => ac.Accounts) .FirstOrDefaultAsync(); } public void CreateOwner(Owner owner) { Create(owner); } public void UpdateOwner(Owner owner) { Update(owner); } public void DeleteOwner(Owner owner) { Delete(owner); } }
IRepositoryWrapper and RepositoryWrapper Changes
We have to modify the Save method in the mentioned interface and the class as well:
public interface IRepositoryWrapper { IOwnerRepository Owner { get; } IAccountRepository Account { get; } Task SaveAsync(); }
And let’s just modify the Save method in the RepositoryWrapper class:
public async Task SaveAsync() { await _repoContext.SaveChangesAsync(); }
Now, we can continue to the controller modification.
Controller Modification
Finally, we need to modify all of our actions in the OwnerController
to work asynchronously.
So, let’s first start with the GetAllOwners method:
[HttpGet] public async Task<IActionResult> GetAllOwners() { try { var owners = await _repository.Owner.GetAllOwnersAsync(); _logger.LogInfo($"Returned all owners from database."); var ownersResult = _mapper.Map<IEnumerable<OwnerDto>>(owners); return Ok(ownersResult); } catch (Exception ex) { _logger.LogError($"Something went wrong inside GetAllOwners action: {ex.Message}"); return StatusCode(500, "Internal server error"); } }
We haven’t changed much in this action. We’ve just changed the return type and added the async keyword to the method signature. In the method body, we can now await the GetAllOwnersAsync()
method. And that is pretty much what we should do in all the actions in our controller.
So let’s modify all the other actions.
GetOwnerById:
[HttpGet("{id}", Name = "OwnerById")] public async Task<IActionResult> GetOwnerById(Guid id) { try { var owner = await _repository.Owner.GetOwnerByIdAsync(id); if (owner == null) { _logger.LogError($"Owner with id: {id}, hasn't been found in db."); return NotFound(); } else { _logger.LogInfo($"Returned owner with id: {id}"); var ownerResult = _mapper.Map<OwnerDto>(owner); return Ok(ownerResult); } } catch (Exception ex) { _logger.LogError($"Something went wrong inside GetOwnerById action: {ex.Message}"); return StatusCode(500, "Internal server error"); } }
GetOwnerWithDetails:
[HttpGet("{id}/account")] public async Task<IActionResult> GetOwnerWithDetails(Guid id) { try { var owner = await _repository.Owner.GetOwnerWithDetailsAsync(id); if (owner == null) { _logger.LogError($"Owner with id: {id}, hasn't been found in db."); return NotFound(); } else { _logger.LogInfo($"Returned owner with details for id: {id}"); var ownerResult = _mapper.Map<OwnerDto>(owner); return Ok(ownerResult); } } catch (Exception ex) { _logger.LogError($"Something went wrong inside GetOwnerWithDetails action: {ex.Message}"); return StatusCode(500, "Internal server error"); } }
CreateOwner:
[HttpPost] public async Task<IActionResult> CreateOwner([FromBody]OwnerForCreationDto owner) { try { if (owner == null) { _logger.LogError("Owner object sent from client is null."); return BadRequest("Owner object is null"); } if (!ModelState.IsValid) { _logger.LogError("Invalid owner object sent from client."); return BadRequest("Invalid model object"); } var ownerEntity = _mapper.Map<Owner>(owner); _repository.Owner.CreateOwner(ownerEntity); await _repository.SaveAsync(); var createdOwner = _mapper.Map<OwnerDto>(ownerEntity); return CreatedAtRoute("OwnerById", new { id = createdOwner.Id }, createdOwner); } catch (Exception ex) { _logger.LogError($"Something went wrong inside CreateOwner action: {ex.Message}"); return StatusCode(500, "Internal server error"); } }
UpdateOwner:
[HttpPut("{id}")] public async Task<IActionResult> UpdateOwner(Guid id, [FromBody]OwnerForUpdateDto owner) { try { if (owner == null) { _logger.LogError("Owner object sent from client is null."); return BadRequest("Owner object is null"); } if (!ModelState.IsValid) { _logger.LogError("Invalid owner object sent from client."); return BadRequest("Invalid model object"); } var ownerEntity = await _repository.Owner.GetOwnerByIdAsync(id); if (ownerEntity == null) { _logger.LogError($"Owner with id: {id}, hasn't been found in db."); return NotFound(); } _mapper.Map(owner, ownerEntity); _repository.Owner.UpdateOwner(ownerEntity); await _repository.SaveAsync(); return NoContent(); } catch (Exception ex) { _logger.LogError($"Something went wrong inside UpdateOwner action: {ex.Message}"); return StatusCode(500, "Internal server error"); } }
DeleteOwner:
[HttpDelete("{id}")] public async Task<IActionResult> DeleteOwner(Guid id) { try { var owner = await _repository.Owner.GetOwnerByIdAsync(id); if (owner == null) { _logger.LogError($"Owner with id: {id}, hasn't been found in db."); return NotFound(); } if (_repository.Account.AccountsByOwner(id).Any()) { _logger.LogError($"Cannot delete owner with id: {id}. It has related accounts. Delete those accounts first"); return BadRequest("Cannot delete owner. It has related accounts. Delete those accounts first"); } _repository.Owner.DeleteOwner(owner); await _repository.SaveAsync(); return NoContent(); } catch (Exception ex) { _logger.LogError($"Something went wrong inside DeleteOwner action: {ex.Message}"); return StatusCode(500, "Internal server error"); } }
Excellent. Now we are talking async 😀
We can make our actions even more maintainable and readable by implementing Global Error Handling to remove try-catch blocks and Action Filters as well to remove validation code repetitions.
Also, in this article, we are not using the service layer because we didn’t want to make things more complicated for this small project. But if you want to use it in your projects, which we strongly recommend, please read our Onion Architecture article to see how it should be done.
Conclusion
There you go. We have seen how easy is to convert the synchronous repository to asynchronous and how easy is to write async code overall. With a couple of changes, we gave more breathing space to our API and created a more responsive application.
It is a good practice to write async methods (when we have an opportunity) that handles the I/O actions because it is easy to write those methods and the benefits are indisputable.
Thank you for reading the article and we hope you found something useful in it.
I am a late player in .Net Core but through this article, I was able to confirm that I can still use by previous knowledge of repository pattern, MVC and WebAPI 2. Thanks for the great articles, its straightforward and easy to understand. Looking forward to avail your e-books.
I’m glad Carlo that you find our articles useful. This is always great to hear.
hello ,
thanks for this nice work.
please i need to return the data from created method and i change in repository base the create function like this
please i need to know if it’s correct ?
Well, as you have created the return type for your Creates method, you already know that you are not getting just an Entity from the Add method, but the whole EntityEntry object which contains the Entity itself, the State of the entity, and a lot more properties. So, I think you don’t need all of that, but just the Entity property. Also, wouldn’t modify this base method because as you can see in the controller implementation we are returning the created entity to the client.
hi
i want to add a generic metod to call parameterized storeprocedure from RepositoryBase class, i do not see FromSql there, i see ExecuteSqlRawAsync, how to do that. may i know please
Hello there. You may try something like this:
hello thank you for this usefull article please make the article of .net core 6.0 with sql server .
You are one of my favorite places to consult. The content is very detailed, but yet very straightforward. I want to appreciate you for the time taken to build all these up. You’re that champs!! I can’t wait to get my own ebook.
Thanks for all the kind words. It really means a lot to us. Wish you all the best and if you have any questions about the ebook, just pin us in the chat on our sales page.
I’m reading MSDN web site page here, and they added IDisposable in the repository but I can’t find it here.
Is it not mandatory at all ?
In what specific scenario/situation do we need to add IDisposable in the repo ?
They Use EF not EF Core. You have to understand how EF Core’s context works and gets disposed. You don’t need IDisposable here.
Hi Marinko, It’s Johnson again.
1) Do you have any article on Unit of Work?
2) Or can you explain the benefit of UoW in this project? Why we need it in our application? cause I did some search but many people say that EF core already provide us with UoW through change tracking entities.
3) Also I read that sometimes transaction fails !!! Can you give some practical example where/how a transaction may fail and requires us to use Uow, as I know I used to work on many-to-many relationship database in EF core.
Hello Johnson.
1) No, we really don’t have anything that is only specified for that topic. We just use it inside the repository pattern.
2) “but many people say that EF core already provides us with UoW” this is the sentence that everyone repeats all the way if they want to prove how using the repository pattern with EF Core is not good. And I have nothing against that, if they want to use the context object which directly communicates with the database and place it inside the controller or inject it inside the service layer, let them do it. We don’t want to do things that way. And we strongly believe that this is the way to do it.
Here are some of the reasons why you might opt-in to use the repo pattern:
– Not relying on hidden implementations of EF
– A higher degree of control over which functionalities you expose to other developers (you can hide the delete method eg, important for bigger projects)
– More control over performance and logging
– Easier testing
3) Well I can’t do that right now. I don’t know when that can fail but if it does, the transaction will prevent partial commit, which is what we want. By using this wrapper you can write _repo.Owner.Delete… _repo.Owner.Add… _repo.Account.Add and then call the Save to wrap all of that inside a transaction. This way you have control whether they all execute or none.
Hi Marinko, first off, thank you so much for the article, I personally learnt a lot.
I have few questions :
Q1) In the IOwnerRepository interface, why did you add again the last 3 methods
( CreateOwner, UpdateOwner and DeleteOwner) ? as they already exist in the IRepositoryBase as Generic. So is it not wasting keystroke ?
Because I read in another book where the author did not add any CRUD methods in a specific Interface like you did in IOwnerRepository,
The author left it empty but he says that we can only add methods here if those methods are specific to this repository only like the first 3 methods GetAllOwnersAsync(), GetOwnerByIdAsync(Guid ownerId), and GetOwnerWithDetailsAsync() which obviously belong in IOwnerRepository.
Q2) Why did you put Save() method into the Repository Wrapper rather than in the generic repository (IRepositoryBase) ?
Regards
Hello Johnson.
Regarding the first question, it is really not any waste of code lines. If you use expression body methods, that becomes three lines of code, so it is really nothing. That said, you are correct, you don’t have to add them in the specific repository class, you can leave them out. But it can cause confusion sometimes and we wanted to explicitly show where and how these repo methods call the methods from a base class. Also, if you have to modify them in some way, you already have the methods in place and don’t have to add them first inside the interface and then implement them. But again, you can do it your way, there is nothing wrong with it.
Regarding the second question, since our RepositoryWrapper class wraps all the specific repository classes, and the Save method will create a transaction for all the methods from all these wrapped repository classes, it was a perfect place to put it. Also, the RepositoryWrapper acts as a UoW, so, we want the Save method there.
Thanks for the comment, and have a great day.
Thanks for your reply.
I am getting System.InvalidOperationException: ‘Nullable object must have a value.’ while executing ‘FindByCondition’
Is there any way that you could download our source code and compare it with yours? That way you can easily find what is different in your project and maybe causing that error.
Thank you so much <3
You are most welcome.
Hello, in the repository async pattern how can we load related entities for example here if have Owner entity, but also lets say OwnerContact relation 1 to 1 or if have OwnerAddress relation 1 to many. Joining or something else?
Thank you
What is the best way to do joins between concrete Repositories. Assuming you want to join data from 4 or 5 repositories
Sounds like we encountered similar problem : )
The best way to combine logic from few different repos is to have a service layer where you can inject your repo user classes and then just call the methods you need.
Hi implementing this repo pattern to see if it’s useful (;
How would you modify the Create method in the base class to get the object back with a new id? using oracle identity
This is how I do it now.
public async Task AddContactType(ContactType contactType)
{
var addedEntity = _context.Add(contactType);
await _context.SaveChangesAsync();
return addedEntity.Entity;
}
Maybe something like this? you just need to remember to save
public T Create(T entity)
{
return _context.Add(entity).Entity;
}
I didn’t use the Oracle Identity. But with EF Core, the Add method, called in the RepositoryBase class in the Create method, returns the EntityEntry type. With it, you can use the Entity Property of type T. Once you get hold of that T entity you can cast it in the entity type you require like this:
public void Create(T entity)().Add(entity);
{
var entity2 = RepositoryContext.Set
var owner = entity2.Entity as Owner;
var ownerName = owner.Name;
var ownerId = owner.Id;
}
Of course, this is just a dummy implementation but you get the point.
Okay I don’t want to sound negative but I can not see what the repository brings to the table.
In my opinion and Microsoft’s the DbContext is already a repository.
Let me break it down. Here are the baseclass
Look at the delete, add methods. I amusing the new syntax.
using System; : IRepositoryBase where T : class
using System.Linq;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
namespace Samson.Data.Repositories
{
public abstract class RepositoryBase
{
private readonly SamDBContext _context;
internal RepositoryBase(SamDBContext repositoryContext) FindAll() ().AsNoTracking(); FindByCondition(Expression> expression) ()
{
_context = repositoryContext;
}
public IQueryable
{
return _context.Set
}
public IQueryable
{
return _context.Set
.Where(expression).AsNoTracking();
}
public T Create(T entity)
{
return _context.Add(entity).Entity;
}
public void Update(T entity)
{
_context.Update(entity);
}
public void Delete(T entity)
{
_context.Remove(entity);
}
}
}
FindAll:
Using AsNoTracking is a most have. But I configure the DbContext in the constructor to disable all tracking.
So FindAll gets an A+
FindByCondition:
Also using AsNoTracking. But now you can not write the condition in the FirstOrXXX but it’s a small price to pay for always having no tracking on.
The create, update, delete and the missing attach are sugar coating and not adding any value.
The repository wrapper? you already got that in the db context.
I think there are more value in writing wrappers around complex queries.
I have coded for 25 years and have used many ORM mappers with lots off repository patterns. My advise would be that EF Core and NHibernate have you covered. Use the session and db context as there are.
I don’t find your comment as a negative one. That is your opinion and I appreciate it a lot. Just to mention, we stated nowhere that you should use RepoPattern, we just shown how to do that. I will always stand behind this solution if you need it in your project if not, then thats legit as well.
Thanks I used 3 hours to try it out, always curious to learn something new.
The asp.net configuration series and the global error handling articles was for me pure gold. Stole it all (:
form where you got “_maper ” ?
Please download or at least look at the source code. You will find it injected in the controller.
The diagram of the asynchronous workflow is wrong.
There are not such thing as “main” and “background” threads in ASP.Net Core.
There is a “threads pool”, and each incomming request took a thread from that pool and executes there.
With async programing, when the code reach an “await” it means that at that point the execution could be put on hold there until the async operation ends and the thread released so that it can be used by another request. But the execution does not goes to any “background thread”. When the awaited operation completes, another thread is taken from the thread pool and the execution continues.
It’s also a little more complex to explain maybe, this video explains it very well:
https://uploads.disquscdn.com/images/e3f3b723c66420173b236d1aa6afaabe751cae42e8cbcfa09e329c3f4a1895ef.png
https://www.youtube.com/watch?v=_T3kvAxAPpQ
Maybe the confussion comes from desktop applications where there is an “UI Thread”, wich can be considered as the “main thread”.
Hello Andres. First of all, thanks a lot for that suggestion and explanation. I must say, I wrote this article a long time ago and back then all the resources were pointing to this type of diagram and explanation, so I had no reason to question them at all. Still, I believe the article is not misleading and provides a valuable info for the readers. Of course, I will check this out – After all, we all like watching Kevin’s videos 😀 😀
It looks much better with the corrections 🙂
Again thanks a lot. This just proves that we are all here to learn. We should be just enough open minded to accept constructive critics.
Hello :)))
I have one question. What with transactions???
What about the business layer here? how to call the business layer in the controller
Hello. You would add the business layer in the same way as the repo. So, you would have user classes, wrapped with a single wrapper class and that class would be registered in IOC and injected in the controller. Of course then the repo layer should be injected in a business layer l, not in a controller (presentation layer).
Hi, thanks for the reply, can you point me out to an example please.
Hello keshavtaurah. We don’t have any specific code to share with you and it would require a lot of coding for the comment section, but as I said it is the straight forward process, the same as for the repository layer.
For example it could go like this:
You have a new class for example OwnerService and in a constructor of that class you have injected Repository wrapper.
Then you have ServiceWrapper class, that would wrapp all the service classes, in this case OwnerService.
Finaly you would register ServiceWrapper into IOC and inject it in the controller instead of the repository wrapper which is now injected into a concrete service class.
Please take a look at our Repository Pattern article (Part 4 of .NET Core series), link is provided at a beginning of this article, and everything is going to be much easier to comprehend.
All the best.
Yeah, I read the topic about ONION architecture. It is helpful to guide me to create and inject services as keshavtaurah comment above.
can you please tell as why those are supposed to be changed but they are not? http://prntscr.com/okvic0
Hello Feruchio. Those method signatures have been changed, but comparing to the starting project. In a starting project they returned IEnumerable but now they return IQueryable. If you take a look at our starting project (link is in the article) you are going to see the difference. Best regards.
oh sorry then I came after this one http://34.65.74.140/net-core-web-development-part6/ and saw that mine were already like that so I baffled what is wrong with them, also another thing I noticed that probably here OwnerRepository.cs in get by id method probably should be FirstOrDefaultAsync instead of SingleAsync, because it give me this error (http://prntscr.com/okvuux) when I tried with id that does not exist.
Well as much as I can see, you have commented DefaultIfEmpty method. This method will return an empty object if the list is empty so the SingleAsync won’t throw exception. If you want to have GetByIdAsync without DefaultIfEmpty, then you should switch to FirstOrDefaultAsync.
yeah that’s what I did, and thank you for the fast replies and your tutorials are awesome keep up the good work.
I have two questions
1) In the syncronous example you are using Notracking on the Find methods … why ?
2) Inside UpdateOwnerAsync method I saw a magic Map method ( is not defined on the Model … well why should be a method on the Client Model?)… in EF6 I think I did something like that to update records properly in the past
_dbContext.Entry(dbClient).CurrentValues.SetValues(client);
await _db.SaveChangesAsync();
Is that why you remove the notracking from async Find methods ? the Map method that you use do something like that?
Hello Eduardo. Let me answer your questions:
1) AsNoTracking is telling EF Core not to track changes of the entity retrieved from the current context. This means that EF Core won’t create a copy of the entity to track its changes, thus making the query execute faster. This is a pure EF Core theory, doesn’t have anything to do with the async await logic.
2) The Map method is just an Extension Method on the Owner type. So it is hidden from a user class and doing some mapping from one object to another. AsNoTracking has nothing to do with that. I just didn’t implement it here, because the main focus is on the Async Await logic, not on the query it self.
Hello, I am implementing your whole solution, but I have some problem because I have not the code inside ServiceExtensions
Could you publish the whole solution? Thank you in advance
Hello Terry. I don’t know what you mean by “your whole solution”. This article is about async programming and it contains everything it has to have. You have the source code for the starting project, you have the source code for the finished project (links in the beginning of this article). If you want to create the starting project on your own, then you have a ling for that too (a first one in this article “.NET Core Series”). So, trust me when I say that you have everything you need for async-await and even more. I really can’t tell what more needs to be published. Just visit all the mentioned links and you will find everything you need.
hi..
in the OwnerRepository class. FindAllAsync() has the OrderBy().
how to place OrderBy() in the RepositoryBase class before ToListAsync() ?
Hello ricy. There is a way, but I don’t recommend that at all due to couple of reasons. First that is completely generic class so your method is not aware which type it’s working with, maybe when you work with owner type you want to order by name but if you work with account you want to order by type etc. So the sole purpose of this FindAllAsync method is to return all the entities from a db and then you can do with them what you want. The second thing is that you can order a list of the entities by ascending for example. But then at some point you need that list ordered descending, you will have to order it again which doubles the order operation (or to create another FindAll method with different order, which makes no sense at all, why would you need two FindAll methods?). So the best way is not to interfere with FindAllAsync method. Let it do its job (returning a list of entities) and once you have your list, order it in a way you like. As I said, there is a way. You can find different implementations of RepositoryPattern where some find all methods accepts parameters like take, skip, orderby etc… so that is another way which will help you to order your list inside the FindAll method.
Can anyone explain to me how IEntityExtensions is working and how you can call IsEmptyObject on Owner object?
Hello oppa. We have the .NET Core Series and I recommend you take a look at it. You will find many different concepts explained in there. In this part of the article ( http://34.65.74.140/net-core-web-development-part2/#extension ) we are talking about extension methods. Basically you can extend any type to call a method upon it. So in this example we have a class Owner which inherits from the IEntity. So I have created and extension class on the IEntity type (IEntityExtensions) and every method in that class could be called with any object of IEntity type. You are basically extending types with extension methods.
All the best.
Thank you!
By the way, will you cover authentication/authorization in your series?
We have already covered this with using JWT, in two parts. The link of the first part is here: http://34.65.74.140/authentication-aspnetcore-jwt-1/, from there you can navigate to second part as well. You will find how to use JWT for authentication and authorization for .NET Core and Angular as well. Very soon, we will publish Identity articles as well.
This was really helpful. I’m getting back into .NET after a multi-year absence and finding ASP.NET Core MVC a bit light compared to a lot of the magic that I’m used to in frameworks like Rails.
I notice that the CodeMaze repos lack a license of any kind, so what’s the policy on using the code you’ve provided? I’d like to use some of this as the base of a couple portfolio projects under the MIT license but I hesitate to do so. And none of the libraries I see on Nuget seem to have async support on .NET Core unless I’m not understanding the Nuget dependency listings, which is possible.
Hello Aaron.
I am glad that our articles could help you and you are right, we don’t have any kind of license on our code. We are dedicated to spread knowledge and to enable easier learning by looking at our source code as well, thus it is free to be used. Otherwise, It wouldn’t make any sense. So, feel free to use it, we are proud that we can help that way.
With the genric repository pattern, what is your recommendation for pages that are more ‘reports’ than CRUD? I see adding the addition methods like GetOwnerWithDetails to handle smaller details, but what to do for reporting data?
Hi XHunterX3. Well, as we did with the GetOwnerWithDetails, we would do for any report. Of course, depending on the complexity of that report we would have one action endpoint but maybe multiple methods, but still the logic of creating those reports is the same. If you need to combine different entities in your report, you can always use the RepositoryContext property from the base class (as we did for the accounts part of the GetOwnerWithDetails).
Thank you for reading the article.
All the best.
Thanks for replying. I think I’m on the right track. Keep the great articles coming!
Hello
I am new to asp.net core so i searched on intranet about repository pattern in ,net core app. I found your article very useful and easy to understand I am reading your article understand how to implement repo. Thanks for this. But I am having one query. I have crated one layered architecture in which i am using repository pattern, but how we can implement the identity class in repository pattern. Because everywhere i have seen article videos they are implementing the identity in the same project application not in seprate classes,
Can you explain in your blog how to implement the identity using repository pattern.
Thanks in advance.
Omkar.
By “Identity class” you mean “User class” ? what do you mean?