Code Maze

  • Blazor WASM 🔥
  • ASP.NET Core Series
  • GraphQL ASP.NET Core
  • ASP.NET Core MVC Series
  • Testing ASP.NET Core Applications
  • EF Core Series
  • HttpClient with ASP.NET Core
  • Azure with ASP.NET Core
  • ASP.NET Core Identity Series
  • IdentityServer4, OAuth, OIDC Series
  • Angular with ASP.NET Core Identity
  • Blazor WebAssembly
  • .NET Collections
  • SOLID Principles in C#
  • ASP.NET Core Web API Best Practices
  • Top REST API Best Practices
  • Angular Development Best Practices
  • 10 Things You Should Avoid in Your ASP.NET Core Controllers
  • C# Back to Basics
  • C# Intermediate
  • Design Patterns in C#
  • Sorting Algorithms in C#
  • Docker Series
  • Angular Series
  • Angular Material Series
  • HTTP Series
  • .NET/C# Author
  • .NET/C# Editor
  • Our Editors
  • Leave Us a Review
  • Code Maze Reviews

Select Page

Implementing Asynchronous Generic Repository in ASP.NET Core

Posted by Marinko Spasojević | Updated Date Jul 26, 2021 | 64

Implementing Asynchronous Generic Repository in ASP.NET Core

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.

  • C# Intermediate Tutorial
  • Global Error Handling in .NET Core Web API
  • ASP.NET Core Authentication with JWT and Angular
  • Create PDF in .NET Core Web API
  • ASP.NET Core Web API with EF Core Code-First Approach

To download the source code for our starting project, you can visit our GitHub repo for the starting project.

Become a patron at Patreon!

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.

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.

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:

Asynchronous Generic Pattern

Now that we cleared that out we can learn how to implement the asynchronous code in .NET Core.

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:

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):

In asynchronous programming we have three return types:

  • Task<TResult> , for an async method that returns a value
  • Task , to use it for an async method that does not return a value
  • void , 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 return Task . 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.

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:

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:

This class is the implementation of the previous interface and it is a base class for accessing the data from the database.

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:

Now, we can continue to the 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.

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.

guest

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.

MarinkoSpasojevic

I’m glad Carlo that you find our articles useful. This is always great to hear.

el mehdi

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 ?

Marinko Spasojević

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.

bhanu

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

Marinko Spasojevic

Hello there. You may try something like this:

sabiha

hello thank you for this usefull article please make the article of .net core 6.0 with sql server .

Allan Odhiambo

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.

Johnson

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 ) ?

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.

vinay khanna

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.

DinhHao

Thank you so much <3

You are most welcome.

dvstd

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

Akwasi Owusu

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 : )

Marinko

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.

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; using System.Linq; using System.Linq.Expressions; using Microsoft.EntityFrameworkCore; namespace Samson.Data.Repositories { public abstract class RepositoryBase : IRepositoryBase where T : class { private readonly SamDBContext _context;

internal RepositoryBase(SamDBContext repositoryContext) { _context = repositoryContext; } public IQueryable FindAll() { return _context.Set ().AsNoTracking(); } public IQueryable FindByCondition(Expression > expression) { 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 (:

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) { var entity2 = RepositoryContext.Set ().Add(entity); 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.

Mohammad Nabulsi

form where you got “_maper ” ?

Please download or at least look at the source code. You will find it injected in the controller.

Andres Cichero

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.

comment image

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.

Zieniu

I have one question. What with transactions???

keshavtaurah

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.

Huan Dam

Yeah, I read the topic about ONION architecture. It is helpful to guide me to create and inject services as keshavtaurah comment above.

feruchio

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.

Eduardo Chávez Hernández

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.

Terry Cola

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.

oppa

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.

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.

Aaron Duty

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.

XHunterX3

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?

Marinko

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!

omkar mhaiskar

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?

wpdiscuz

Join our 20k+ community of experts and learn about our Top 16 Web API Best Practices .

net task generic

Learn about API Management from experts on today’s innovative API architectures to accelerate growth, improve discoverability, transform existing services, and more! All with a hybrid, multi-cloud management platform for APIs across all environments.

Understanding the Whys, Whats, and Whens of ValueTask

' data-src=

Stephen Toub - MSFT

November 7th, 2018 23 8

The .NET Framework 4 saw the introduction of the  System.Threading.Tasks  namespace, and with it the  Task  class. This type and the derived  Task<TResult>  have long since become a staple of .NET programming, key aspects of the asynchronous programming model introduced with C# 5 and its  async  /  await  keywords. In this post, I’ll cover the newer  ValueTask / ValueTask<TResult>  types, which were introduced to help improve asynchronous performance in common use cases where decreased allocation overhead is important.

Task  serves multiple purposes, but at its core it’s a “promise”, an object that represents the eventual completion of some operation. You initiate an operation and get back a  Task  for it, and that  Task  will complete when the operation completes, which may happen synchronously as part of initiating the operation (e.g. accessing some data that was already buffered), asynchronously but complete by the time you get back the  Task  (e.g. accessing some data that wasn’t yet buffered but that was very fast to access), or asynchronously and complete after you’re already holding the  Task  (e.g. accessing some data from across a network). Since operations might complete asynchronously, you either need to block waiting for the results (which often defeats the purpose of the operation having been asynchronous to begin with) or you need to supply a callback that’ll be invoked when the operation completes. In .NET 4, providing such a callback was achieved via  ContinueWith  methods on the  Task , which explicitly exposed the callback model by accepting a delegate to invoke when the  Task  completed:

But with the .NET Framework 4.5 and C# 5,  Task s could simply be  await ed, making it easy to consume the results of an asynchronous operation, and with the generated code being able to optimize all of the aforementioned cases, correctly handling things regardless of whether the operation completes synchronously, completes asynchronously quickly, or completes asynchronously after already (implicitly) providing a callback:

Task  as a class is very flexible and has resulting benefits. For example, you can await it multiple times, by any number of consumers concurrently. You can store one into a dictionary for any number of subsequent consumers to await in the future, which allows it to be used as a cache for asynchronous results. You can block waiting for one to complete should the scenario require that. And you can write and consume a large variety of operations over tasks (sometimes referred to as “combinators”), such as a “when any” operation that asynchronously waits for the first to complete.

However, that flexibility is not needed for the most common case: simply invoking an asynchronous operation and awaiting its resulting task:

In such usage, we don’t need to be able to await the task multiple times. We don’t need to be able to handle concurrent awaits. We don’t need to be able to handle synchronous blocking. We don’t need to write combinators. We simply need to be able to await the resulting promise of the asynchronous operation. This is, after all, how we write synchronous code (e.g.  TResult result = SomeOperation(); ), and it naturally translates to the world of  async  /  await .

Further,  Task  does have a potential downside, in particular for scenarios where instances are created  a lot  and where high-throughput and performance is a key concern:  Task  is a class. As a class, that means that any operation which needs to create one needs to allocate an object, and the more objects that are allocated, the more work the garbage collector (GC) needs to do, and the more resources we spend on it that could be spent doing other things.

The runtime and core libraries mitigate this in many situations. For example, if you write a method like the following:

in the common case there will be space available in the buffer and the operation will complete synchronously. When it does, there’s nothing special about the  Task  that needs to be returned, since there’s no return value: this is the  Task -based equivalent of a  void -returning synchronous method. Thus, the runtime can simply cache a single non-generic  Task  and use that over and over again as the result task for any  async Task  method that completes synchronously (that cached singleton is exposed via `Task.CompletedTask`). Or for example, if you write:

in the common case, we expect there to be some data buffered, in which case this method simply checks  _bufferedCount , sees that it’s larger than  0 , and returns  true ; only if there’s currently no buffered data does it need to perform an operation that might complete asynchronously. And since there are only two possible  Boolean  results ( true  and  false ), there are only two possible  Task<bool>  objects needed to represent all possible result values, and so the runtime is able to cache two such objects and simply return a cached  Task<bool>  with a  Result  of  true , avoiding the need to allocate. Only if the operation completes asynchronously does the method then need to allocate a new  Task<bool> , because it needs to hand back the object to the caller before it knows what the result of the operation will be, and needs to have a unique object into which it can store the result when the operation does complete.

The runtime maintains a small such cache for other types as well, but it’s not feasible to cache everything. For example, a method like:

will also frequently complete synchronously. But unlike the  Boolean  case, this method returns an  Int32  value, which has ~4 billion possible results, and caching a  Task<int>  for all such cases would consume potentially hundreds of gigabytes of memory. The runtime does maintain a small cache for  Task<int> , but only for a few small result values, so for example if this completes synchronously (there’s data in the buffer) with a value like 4, it’ll end up using a cached task, but if it completes synchronously with a value like 42, it’ll end up allocating a new  Task<int> , akin to calling  Task.FromResult(42) .

Many library implementations attempt to mitigate this further by maintaining their own cache as well. For example, the  MemoryStream.ReadAsync  overload introduced in the .NET Framework 4.5 always completes synchronously, since it’s just reading data from memory.  ReadAsync  returns a  Task<int> , where the  Int32  result represents the number of bytes read.  ReadAsync  is often used in a loop, often with the number of bytes requested the same on each call, and often with  ReadAsync  able to fully fulfill that request. Thus, it’s common for repeated calls to  ReadAsync  to return a  Task<int>  synchronously with the same result as it did on the previous call. As such,  MemoryStream  maintains a cache of a single task, the last one it returned successfully. Then on a subsequent call, if the new result matches that of its cached  Task<int> , it just returns the cached one again; otherwise, it uses  Task.FromResult  to create a new one, stores that as its new cached task, and returns it.

Even so, there are many cases where operations complete synchronously and are forced to allocate a  Task<TResult>  to hand back.

ValueTask <TResult> and synchronous completion

All of this motivated the introduction of a new type in .NET Core 2.0 and made available for previous .NET releases via a  System.Threading.Tasks.Extensions  NuGet package:  ValueTask<TResult> .

ValueTask<TResult>  was introduced in .NET Core 2.0 as a struct capable of wrapping either a  TResult  or a  Task<TResult> . This means it can be returned from an async method, and if that method completes synchronously and successfully, nothing need be allocated: we can simply initialize this  ValueTask<TResult>  struct with the  TResult  and return that. Only if the method completes asynchronously does a  Task<TResult>  need to be allocated, with the  ValueTask<TResult>  created to wrap that instance (to minimize the size of  ValueTask<TResult>  and to optimize for the success path, an async method that faults with an unhandled exception will also allocate a  Task<TResult> , so that the  ValueTask<TResult>  can simply wrap that  Task<TResult>  rather than always having to carry around an additional field to store an  Exception ).

With that, a method like  MemoryStream.ReadAsync  that instead returns a  ValueTask<int>  need not be concerned with caching, and can instead be written with code like:

ValueTask <TResult> and asynchronous completion

Being able to write an async method that can complete synchronously without incurring an additional allocation for the result type is a big win. This is why  ValueTask<TResult>  was added to .NET Core 2.0, and why new methods that are expected to be used on hot paths are now defined to return  ValueTask<TResult>  instead of  Task<TResult> . For example, when we added a new  ReadAsync  overload to  Stream  in .NET Core 2.1 in order to be able to pass in a  Memory<byte>  instead of a  byte[] , we made the return type of that method be  ValueTask<int> . That way, Streams (which very often have a  ReadAsync  method that completes synchronously, as in the earlier  MemoryStream  example) can now be used with significantly less allocation.

However, when working on very high-throughput services, we still care about avoiding as much allocation as possible, and that means thinking about reducing and removing allocations associated with asynchronous completion paths as well.

With the  await  model, for any operation that completes asynchronously we need to be able to hand back an object that represents the eventual completion of the operation: the caller needs to be able to hand off a callback that’ll be invoked when the operation completes, and that requires having a unique object on the heap that can serve as the conduit for this specific operation. It doesn’t, however, imply anything about whether that object can be reused once an operation completes. If the object can be reused, then an API can maintain a cache of one or more such objects, and reuse them for serialized operations, meaning it can’t use the same object for multiple in-flight async operations, but it can reuse an object for non-concurrent accesses.

In .NET Core 2.1,  ValueTask<TResult>  was augmented to support such pooling and reuse. Rather than just being able to wrap a  TResult  or a  Task<TResult> , a new interface was introduced,  IValueTaskSource<TResult> , and  ValueTask<TResult>  was augmented to be able to wrap that as well.  IValueTaskSource<TResult>  provides the core support necessary to represent an asynchronous operation to  ValueTask<TResult>  in a similar manner to how  Task<TResult>  does:

GetStatus  is used to satisfy properties like  ValueTask<TResult>.IsCompleted , returning an indication of whether the async operation is still pending or whether it’s completed and how (success or not).  OnCompleted  is used by the  ValueTask<TResult> ‘s awaiter to hook up the callback necessary to continue execution from an  await  when the operation completes. And  GetResult  is used to retrieve the result of the operation, such that after the operation completes, the awaiter can either get the  TResult  or propagate any exception that may have occurred.

Most developers should never have a need to see this interface: methods simply hand back a  ValueTask<TResult>  that may have been constructed to wrap an instance of this interface, and the consumer is none-the-wiser. The interface is primarily there so that developers of performance-focused APIs are able to avoid allocation.

There are several such APIs in .NET Core 2.1. The most notable are  Socket.ReceiveAsync  and  Socket.SendAsync , with new overloads added in 2.1, e.g.

This overload returns a  ValueTask<int> . If the operation completes synchronously, it can simply construct a  ValueTask<int>  with the appropriate result, e.g.

If it completes asynchronously, it can use a pooled object that implements this interface:

The  Socket  implementation maintains one such pooled object for receives and one for sends, such that as long as no more than one of each is outstanding at a time, these overloads will end up being allocation-free, even if they complete operations asynchronously. That’s then further surfaced through  NetworkStream . For example, in .NET Core 2.1,  Stream  exposes:

which  NetworkStream  overrides.  NetworkStream.ReadAsync  just delegates to  Socket.ReceiveAsync , so the wins from  Socket  translate to  NetworkStream , and  NetworkStream.ReadAsync  effectively becomes allocation-free as well.

Non-generic ValueTask

When  ValueTask<TResult>  was introduced in .NET Core 2.0, it was purely about optimizing for the synchronous completion case, in order to avoid having to allocate a  Task<TResult>  to store the  TResult  already available. That also meant that a non-generic  ValueTask  wasn’t necessary: for the synchronous completion case, the  Task.CompletedTask  singleton could just be returned from a  Task -returning method, and was implicitly by the runtime for  async Task  methods.

With the advent of enabling even asynchronous completions to be allocation-free, however, a non-generic  ValueTask  becomes relevant again. Thus, in .NET Core 2.1 we also introduced the non-generic  ValueTask  and  IValueTaskSource . These provide direct counterparts to the generic versions, usable in similar ways, just with a void result.

Implementing IValueTaskSource / IValueTaskSource<T&gt

Most developers should never need to implement these interfaces. They’re also not particularly easy to implement. If you decide you need to, there are several implementations internal to .NET Core 2.1 that can serve as a reference, e.g.

  • AwaitableSocketAsyncEventArgs
  • AsyncOperation<TResult>
  • DefaultPipeReader

To make this easier for developers that do want to do it, in .NET Core 3.0 we plan to introduce all of this logic encapsulated into a  ManualResetValueTaskSourceCore<TResult>  type, a struct that can be encapsulated into another object that implements  IValueTaskSource<TResult>  and/or  IValueTaskSource , with that wrapper type simply delegating to the struct for the bulk of its implementation. You can learn more about this in the associated issue in the dotnet/corefx repo at  https://github.com/dotnet/corefx/issues/32664 .

Valid consumption patterns for ValueTasks

From a surface area perspective,  ValueTask  and  ValueTask<TResult>  are much more limited than  Task  and  Task<TResult> . That’s ok, even desirable, as the primary method for consumption is meant to simply be  await ing them.

However, because  ValueTask  and  ValueTask<TResult>  may wrap reusable objects, there are actually significant constraints on their consumption when compared with  Task  and  Task<TResult> , should someone veer off the desired path of just  await ing them. In general, the following operations should  never  be performed on a  ValueTask  /  ValueTask<TResult> :

  • Awaiting a  ValueTask  /  ValueTask<TResult>  multiple times.  The underlying object may have been recycled already and be in use by another operation. In contrast, a  Task  /  Task<TResult>  will never transition from a complete to incomplete state, so you can await it as many times as you need to, and will always get the same answer every time.
  • Awaiting a  ValueTask  /  ValueTask<TResult>  concurrently.  The underlying object expects to work with only a single callback from a single consumer at a time, and attempting to await it concurrently could easily introduce race conditions and subtle program errors. It’s also just a more specific case of the above bad operation: “awaiting a  ValueTask  /  ValueTask<TResult>  multiple times.” In contrast,  Task  /  Task<TResult>  do support any number of concurrent awaits.
  • Using  .GetAwaiter().GetResult()  when the operation hasn’t yet completed.  The  IValueTaskSource  /  IValueTaskSource<TResult>  implementation need not support blocking until the operation completes, and likely doesn’t, so such an operation is inherently a race condition and is unlikely to behave the way the caller intends. In contrast,  Task  /  Task<TResult>  do enable this, blocking the caller until the task completes.

If you have a  ValueTask  or a  ValueTask<TResult>  and you need to do one of these things, you should use  .AsTask()  to get a  Task  /  Task<TResult>  and then operate on that resulting task object. After that point, you should never interact with that  ValueTask  /  ValueTask<TResult>  again.

The short rule is this:  with a  ValueTask  or a  ValueTask<TResult> , you should either  await  it directly (optionally with  .ConfigureAwait(false) ) or call  AsTask()  on it directly, and then never use it again, e.g.

There is one additional advanced pattern that some developers may choose to use, hopefully only after measuring carefully and finding it provides meaningful benefit. Specifically,  ValueTask  /  ValueTask<TResult>  do expose some properties that speak to the current state of the operation, for example the  IsCompleted  property returning  false  if the operation hasn’t yet completed, and returning  true  if it has (meaning it’s no longer running and may have completed successfully or otherwise), and the  IsCompletedSuccessfully  property returning  true  if and only if it’s completed and completed successfully (meaning attempting to await it or access its result will not result in an exception being thrown). For very hot paths where a developer wants to, for example, avoid some additional costs only necessary on the asynchronous path, these properties can be checked prior to performing one of the operations that essentially invalidates the  ValueTask  /  ValueTask<TResult> , e.g.  await ,  .AsTask() . For example, in the  SocketsHttpHandler  implementation in .NET Core 2.1, the code issues a read on a connection, which returns a  ValueTask<int> . If that operation completed synchronously, then we don’t need to worry about being able to cancel the operation. But if it completes asynchronously, then while it’s running we want to hook up cancellation such that a cancellation request will tear down the connection. As this is a very hot code path, and as profiling showed it to make a small difference, the code is structured essentially as follows:

This pattern is acceptable, because the  ValueTask<int>  isn’t used again after either  .Result  is accessed or it’s awaited.

Should every new asynchronous API return ValueTask / ValueTask<TResult>?

In short, no: the default choice is still  Task  / Task<TResult> .

As highlighted above,  Task  and  Task<TResult>  are easier to use correctly than are  ValueTask  and  ValueTask<TResult> , and so unless the performance implications outweigh the usability implications,  Task  /  Task<TResult> are still preferred. There are also some minor costs associated with returning a  ValueTask<TResult>  instead of a  Task<TResult> , e.g. in microbenchmarks it’s a bit faster to  await  a  Task<TResult>  than it is to  await  a  ValueTask<TResult> , so if you can use cached tasks (e.g. you’re API returns  Task  or  Task<bool> ), you might be better off performance-wise sticking with  Task  and  Task<bool> .  ValueTask  /  ValueTask<TResult>  are also multiple words in size, and so when these are  await d and a field for them is stored in a calling async method’s state machine, they’ll take up a little more space in that state machine object.

However,  ValueTask  /  ValueTask<TResult>  are great choices when a) you expect consumers of your API to only  await  them directly, b) allocation-related overhead is important to avoid for your API, and c) either you expect synchronous completion to be a very common case, or you’re able to effectively pool objects for use with asynchronous completion. When adding abstract, virtual, or interface methods, you also need to consider whether these situations will exist for overrides/implementations of that method.

What’s Next for ValueTask and ValueTask<TResult>?

For the core .NET libraries, we’ll continue to see new  Task  /  Task<TResult> -returning APIs added, but we’ll also see new  ValueTask / ValueTask<TResult> -returning APIs added where appropriate. One key example of the latter is for the new  IAsyncEnumerator<T>  support planned for .NET Core 3.0.  IEnumerator<T>  exposes a  bool -returning  MoveNext  method, and the asynchronous  IAsyncEnumerator<T>  counterpart exposes a  MoveNextAsync method. When we initially started designing this feature, we thought of  MoveNextAsync  as returning a  Task<bool> , which could be made very efficient via cached tasks for the common case of  MoveNextAsync  completing synchronously. However, given how wide-reaching we expect async enumerables to be, and given that they’re based on interfaces that could end up with many different implementations (some of which may care deeply about performance and allocations), and given that the vast, vast majority of consumption will be through  await foreach  language support, we switched to having  MoveNextAsync  return a  ValueTask<bool> . This allows for the synchronous completion case to be fast but also for optimized implementations to use reusable objects to make the asynchronous completion case low-allocation as well. In fact, the C# compiler takes advantage of this when implementing async iterators to make async iterators as allocation-free as possible.

' data-src=

Stephen Toub - MSFT Partner Software Engineer, .NET

' data-src=

23 comments

Comments are closed. Login to edit/delete your existing comments

' data-src=

I think there is a bug with the code snippets you put on the article ! I see the html tags like <span>

' data-src=

Can you point to where exactly?

' data-src=

Thnx for a good article. There is one thing i dont understand. Inside our services we have a cache layer so we use the ValueTask so it dosent create threads when we return from the Cache. The question is when using Task.WhenAll we have to use the .AsTask extension but will this create a thread? Is the code below best practice? var routeTask = (RouteService.GetByPathAsync(path)).AsTask()var routePropertiesTask = RouteService.GetPropertyBag(path).GetAllValuesAsync().AsTask()var businessProfileTask = BusinessProfileService.GetByPathAsync(path).AsTask(); await Task.WhenAll(routeTask, routePropertiesTask, businessProfileTask); var route = await routeTask;var routeProperties = await routePropertiesTask;var businuessProfile = await businessProfileTask;

Using ValueTask vs Task doesn’t impact whether an additional thread is used; just the act of returning Task doesn’t cause an additional thread to be used.  The only difference in this regard is whether there’s an allocation.  Using .AsTask() doesn’t cause an additional thread to be created.

' data-src=

Using Task or ValueTask when you return from cache can’t implicitly create thread. A good resource is available at https://blog.stephencleary.com/2013/11/there-is-no-thread.html

' data-src=

I read this article once a month. Lol. Thanks for this.  For clarity, why does ValueTask<bool> have lower allocation than a cached Task<bool> for IAsycEnumerable async scenarios? Is it due to data locality or because the Tasks state machine will be allocated while ValueTask implementation can reuse a pooled object? 

There’s no allocation difference between a cached Task<bool> and a ValueTask<bool> if MoveNext completes synchronously.  But if it completes asynchronously, if it returned a Task<bool>, we’d need to allocate a Task<bool>.  With ValueTask<bool>, we can create one that wraps the enumerator itself, which implements IValueTaskSource<bool>, which means regardless of whether MoveNext completes synchronously or asynchronously, there’s no allocation for its return.  That means that for the entire lifetime of the async enumerable, there’s just the one allocation of overhead incurred for the whole enumeration: the enumerable object, which is reused as the enumerator, which is also reused as the IValueTaskSource<bool> backing the MoveNext ValueTask<bool>, which is also reused as the IValueTaskSource backing the ValueTask returned from DisposeAsync.

' data-src=

Sorry but I find this extremely complicated to an already overly complicated and disruptive API.  Not only do we now have to account for Task but now we have ValueTask and have to know the difference between the two.  What happens in the case of simply eliding the ValueTask?  In Task, it is designed to simply return the Task without the use of the async/await.  This appears to be gone now with ValueTask and you are forced to await without simply returning a Task — or if you do you are forced to allocate.  Plus now we have IAsyncDispose and IAsyncEnumerable, what’s next ObjectAsync?  ALL THE ASYNC AND ASYNC ISN’T EVEN A WORD111  We can do better:  https://developercommunity.visualstudio.com/idea/583945/improve-asynchronous-programming-model.html

> have to know the difference between the two

Not really.  If you’re writing an API, just keep using Task: if you need even more performance and care to optimize further, you can then consider employing ValueTask.  If you’re consuming an API, just await it: it makes no difference whether you’re awaiting a Task or a ValueTask.

> What happens in the case of simply eliding the ValueTask?  In Task, it is designed to simply return the Task without the use of the async/await.  This appears to be gone now with ValueTask

I’m not understanding.  What’s gone?  If you have a ValueTask and you want to return it, just return it.

> or if you do you are forced to allocate

Forced to allocate what?

I appreciate your engagement here, @Stephen. It is much respected and welcomed. > Forced to allocate what? Forced to allocate a new Task/object/reference. > If you’re consuming an API, just await it What if we do not want to await it, was the point I was making.  There are a lot of gotchas to awaiting as outlined in the many comments of evidence in my Vote (which got 131 votes in UserVoice, BTW — meaning that I am not simply speaking for myself here). There is a collective assumption of awaiting, but what is overlooked, or perhaps forgotten, is that the system is, of course, also natively designed to not await a Task — that is, to elide await/async and reduce the hidden magic machinery that is produced by the compiler. That is one of many points of confusion now. Consider: do you want asynchronous or synchronous? If asynchronous, do you want to elide async/await or not? Further, do you want to Task or ValueTask? Lots of decisions and required knowledge here and the ask is to consider reducing the complexity in a future version of .NET. >  if you need even more performance and care to optimize further, you can then consider employing ValueTask Who wants to make slow software?  I know you state that we should keep using Task — and I want to believe you!  However, all the new APIs and new code being released in the wild now are running counter to this by using ValueTask. I hope you can understand the confusion here. In short, the exception being raised here (pardon the pun) is that we now have an incredibly fragmented ecosystem between synchronous and asynchronous APIs.  The asynchronous APIs are now further being fragmented with ValueTask.  This fragmentation is a clear sign that the APIs as initially designed did not accurately model the problem space from the outset.  This is, of course, completely understandable as it is an incredibly challenging problem to solve. The request is that perhaps going forward for a future version of .NET, we can somehow reconcile all of these identified friction points to create a much more congruent, cohesive API that improves the developer experience, reduces the decisions/confusion, and (perhaps most importantly) returns elegance/aesthetics to our API design surfaces (and resulting ecosystem). Thank you in advance for any consideration and further dialogue.

> Forced to allocate a new Task/object/reference.

I do not understand.  You wrote “This appears to be gone now with ValueTask and you are forced to await without simply returning a Task — or if you do you are forced to allocate.”  You can absolutely just return a ValueTask.  It’s no different than Task in that regard.

> What if we do not want to await it, was the point I was making.

The 99.9% use case for all of these APIs is to await it.  If you’re in the minority case where you don’t want to and you want to do something more complicated with it, then call .AsTask() on it, and now you’ve got your Task that is more flexible.  This is no different in my mind than an API returning an `IEnumerable<T>`; if you just want to iterate through it, you can do so, but if you want to do more complicated things, enumerating it multiple times and being guaranteed to get the same results every time, caching it for later use, etc., you can call ToArray or ToList to get a more functional copy.

> that is, to elide await/async and reduce the hidden magic machinery that is produced by the compiler

All of that exists for both Task and ValueTask.  I don’t understand the point you’re trying to make.  When you await either a Task or a ValueTask, if they’ve already completed, the code will just continue executing synchronously, no callbacks, no yielding out of the MoveNext state machine method.

>  Consider: do you want asynchronous or synchronous?

Yes, that is a fundamental decision you have to make when designing an API.  That was true long before async/await and Task, and it continues to be true.

> If asynchronous, do you want to elide async/await or not?

I don’t understand this, nor how it has any bearing on Task vs ValueTask.

> do you want to Task or ValueTask?

I shared my rubric.

> Who wants to make slow software?

There are tons of possible “optimizations” one can make in their code that have absolutely zero observable impact, and each of those “optimizations” often entails more complicated code that takes up more space, is more difficult to debug, is more difficult to maintain, and so on.  The observable difference between Task and ValueTask from a performance perspective in the majority case is non-existent.  It’s only for when you’re developing something that’s going to be used on a critical hot path over and over and over again, where an extra allocation matters.

> However, all the new APIs and new code being released in the wild now are running counter to this by using ValueTask.

a) That isn’t true; there are new APIs being shipped in .NET Core that return Task instead of ValueTask.

b) The core libraries in .NET are special.  The functionality is all library code that may be used in a wide variety of situations, including cases where the functionality is in fact on very hot paths.  It’s much more likely that code in the core libraries benefit from ValueTask than does code outside of the core libraries. Further, many of the places you see ValueTask being exposed (e.g. IAsyncDisposable.DisposeAsync, Stream.ReadAsync) are interfaces / abstract / virtual methods that are meant to be overridden by 3rd parties, such that we can’t predict whether the situations will necessitate the utmost in performance, and in these cases, we’ve opted to enable that extra inch of perf in exchange for the usability drawbacks, which for these methods are generally minimal exactly because of how we expect them to be used (e.g. it would be very strange to see the result of DisposeAsync stored into a collection for use by many other consumers later on… it’s just not the use case).

I fundamentally disagree with a variety of your conclusions.  We may need to agree to disagree.  Thanks.

>The 99.9% use case for all of these APIs is to await it.

Yet it is also designed to not await. Hence, fragmentation and confusion. All the guidance does use async/await but there are many documented pitfalls and explanations that occur when doing so. I personally like to elide async/await as I find it simpler and avoid all of the generated compiler magic that seems to cause so much grief. > I don’t understand the point you’re trying to make. When you await either a Task or a ValueTask… The point is that the system is naturally designed to not await by default, and you can, in fact, elide these keywords. The async/await are not required and have been designed as such from the outset. > That was true long before async/await and Task, and it continues to be true. Agreed. async/await was a step in the right direction as it did improve over the previous model. The ask here is to further simplify async/await as it has its own set of pitfalls and areas where it can improve, not to mention its increasing fragmentation of classes and functionality, each requiring their own set of rules and knowledge for successful implementation. > I shared my rubric. And indeed you did. Another point in consideration: the sheer amount of explanation around this space. While incredibly detailed and informative, to me it’s a sign that some additional work can be done to further simplify the overall design. That is really all the point being made here is. > That isn’t true; there are new APIs being shipped in .NET Core that return Task instead of ValueTask. Example of this, please? And do you happen to know the ratio between new APIs with ValueTask vs Task? All the new ones that I have seen are using ValueTask which lead to my confusion here. > The core libraries in .NET are special. Right, and developers use them as a reference point for building their own code and making their own decisions. If all the new (or at least a sizable majority of all the new) APIs are pointing in one direction, while at the same time the recommendation is to continue using the established/historical direction, confusion will ensue — and has. > I fundamentally disagree with a variety of your conclusions. It sounds like you aren’t even understanding half of my concerns, so I cannot fault you. The ask here is for further consideration in future .NET versions to improve the asynchronous workflow. Although, I am guessing at this point something competitive will need to arise from Apple/Google to get your attention. In any case, I do appreciate you taking the time to address developers and their concerns here on your posts. I have always been a big fan of you and your writings and will continue to be.

FWIW: https://developercommunity.visualstudio.com/comments/603640/view.html

' data-src=

When writing a method that returns ValueTask<T>, is it more efficient to make it non-async and manually wrap a synchronous result in a new ValueTask<T> or will the compiler automatically optimize it in the synchronous case?

The async method builder implementation handles the case where the method completes synchronously, and returns a `new ValueTask<T>(T)` instead of creating a `Task<T>` and returning `new ValueTask<T>(task)`.

' data-src=

Thanks for the post.  We just had a very lively discussion on my team as a result of this post.  Love it.

Glad it was useful!

' data-src=

You should be writing a book – you explain brilliantly !

' data-src=

Stephen quick question for you.

My application sends a lot of data across the wire (TCP). What I like to do is start the sending process and await the task later. I gather from your post that It would be “safe” to do this with a ValueTask as long as I don’t await the same ValueTask twice?

For example, is the below code an acceptable use of ValueTask.

' data-src=

Yes, that should be fine to do.

' data-src=

This is great article, so clear and comprehensive. Thank You very much.

' data-src=

Great article, thanks 🙂

light-theme-icon

  • skip navigation Telerik UI for ASP.NET Core Product Bundles DevCraft All Telerik .NET tools and Kendo UI JavaScript components in one package. Now enhanced with: NEW : Design Kits for Figma

Getting Started with Generics in .NET

assis-zang-bio

Generic types are part of the fundamentals of the C# language and can help the developer to save time by reusing code. Learn more about generic types and how to create an API in .NET 7 using the generic repository pattern.

Generic types are a valuable feature available in C# since the earliest versions of .NET. Despite being part of the fundamentals of the language, many developers avoid using them—maybe because they don’t know exactly how to use them or maybe even don’t know about them.

In this post, you will learn a little about generics in the context of .NET and how to use them in a real scenario.

What Are Generics in .NET?

Generics in the .NET context are classes, structures, interfaces and methods with placeholders for one or more of the types they store or use.

Imagine that you need to create an application that will perform the registration of new customers. However, when performing the registration, it is necessary to save this information in other databases such as a history table—another table from an external supplier—and so we have at least three inserts to do in each table. So as not to repeat the same method and duplicate the code, we can create a method that accepts a generic class, and that way the three inserts can be done using the same method.

Among the benefits of generics are the reduction of repeated code, performance gains and type safety.

Pros and Cons of Generics

  • Type security: The data type is checked during runtime, eliminating the need to create extra code to check.
  • Less code writing:  With generic types, it is possible to reuse classes and methods, making less code to be written.
  • Better performance: Generic types generally perform best when storing and manipulating value types.
  • Simplification of dynamically generated code: Dispenses with type generation when dynamically generated. So it is possible to use lightweight dynamic methods instead of generating entire assemblies.
  • There are no cons to using generics, but there are some limitations such as enumerations that cannot have generic type parameters and the fact that .NET does not support context-bound generic types.

When to Use Generics

Generic classes and methods combine reuse, type safety and efficiency in a way that non-generic alternatives cannot. So whenever you want to utilize any of these benefits, consider using generics.

The Generic Repository Pattern

In small projects with few entities, it is common to find one repository for each of the entities, perform CRUD operations for each of them, and repeat the code several times.

However, imagine a project where it is necessary to implement the CRUD of several entities. Besides the repetition of code, the maintenance of this project would also be expensive—after all, there would be several classes to be changed.

With the generic repository pattern, we eliminate these problems by creating a single repository that can be shared with as many entities as needed.

Practical Example

To demonstrate the use of generics in a real example, we will create a .NET application in a scenario where it will be necessary to implement a CRUD for two entities—one for the Product entity and another for the Seller entity. Thus we’ll see in practice how it is possible to reuse code through generics.

To develop the application, we’ll use .NET 7. You can access the project’s source code here .

Project Dependencies

You need to add the project dependencies—either directly in the project code “ProductCatalog.csproj”:

or by downloading the NuGet packages:

  • Microsoft.EntityFrameworkCore 7.0.0
  • Microsoft.EntityFrameworkCore.Design 7.0.0
  • Microsoft.EntityFrameworkCore.Sqlite 7.0.0
  • Microsoft.EntityFrameworkCore.Tools 7.0.0

Create the Application

First, let’s create the solution and the project. So, in Visual Studio:

  • Create a new project
  • Choose ASP. NET Core Web API
  • Name it (“ProductCatalog” is my suggestion)
  • Choose .NET 7.0 (Standard Term Support)
  • Uncheck “Use controllers”
  • Click Create

Create the Entities Classes

Let’s create two model classes that will represent the Product and Seller entities. So, create a new folder called “Models” and, inside it, create the classes below:

Create the Context Class

The context class will be used to add the database settings which in this case will be SQLite. So, create a new folder called “Data” and, inside it, create the class below:

  • ProductCatalogContext

Create the Generic Repository

The repository will contain the methods responsible for executing the CRUD functions. As we will use the generic repository pattern, we will have only one class and one interface that will be shared by the Product and Seller entities.

So inside the “Data” folder, create a new folder called “Repository.” Inside it, create a new folder called “Interfaces,” and inside that create the interface below:

  • IGenericRepository

💡 Note that the methods expect as a parameter a generic type represented in C# with the expression “<T>” and in the case of getting methods they also return generic types.

The next step is to create the class that will implement the interface methods. Then inside the “Repository” folder create the class below:

  • GenericRepository

💡 Note that in the class above we have the global variable “_entities” that receives the value of data collections, depending on which entity is being passed as a parameter.

We also have the methods that perform CRUD operations through the EF Core extension methods like “FindAsync,” “AddAsync,” etc.

Add the Dependencies Injections

To add dependency injections, in the Program.cs file add the following lines of code just below the “builder.Services.AddSwaggerGen()” snippet:

Create the API Endpoints

Below is the code of all API endpoints, both Product and Seller. Then, still in the Program.cs file, add the code below before the snippet “app.Run()”:

💡 Note that in each endpoint when the interface “IGenericRepository” is declared, we are passing as an argument the entity corresponding to the scope of the API. That is, in the Product API we declare IGenericRepository<Product> , and in the Seller API, IGenericRepository<Seller> . This way we can use the same interface for any entity that our code may have, as it expects a generic type, regardless of what it is.

Generate the Migrations

To generate the database migrations, we need to run the EF Core commands. For that, we need to install the .NET CLI tools. Otherwise, the commands will result in an error.

The first command will create a migration called InitialModel and the second will have EF create a database and schema from the migration.

More information about migrations is available in Microsoft’s official documentation .

You can run the commands below in a project root terminal.

Alternatively, run the following commands from the Package Manager Console in Visual Studio:

Test the Application

To test the application, just run the project and perform the CRUD operations. The GIF below demonstrates using the Swagger interface to perform the create functions in both APIs.

Through the example taught in the article, we can see in practice how the use of generics can be a great option when the objective is to save time through code reuse.

So always consider making use of generics when the opportunity arises. Doing so, you will be acquiring all the advantages of generic types in addition to demonstrating that you care about creating reusable code.

ASP.NET Core REPL: Share code snippets, edit demos on the spot with ASP.NET Core REPL

Assis Zang is a software developer from Brazil, developing in the .NET platform since 2017. In his free time, he enjoys playing video games and reading good books. You can follow him at: LinkedIn  and Github .

Related Posts

Creating good monoliths in asp.net core, applying the cqrs pattern in an asp.net core application, build 2022 updates for asp.net core developers, all articles.

  • ASP.NET Core
  • ASP.NET MVC
  • ASP.NET AJAX
  • Blazor Desktop/.NET MAUI
  • Design Systems
  • Document Processing
  • Accessibility

net task generic

Latest Stories in Your Inbox

Subscribe to be the first to get our expert-written articles and tutorials for developers!

All fields are required

Loading animation

Progress collects the Personal Information set out in our Privacy Policy and the Supplemental Privacy notice for residents of California and other US States and uses it for the purposes stated in that policy.

You can also ask us not to share your Personal Information to third parties here: Do Not Sell or Share My Info

By submitting this form, I understand and acknowledge my data will be processed in accordance with Progress' Privacy Policy .

I agree to receive email communications from Progress Software or its Partners , containing information about Progress Software’s products. I understand I may opt out from marketing communication at any time here or through the opt out option placed in the e-mail communication received.

By submitting this form, you understand and agree that your personal data will be processed by Progress Software or its Partners as described in our Privacy Policy . You may opt out from marketing communication at any time here or through the opt out option placed in the e-mail communication sent by us or our Partners.

We see that you have already chosen to receive marketing materials from us. If you wish to change this at any time you may do so by clicking here .

Thank you for your continued interest in Progress. Based on either your previous activity on our websites or our ongoing relationship, we will keep you updated on our products, solutions, services, company news and events. If you decide that you want to be removed from our mailing lists at any time, you can change your contact preferences by clicking here .

FUTURES - ON AI

AI21 Labs Outperforms Generic LLMs With Task-Specific Models

AI21 Labs cofounders Prof. Amnon Shashua (Left), Ori Goshen (Middle) and Prof. Yoav Shoham (Right) Roei ShorAI21 Labs, established in 2017 by tech visionaries Yoav Shoham, Ori Goshen and Amnon Shashua in Tel Aviv, Israel, has emerged as a leader in the field of generative AI and large language models. Specializing in natural language processing, AI21 Labs develops AI systems adept at understanding and generating human-like text. The company made a significant mark with the launch of Wordtune, an AI-based writing assistant, and further expanded its portfolio with the introduction of AI21 Studio. This platform enables businesses to create custom text-based applications leveraging the company’s sophisticated AI models, including the advanced Jurassic-2 model.

AI21 Labs has been at the forefront of innovation in the AI space, consistently pushing the boundaries with products like Wordtune AI21 Studio, which allows for the creation of generative AI-driven applications. Notably, AI21 Labs has also secured significant funding, raising $208 million in a Series C funding round, which, along with previous rounds, brings its total raised to $336 million and valuing the company at $1.4 billion.

The company has partnered with Amazon Web Services to streamline the use of generative AI by integrating its advanced Jurassic-2 language models into AWS’s Bedrock service. This collaboration aims to simplify the development of AI-powered applications for large organizations by offering easy access to pre-trained models through Bedrock, overcoming common deployment challenges like procurement and technical integration. By leveraging Jurassic-2 models within AWS, customers can rapidly develop and scale their AI applications without managing the underlying infrastructure.

In a recent conversation with Ori Goshen, co-CEO of AI21, I had the opportunity to delve deep into the intricacies of AI development and its profound impact on businesses.

AI21 stands out in the crowded landscape of large language models with its pioneering approach to creating specialized models tailored for specific industry needs. This dialogue not only offered insights into AI21’s strategic direction but also highlighted broader trends shaping the AI industry.

As someone who closely tracks the emerging technology space, I found the shift towards specialized, task-specific models particularly compelling. Ori shared that unlike the general-purpose models designed for a wide range of tasks, AI21 focuses on developing small language models that excel in specific applications, such as summarizing financial reports or generating product descriptions. This approach is driven by the understanding that enterprises face unique challenges when dealing with generative AI.

The advantages of this strategy are manifold. Specialized models are not only more accurate but also offer benefits in terms of cost, speed and ease of integration. This marks a significant evolution in AI development, emphasizing efficiency and purpose-built solutions over the pursuit of larger, more complex models.

Flexibility in deployment is another critical aspect that Ori and I discussed. AI21’s models are designed to adapt to various deployment scenarios, including cloud environments and on-premises deployments. This is particularly important for industries that are tightly regulated or handle sensitive data, offering them the security and efficiency they need to leverage AI capabilities.

During our conversation, the balance between model size and dataset quality emerged as a recurring theme. Ori argued that the real innovation lies in the specialization of models for specific tasks, which can significantly enhance the performance of AI applications. This perspective resonates with my understanding of the AI space, where the future lies in leveraging a portfolio of specialized models for different functions.

Wordtune, AI21’s proprietary tool, is a prime example of the practical application of specialized models. As a user myself, I was curious about the technology underpinning Wordtune. Ori confirmed that it utilizes AI21’s task-specific models, highlighting the company’s commitment to providing solutions that are not only innovative but also highly relevant to real-world applications.

Our discussion also touched upon the human element in AI, particularly in the training and fine-tuning of models. Ori described a meticulous process involving supervised fine-tuning with high-quality labeled data and reinforcement learning from human feedback, ensuring the models are aligned with human expectations and can perform tasks reliably.

Looking ahead, Ori shared insights on the potential for integrating AI models on edge devices. This suggests a move towards hybrid models that combine local processing with cloud-based computation for more complex tasks, indicating continuous innovation within the AI field.

As we concluded our discussion, we touched upon the competitive landscape of the LLM market. Ori expressed confidence that the market is diversifying, with multiple vendors thriving by catering to different needs. This view is in line with my belief that the AI industry will continue to evolve dynamically, driven by the demand for specialized and effective models.

My conversation with Ori Goshen was not just an exploration of AI21’s pioneering role in AI technology but a broader reflection on the trends shaping the future of AI. As the industry moves towards more specialized, task-oriented models, the potential for enterprises to benefit from precisely tailored AI solutions is immense.

AI21 Labs distinguishes itself in the competitive large language model landscape with its emphasis on specialized, task-specific models. This tailored approach marks the beginning of a new trend of small language models, which provide businesses with the accuracy and performance they expect.

{Categories} _Category: Inspiration{/Categories} {URL}https://www.forbes.com/sites/janakirammsv/2024/02/23/ai21-labs-outperforms-generic-llms-with-task-specific-models/{/URL} {Author}Janakiram MSV, Senior Contributor{/Author} {Image}https://imageio.forbes.com/specials-images/imageserve/65d881b1dd8fa05a2f54b1b3/0x0.jpg?format=jpg&height=600&width=1200&fit=bounds{/Image} {Keywords}Cloud,/cloud,Innovation,/innovation,Cloud,/cloud,AI,/ai,technology,standard{/Keywords} {Source}All{/Source} {Thumb}{/Thumb}

Weber Shandwick - Futures

2023 WEBER SHANDWICK // ALL RIGHTS RESERVED // PRIVACY POLICY // CCPA PRIVACY POLICY

  • GEN AI NEWSFEED
  • READ // WATCH // LISTEN
  • ON AI NEWSLETTER

Welcome Back!

Login to your account below

Remember Me

Retrieve your password

Please enter your username or email address to reset your password.

Add New Playlist

- Select Visibility - Public Private

More From Forbes

Ai21 labs outperforms generic llms with task-specific models.

  • Share to Facebook
  • Share to Twitter
  • Share to Linkedin

AI21 Labs cofounders Prof. Amnon Shashua (Left), Ori Goshen (Middle) and Prof. Yoav Shoham (Right)

AI21 Labs, established in 2017 by tech visionaries Yoav Shoham, Ori Goshen and Amnon Shashua in Tel Aviv, Israel, has emerged as a leader in the field of generative AI and large language models. Specializing in natural language processing, AI21 Labs develops AI systems adept at understanding and generating human-like text. The company made a significant mark with the launch of Wordtune , an AI-based writing assistant, and further expanded its portfolio with the introduction of AI21 Studio . This platform enables businesses to create custom text-based applications leveraging the company's sophisticated AI models, including the advanced Jurassic-2 model .

AI21 Labs has been at the forefront of innovation in the AI space, consistently pushing the boundaries with products like Wordtune AI21 Studio, which allows for the creation of generative AI-driven applications. Notably, AI21 Labs has also secured significant funding, raising $208 million in a Series C funding round, which, along with previous rounds, brings its total raised to $336 million and valuing the company at $1.4 billion.

The company has partnered with Amazon Web Services to streamline the use of generative AI by integrating its advanced Jurassic-2 language models into AWS's Bedrock service. This collaboration aims to simplify the development of AI-powered applications for large organizations by offering easy access to pre-trained models through Bedrock, overcoming common deployment challenges like procurement and technical integration. By leveraging Jurassic-2 models within AWS, customers can rapidly develop and scale their AI applications without managing the underlying infrastructure.

In a recent conversation with Ori Goshen, co-CEO of AI21, I had the opportunity to delve deep into the intricacies of AI development and its profound impact on businesses.

AI21 stands out in the crowded landscape of large language models with its pioneering approach to creating specialized models tailored for specific industry needs. This dialogue not only offered insights into AI21's strategic direction but also highlighted broader trends shaping the AI industry.

Incredibly, The Russian Air Force Has Lost Another Rare A-50 Radar Plane

Ios 17 4 apple reveals surprise new iphone feature that will change everything, suspect arrested in on campus death of georgia student laken riley what we know.

As someone who closely tracks the emerging technology space, I found the shift towards specialized, task-specific models particularly compelling. Ori shared that unlike the general-purpose models designed for a wide range of tasks, AI21 focuses on developing small language models that excel in specific applications, such as summarizing financial reports or generating product descriptions. This approach is driven by the understanding that enterprises face unique challenges when dealing with generative AI.

The advantages of this strategy are manifold. Specialized models are not only more accurate but also offer benefits in terms of cost, speed and ease of integration. This marks a significant evolution in AI development, emphasizing efficiency and purpose-built solutions over the pursuit of larger, more complex models.

Flexibility in deployment is another critical aspect that Ori and I discussed. AI21's models are designed to adapt to various deployment scenarios, including cloud environments and on-premises deployments. This is particularly important for industries that are tightly regulated or handle sensitive data, offering them the security and efficiency they need to leverage AI capabilities.

During our conversation, the balance between model size and dataset quality emerged as a recurring theme. Ori argued that the real innovation lies in the specialization of models for specific tasks, which can significantly enhance the performance of AI applications. This perspective resonates with my understanding of the AI space, where the future lies in leveraging a portfolio of specialized models for different functions.

Wordtune, AI21's proprietary tool, is a prime example of the practical application of specialized models. As a user myself, I was curious about the technology underpinning Wordtune. Ori confirmed that it utilizes AI21's task-specific models, highlighting the company's commitment to providing solutions that are not only innovative but also highly relevant to real-world applications.

Our discussion also touched upon the human element in AI, particularly in the training and fine-tuning of models. Ori described a meticulous process involving supervised fine-tuning with high-quality labeled data and reinforcement learning from human feedback, ensuring the models are aligned with human expectations and can perform tasks reliably.

Looking ahead, Ori shared insights on the potential for integrating AI models on edge devices. This suggests a move towards hybrid models that combine local processing with cloud-based computation for more complex tasks, indicating continuous innovation within the AI field.

As we concluded our discussion, we touched upon the competitive landscape of the LLM market. Ori expressed confidence that the market is diversifying, with multiple vendors thriving by catering to different needs. This view is in line with my belief that the AI industry will continue to evolve dynamically, driven by the demand for specialized and effective models.

My conversation with Ori Goshen was not just an exploration of AI21's pioneering role in AI technology but a broader reflection on the trends shaping the future of AI. As the industry moves towards more specialized, task-oriented models, the potential for enterprises to benefit from precisely tailored AI solutions is immense.

AI21 Labs distinguishes itself in the competitive large language model landscape with its emphasis on specialized, task-specific models. This tailored approach marks the beginning of a new trend of small language models, which provide businesses with the accuracy and performance they expect.

Janakiram MSV

  • Editorial Standards
  • Reprints & Permissions

How did cultural practices over Lunar New Year come about and what do they symbolise?

Year of the Dragon

The new year is all about having a renewed outlook filled with fresh hopes for all the positives — good luck, good health, prosperity, and happiness.

For some Chinese communities observing Lunar New Year, adhering to specific rituals helps set the tone for the rest of the year.

We asked you to share some of your auspicious practices so we can find out more about their origins and the symbolic meanings they uphold.

Let's unpack.

A couple wearing face masks picking out red decorations from a stall

The Chinese culture is doused in symbolism and homophones, with origins rooted in legends that are thousands of years old, explains Ning Zhang, a Chinese language expert from the University of Adelaide.

With that in mind, she says every practice is intended to uphold the positive themes that underpin any Lunar New Year or Spring Festival celebration.

The list of 'what not to do'

Can't sweep the floor or do any cleaning on the first day of the new year. — Anita
I was told to never sweep the floor, wash my hair, or buy new shoes on Chinese New Year. — Emnabelle
You have to clean the whole house before Chinese New Year reunion dinner. Nor can you get a haircut or buy shoes in the first seven days of the new year as well. — Grant
1) Not washing or cutting your hair on new year's day! 2) Not sweeping the house or cleaning around the new year. — Fran
We are not allowed to sweep or clean the house on the first day of the Chinese New Year. This is to ensure we don't sweep away any luck or good fortune. — Michelle

Pan Wang, an Associate Professor in Chinese and Asian studies from the University of New South Wales, explains how homophones come into full swing during celebrations.

Dr Wang says the Chinese word for shoes (xie) has the same pronunciation as evil (xie).

So to buy new shoes is to attract evil spirits and to throw away old ones is to ward off evil spirits.

There are also several variations behind why you shouldn't wash or cut your hair on New Year's Day.

One of which she says, plays on homophones but also links back to a grim past.

"In history, the Manchus imposed their identity on the Han, by forcing them to clean their foreheads and braid their hair, which was rejected by the Han," Dr Wang says.

So missing this ancient practice (si jiu) speaks the same as "uncles will die" (si jiu).

That brought about the saying: "If you cut your hair in the first month of the Lunar New Year, your uncles will die."

Etiquette when visiting others

Don't gift odd amounts of money in hong bao (especially not unlucky number four). Wear red for luck. — Susannah
We only say positive things all day and wear lots of red for good luck! — Anna
Must visit those older than you on New Year's Day and bring fruits to pay respects.  —   Cassie

Dr Zhang explains the art behind gifting hong baos — envelopes of money.

It draws on the concept of double happiness, derived from the Chinese characters for joy, that's often written in pairs as part of well-wishes during weddings.

"So anything in double is considered to be better or luckier," Dr Zhang says. 

And she says this transfers on how even numbers are preferred over odd ones.

Fruits are also seen as emblems of well-wishes, often by wordplay.

For example, apples (ping) sounds the same as peace (ping) and oranges (cheng) sounds the same for success (cheng).

"Fruits are not only meant for gifting, they can also go on dining tables as decorations to invite good wishes for yourself," says Dr Zhang.

Feasting on foods with meaning

The first dish we eat is Lo han jai (Buddha's Delight) to symbolise purifying and cleansing and we only eat our nian gao (new year sweet rice cake) on the second day of the new year to officially ring in the new year. — Anna
Eat Yee Sang (fish salad) together so that you have abundance in the new year because fish = yu = homophone for abundance. This salad is tossed together by everybody while saying "lo hei" out loud. The higher the tossing, the more abundance in the new year. — Susannah
The last home cooked meal is New Year's Eve dinner and, until the third day of the New Year, the kitchen is closed. Only leftovers are consumed in the first two days. This is as implied that the good fortune of abundant food from previous year will continue to this year. — Jack

"What you put on the dinner table or what you eat also varies between regions and even countries," Dr Zhang says.

For instance, she says, Yee Sang is a dish commonly found in South-East Asian countries such as Malaysia and Singapore.

It consists of various vegetables sporting an array of colours that each represent different wishes.

Where did it all start?

It's hard to pinpoint exactly when and where these traditions originated from.

"Cultural customs and traditions are passed on generation by generation through word of mouth," Dr Wang says.

She believes these practices have roots stemming from various sources such as history, religion, science and feng shui — an art relating to the harmony and flow of energy.

Customs and rituals may also vary between groups of people, depending on regions and dialects where words can hold different meanings.

Don't call it superstition

Dr Wang advises against labelling these cultural customs as superstition.

"The word 'superstition' (mixin) is always coupled with the words 'feudal thoughts' (fengjian) in a Chinese expression known as 'fengjian mixin,'" she says.

"The term is perceived negatively by the public where it was heavily attacked in a campaign during the Cultural Revolution in China back in the 1960s to 70s."

Tingting Liu, who specialises in social and cultural anthropology at the University of Technology in Sydney, expands on this.

"In mainland China, we can see the co-existence of anti-superstition campaigns and the emergence of ritualistic practices to attract luck and the likes," Dr Liu says. 

Despite the past campaigns, Dr Liu says those practices continue to exist until today and may be more robust in communities outside China because they were not affected by the revolution.

She describes current practices as a "compromise", where some are retained while others may be simplified over generations. 

No different from horoscopes or tarot readings

Speaking from personal observation, Dr Liu says, "For my generation, we believe in astrology signs and tarot readings."

She says they are no different from the rituals and customs observed over the New Year because "they are all centred upon ourselves".

"These belief systems help individuals to reduce the uncertainty in their lives," Dr Liu says, especially in the face of a tumultuous year with poor economic outlook.

The emergence of new generations has shifted the focus of cultural practices to be more individualistic, moving away from the Confucius roots which operates on a hierarchy that preferences male, says Dr Liu.

"It's less about the whole family or whole village or country now."

A way to uphold cultural heritage

Dr Wang says whether these practices ring true or not is irrelevant.

"It exists if you believe in it — as part of a tradition or cultural heritage," she says.

"People adhere to the rules because it shows respect for the tradition, including ancestors and their sayings, and demonstrates their reverences."

A little girl with pigtails and boy with spiked hair bow down to two adults while holding red packets

Nostalgia is another aspect, she says.

For instance, following the way of life of a beloved deceased family member could be one way of honouring them.

Furthermore, she says "there is no harm in following the advice" and to an extent, they also "enrich the cultural experience of the new year".

Dr Wang acknowledges that choosing to believe is a personal choice and practices should not be generalised as they can be unique to groups of people.

However, she believes continuing on traditions form "a collective identity and advocacy that conveys shared values".

"Shared beliefs also bring people closer together and distinguishes them from other cultural groups."

  • X (formerly Twitter)

Related Stories

When is lunar new year 2024 and where can i join celebrations.

A line of men decked in yellow holding up a dragon on stick while being surrounded by a crowd

Tomorrow is the start of the Lunar New Year. How well do you know the zodiac animal, what to eat and what to say?

Saturday marks the first day of the Year of the Dragon.

How young Asian Australians are changing Lunar New Year traditions

Kimi Chen hoping for red envelopes at Lunar New Year

  • Arts, Culture and Entertainment
  • Carnivals and Festivals

Dot Net Tutorials

Generic Repository Pattern in ASP.NET Core MVC

Back to: ASP.NET Core Tutorials For Beginners and Professionals

Generic Repository Pattern in ASP.NET Core MVC with EF Core

In this article, I will discuss How to Implement the Generic Repository Design Pattern in ASP.NET Core MVC with Entity Framework Core. This is a continuation of our previous article, where we discussed How to Implement the Basic or Non-Generic Repository Pattern in ASP.NET Core MVC with Entity Framework Core. We will work with the same application we worked on in our previous two articles.

Why do we need a Generic Repository Design Pattern in ASP.NET Core MVC?

As we already discussed, in a Basic or Non-Generic Repository, we need to create separate repositories for every entity in our application. For example, if we have three entities, Employee, Product, and Customer, we must create three repositories: EmployeeRepository, ProductRepository, and CustomerRepository.

This is boring and repetitive work, especially if all the repositories will do the same kind of work (i.e., typically database CRUD operations). This is against the DRY (Don’t Repeat Yourself) principle, as we are repeating the same logic again and again in each repository. To solve the above problem, the Generic Repository Design Pattern comes into the picture. Please have a look at the below diagram for a better understanding.

Why do we need a Generic Repository Design Pattern in ASP.NET Core MVC?

What is a Generic Repository Design Pattern?

The Generic Repository Pattern in ASP.NET Core MVC using Entity Framework Core (EF Core) is a way to abstract data access logic into a generic class, making your code cleaner, more modular, and easier to maintain. This pattern is useful in large applications with many entities where we perform similar database operations on multiple entities. It implements the common data operations in a single, generic repository rather than having separate repositories for each entity type.

How to Implement Generic Repository Design Pattern in ASP.NET Core?

  • Generic Interface: A generic repository typically starts with a generic interface defining common operations like Add, Delete, FindById, FindAll, and Update. These operations are defined in a generic way, applicable to any entity type.
  • Implementation: The generic interface is then implemented in a concrete class. This class handles database interactions, such as querying a database using an ORM (like Entity Framework Core).
  • Entity Framework Core DBontext: The Concrete Implementation class will use the Entity Framework Core DbContext object to interact with the database.

A Generic Repository in ASP.NET Core typically does at least five operations as follows.

  • Selecting all records from a table
  • Selecting a single record based on its primary key
  • Insert a new record into a table
  • Update an existing record in a table
  • Delete an existing record from a table

However, the above list is not fixed. You may have more or fewer methods in your generic repository as per your business requirements. For the simplicity of this demo, let’s assume our Generic Repository will perform the above Five Operations.

To implement a Generic Repository Design Pattern in ASP.NET Core MVC, first, we need to create an interface, let’s say IGenericRepository , with the above five methods, and then we need to create a class, let’s say GenericRepository, which will implement the IGenericRepository interface, and provide the generic implementations for the IGenericRepository interface methods. Let us proceed and implement this step by step in our existing ASP.NET Core MVC Application that we have worked on so far in our previous two articles.

Adding GenericRepository Folder

First, let us add a folder in the project’s root directory named GenericRepository . To do so, right-click on the Project => Add => New Folder and then Rename the folder name as GenericRepository.

Creating Generic Repository Interface:

Next, add an Interface within the GenericRepository folder named IGenericRepository.cs and copy and paste the following code. Here, you can see that instead of Employee, Department, or any type, the interface works with the T type, where T will be a class. That T can be Employee, Product, Customer, Department, etc.

The IGenericRepository interface is a generic interface that defines the same set of five methods we created in the IEmployeeRepository interface in our previous article. Notice that instead of the Employee entity, we use T everywhere. Also, notice that the GetByIdAsync () and DeleteAsync () methods now accept object parameters instead of integer parameters. This is necessary because different tables may have different types of primary keys (The Customers table has a string primary key, whereas the Employees table has an integer primary key, etc.).

Implementing IGenericRepository Interface

Now, we need to implement the IGenericRepository interface. Add a class file named GenericRepository.cs within the GenericRepository Folder and copy and paste the following code. The following GenericRepository<T> class is a generic class and implements the IGenericRepository<T> interface.

As the above GenericRepository class uses the generic type, T, we can’t access the DbSet as a property on the DbContext object. Because we don’t know in advance what DbSet type we need to use, i.e., it may be Employee, Product, Customer, Department, etc. That is why a generic DbSet variable is declared at the top that points to an appropriate DbSet based on the type of T. Then, using that DbSet variable, we are doing the operations.

Register the Generic Repository in the DI Container

Modify the Program.cs class file as follows:

Using the Generic Repository in Controllers:

Once the GenericRepository is ready, we need to use that generic repository in our Employees Controller. So, modify the Employees Controller as shown below. The following Controller uses the GenericRepository to perform the CRUD Operations. Further, if you notice, while creating the instance of GenericRepository, we have specified the type T as Employee. So, in this case, DbSet<T> will be replaced as DbSet<Employee> in the GenericRepository, and the operations will be performed on the Employee table.

We are done with our implementation. Run the application and perform the CRUD operations, which should work as expected. Here, you can observe one thing while fetching the data, either all employees or specific employees by ID: it is only fetching the data from the Employees database table, not fetching the corresponding Departments table data. Here, we cannot change the Generic Repository implementations as it is common for all Entities. In situations like this, we need to use both Generic and Non-Generic Repositories in our application, which we will discuss in our next article.

When to Use and Not Use Generic Repository Pattern in ASP.NET Core MVC:

The decision to use the Generic Repository Pattern in ASP.NET Core MVC with Entity Framework Core depends on several factors related to the complexity and requirements of your application. While this pattern offers benefits like abstraction, maintainability, and testability, it’s not always the best choice for every project. Here are some considerations to help you decide when to use it:

When to Use the Generic Repository Design Pattern in ASP.NET Core MVC?

  • Complex Applications with Large Data Models: If your application has a complex domain model with many entities, the Generic Repository Pattern can help manage this complexity by abstracting data access logic.
  • Need for Abstraction: If you want to abstract away data access details to make your application more maintainable, the Generic Repository Pattern can be beneficial. It allows you to centralize data access rules and logic.
  • Unit Testing: The pattern is particularly useful for unit testing because it allows you to mock repositories easily, testing business logic without relying on a database.
  • Code Reusability: In scenarios where multiple projects have similar data access requirements, using a generic repository can promote code reusability.
  • Decoupling: This pattern can be quite effective if you want to decouple the application layers (especially separating the data layer from the business logic).

When Not to the Generic Repository Design Pattern in ASP.NET Core MVC?

  • Simple Applications: For simple applications with few entities or straightforward CRUD operations, the Generic Repository Pattern might be overkill, adding unnecessary complexity.
  • Performance-Sensitive Queries: In cases where you need highly optimized queries or complex data manipulations, the abstraction provided by the generic repository might hinder performance. EF Core itself is capable of handling complex queries efficiently.
  • Direct Dependency on EF Core Features: If you need to use advanced features of Entity Framework Core (like tracking, lazy loading, etc.), directly working with EF Core might be more beneficial.
  • Tight Coupling with EF Core: Sometimes, using a generic repository can lead to a design that is still tightly coupled with EF Core, negating some of the benefits of the pattern.

We created Entity-Specific Repositories in our previous article and Generic Repository in this article. In the next article, I will discuss Using Both Generic and Non-Generic Repository Patterns in ASP.NET Core MVC with Entity Framework Core. In this article, I explain the Generic Repository Design Pattern in ASP.NET Core MVC with EF Core. I hope you enjoy this Generic Repository Design Pattern in ASP.NET Core MVC with Entity Framework Core article.

dotnettutorials 1280x720

About the Author: Pranaya Rout

Pranaya Rout has published more than 3,000 articles in his 11-year career. Pranaya Rout has very good experience with Microsoft Technologies, Including C#, VB, ASP.NET MVC, ASP.NET Web API, EF, EF Core, ADO.NET, LINQ, SQL Server, MYSQL, Oracle, ASP.NET Core, Cloud Computing, Microservices, Design Patterns and still learning new technologies.

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

This browser is no longer supported.

Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.

Controller action return types in ASP.NET Core web API

  • 10 contributors

This isn't the latest version of this article. For the current release, see the ASP.NET Core 8.0 version of this article .

View or download sample code ( how to download )

ASP.NET Core provides the following options for web API controller action return types:

Specific type

  • IActionResult
  • ActionResult<T>
  • HttpResults

This article explains when it's most appropriate to use each return type.

The most basic action returns a primitive or complex data type, for example, string or a custom object. Consider the following action, which returns a collection of custom Product objects:

Without known conditions to safeguard against, returning a specific type could suffice. The preceding action accepts no parameters, so parameter constraints validation isn't needed.

When multiple return types are possible, it's common to mix an ActionResult return type with the primitive or complex return type. Either IActionResult or ActionResult<T> are necessary to accommodate this type of action. Several samples of multiple return types are provided in this article.

Return IEnumerable<T> or IAsyncEnumerable<T>

See Return IEnumerable<T> or IAsyncEnumerable<T> for performance considerations.

ASP.NET Core buffers the result of actions that return IEnumerable<T> before writing them to the response. Consider declaring the action signature's return type as IAsyncEnumerable<T> to guarantee asynchronous iteration. Ultimately, the iteration mode is based on the underlying concrete type being returned and the selected formatter affects how the result is processed:

  • When using System.Text.Json formatter, MVC relies on the support that System.Text.Json added to stream the result.
  • When using Newtonsoft.Json or with XML-based formatters the result is buffered.

Consider the following action, which returns sale-priced product records as IEnumerable<Product> :

The IAsyncEnumerable<Product> equivalent of the preceding action is:

IActionResult type

The IActionResult return type is appropriate when multiple ActionResult return types are possible in an action. The ActionResult types represent various HTTP status codes. Any non-abstract class deriving from ActionResult qualifies as a valid return type. Some common return types in this category are BadRequestResult (400), NotFoundResult (404), and OkObjectResult (200). Alternatively, convenience methods in the ControllerBase class can be used to return ActionResult types from an action. For example, return BadRequest(); is a shorthand form of return new BadRequestResult(); .

Because there are multiple return types and paths in this type of action, liberal use of the [ProducesResponseType] attribute is necessary. This attribute produces more descriptive response details for web API help pages generated by tools like Swagger . [ProducesResponseType] indicates the known types and HTTP status codes to be returned by the action.

Synchronous action

Consider the following synchronous action in which there are two possible return types:

In the preceding action:

  • A 404 status code is returned when the product represented by id doesn't exist in the underlying data store. The NotFound convenience method is invoked as shorthand for return new NotFoundResult(); .
  • A 200 status code is returned with the Product object when the product does exist. The Ok convenience method is invoked as shorthand for return new OkObjectResult(product); .

Asynchronous action

Consider the following asynchronous action in which there are two possible return types:

  • A 400 status code is returned when the product description contains "XYZ Widget". The BadRequest convenience method is invoked as shorthand for return new BadRequestResult(); .

A 201 status code is generated by the CreatedAtAction convenience method when a product is created. The following code is an alternative to calling CreatedAtAction :

In the preceding code path, the Product object is provided in the response body. A Location response header containing the newly created product's URL is provided.

For example, the following model indicates that requests must include the Name and Description properties. Failure to provide Name and Description in the request causes model validation to fail.

If the [ApiController] attribute is applied, model validation errors result in a 400 status code. For more information, see Automatic HTTP 400 responses .

ActionResult vs IActionResult

The following section compares ActionResult to IActionResult

ActionResult<T> type

ASP.NET Core includes the ActionResult<T> return type for web API controller actions. It enables returning a type deriving from ActionResult or return a specific type . ActionResult<T> offers the following benefits over the IActionResult type :

  • The [ProducesResponseType] attribute's Type property can be excluded. For example, [ProducesResponseType(200, Type = typeof(Product))] is simplified to [ProducesResponseType(200)] . The action's expected return type is inferred from the T in ActionResult<T> .
  • Implicit cast operators support the conversion of both T and ActionResult to ActionResult<T> . T converts to ObjectResult , which means return new ObjectResult(T); is simplified to return T; .

C# doesn't support implicit cast operators on interfaces. Consequently, conversion of the interface to a concrete type is necessary to use ActionResult<T> . For example, use of IEnumerable in the following example doesn't work:

One option to fix the preceding code is to return _repository.GetProducts().ToList(); .

Most actions have a specific return type. Unexpected conditions can occur during action execution, in which case the specific type isn't returned. For example, an action's input parameter may fail model validation. In such a case, it's common to return the appropriate ActionResult type instead of the specific type.

Consider a synchronous action in which there are two possible return types:

  • A 404 status code is returned when the product doesn't exist in the database.
  • A 200 status code is returned with the corresponding Product object when the product does exist.

Consider an asynchronous action in which there are two possible return types:

  • The [ApiController] attribute has been applied and model validation fails.
  • The product description contains "XYZ Widget".
  • A 201 status code is generated by the CreatedAtAction method when a product is created. In this code path, the Product object is provided in the response body. A Location response header containing the newly created product's URL is provided.

HttpResults type

In addition to the MVC-specific built-in result types ( IActionResult and ActionResult<T> ), ASP.NET Core includes the HttpResults types that can be used in both Minimal APIs and Web API.

Different than the MVC-specific result types, the HttpResults :

Are a results implementation that is processed by a call to IResult.ExecuteAsync .

Does not leverage the configured Formatters . Not leveraging the configured formatters means:

  • Some features like Content negotiation aren't available.
  • The produced Content-Type is decided by the HttpResults implementation.

The HttpResults can be useful when sharing code between Minimal APIs and Web API.

IResult type

The Microsoft.AspNetCore.Http.HttpResults namespace contains classes that implement the IResult interface. The IResult interface defines a contract that represents the result of an HTTP endpoint. The static Results class is used to create varying IResult objects that represent different types of responses.

The Built-in results table shows the common result helpers.

Consider the following code:

  • A 200 status code is returned with the corresponding Product object when the product does exist, generated by the Results.Ok<T>() .
  • A 201 status code is generated by the Results.Create method when a product is created. In this code path, the Product object is provided in the response body. A Location response header containing the newly created product's URL is provided.

Results<TResult1, TResultN> type

The static TypedResults class returns the concrete IResult implementation that allows using IResult as return type. The usage of the concrete IResult implementation offers the following benefit over the IResult type :

  • All the [ProducesResponseType] attributes can be excluded, since the HttpResult implementation contributes automatically to the endpoint metadata.

When multiple IResult return types are needed, returning Results<TResult1, TResultN> is preferred over returning IResult . Returning Results<TResult1, TResultN> is preferred because generic union types automatically retain the endpoint metadata.

The Results<TResult1, TResultN> union types implement implicit cast operators so that the compiler can automatically convert the types specified in the generic arguments to an instance of the union type. This has the added benefit of providing compile-time checking that a route handler actually only returns the results that it declares it does. Attempting to return a type that isn’t declared as one of the generic arguments to Results<> results in a compilation error.

  • A 200 status code is returned with the corresponding Product object when the product does exist, generated by the TypedResults.Ok<T> .
  • The [ApiController] attribute was applied and model validation fails.
  • A 201 status code is generated by the TypedResults.Created method when a product is created. In this code path, the Product object is provided in the response body. A Location response header containing the newly created product's URL is provided.

Additional resources

  • Handle requests with controllers in ASP.NET Core MVC
  • Model validation in ASP.NET Core MVC
  • ASP.NET Core web API documentation with Swagger / OpenAPI
  • A 201 status code is generated by the TypedResults.Create method when a product is created. In this code path, the Product object is provided in the response body. A Location response header containing the newly created product's URL is provided.

ASP.NET Core offers the following options for web API controller action return types:

This document explains when it's most appropriate to use each return type.

The simplest action returns a primitive or complex data type (for example, string or a custom object type). Consider the following action, which returns a collection of custom Product objects:

Without known conditions to safeguard against during action execution, returning a specific type could suffice. The preceding action accepts no parameters, so parameter constraints validation isn't needed.

When multiple return types are possible, it's common to mix an ActionResult return type with the primitive or complex return type. Either IActionResult or ActionResult<T> are necessary to accommodate this type of action. Several samples of multiple return types are provided in this document.

ASP.NET Core buffers the result of actions that return IEnumerable<T> before writing them to the response. Consider declaring the action signature's return type as IAsyncEnumerable<T> to guarantee asynchronous iteration. Ultimately, the iteration mode is based on the underlying concrete type being returned. MVC automatically buffers any concrete type that implements IAsyncEnumerable<T> .

  • A 201 status code is generated by the CreatedAtAction convenience method when a product is created. An alternative to calling CreatedAtAction is return new CreatedAtActionResult(nameof(GetById), "Products", new { id = product.Id }, product); . In this code path, the Product object is provided in the response body. A Location response header containing the newly created product's URL is provided.

ASP.NET Core includes the ActionResult<T> return type for web API controller actions. It enables you to return a type deriving from ActionResult or return a specific type . ActionResult<T> offers the following benefits over the IActionResult type :

  • The [ProducesResponseType] attribute's Type property can be excluded. For example, [ProducesResponseType(200, Type = typeof(Product))] is simplified to [ProducesResponseType(200)] . The action's expected return type is instead inferred from the T in ActionResult<T> .

ASP.NET Core

Coming soon: Throughout 2024 we will be phasing out GitHub Issues as the feedback mechanism for content and replacing it with a new feedback system. For more information see: https://aka.ms/ContentUserFeedback .

Submit and view feedback for

We've detected unusual activity from your computer network

To continue, please click the box below to let us know you're not a robot.

Why did this happen?

Please make sure your browser supports JavaScript and cookies and that you are not blocking them from loading. For more information you can review our Terms of Service and Cookie Policy .

For inquiries related to this message please contact our support team and provide the reference ID below.

IMAGES

  1. Generic Repository & Unit Of Work Patterns in .NET

    net task generic

  2. 如何使用 .NET Generic Host for Microsoft.Extensions.Hosting

    net task generic

  3. Get started with .NET Generic Host » André Snede

    net task generic

  4. Superpowered .NET Task Scheduling with Coravel

    net task generic

  5. Generic.exe Windows process

    net task generic

  6. GitHub

    net task generic

COMMENTS

  1. Task<TResult> Class (System.Threading.Tasks)

    Definition Namespace: System. Threading. Tasks Assembly: System.Runtime.dll Represents an asynchronous operation that can return a value. C# public class Task<TResult> : System.Threading.Tasks.Task Type Parameters TResult The type of the result produced by this Task<TResult>. Inheritance Object Task Task<TResult> Remarks

  2. c#

    Generic Task Method Implementation. 0. Generic interface returning Task<T> 0. Calling a generic function from inside a task? 0. Void task with generics in C#. 1. How can I use a `Task<string>` instance as a Task<string?> parameter to a method? 0. Distinguish Task and Task<T> in method signature.

  3. c#

    4. Creating generic method that returns a task is not different from creating a generic method returning anything else: public async Task<List<T>> GetStuffAsync<T> () { ... } You need to provide a generic type parameter T to make the syntax work. Generally, though you would probably want something else to generate that list inside the method ...

  4. Generalized Async Return Types in C# 7

    Let us understand the Generalized Async Return Types (ValueTask) in C# 7 with an example. First of all, you need to add the System.Threading.Tasks.Extensions package from NuGet. Then modify the Program class code as follows. As you can see in the below example, instead of using Task<T>, now we are using ValueTask<T> which is a value type, not a ...

  5. Asynchronous Generic Repository in ASP.NET Core Web API

    Asynchronous Generic Repository in ASP.NET Core Web API - Code Maze Implementing Asynchronous Generic Repository in ASP.NET Core Posted by Marinko Spasojević | Updated Date Jul 26, 2021 | 64 Want to build great APIs? Or become even better at it?

  6. Task in C# with Examples

    Task in C#. In C#, when we have an asynchronous method, in general, we want to return one of the following data types. Task and Task<T>. ValueTask and ValueTask<T>. We will talk about ValueTask later, Now let us keep the focus on Task. The Task data type represents an asynchronous operation. A task is basically a "promise" that the ...

  7. Understanding the Whys, Whats, and Whens of ValueTask

    November 7th, 2018 23 8 The .NET Framework 4 saw the introduction of the System.Threading.Tasks namespace, and with it the Task class. This type and the derived Task<TResult> have long since become a staple of .NET programming, key aspects of the asynchronous programming model introduced with C# 5 and its async / await keywords.

  8. Generics in C# with Examples

    Generic is a concept that allows us to define classes and methods with placeholders. C# Compiler replaces these placeholders with the specified type at compile time. The concept of generics is used to create general-purpose classes and methods. Let us understand the need for Generics in C# with one example.

  9. Getting Started with Generics in .NET

    Generics in the .NET context are classes, structures, interfaces and methods with placeholders for one or more of the types they store or use. Imagine that you need to create an application that will perform the registration of new customers.

  10. AI21 Labs Outperforms Generic LLMs With Task-Specific Models

    As the industry moves towards more specialized, task-oriented models, the potential for enterprises to benefit from precisely tailored AI solutions is immense. AI21 Labs distinguishes itself in the competitive large language model landscape with its emphasis on specialized, task-specific models.

  11. Generic Delegates in C# examples

    The Generic Delegates in C# were introduced as part of .NET Framework 3.5 which doesn't require defining the delegate instance in order to invoke the methods. To understand the Generic Delegates in C# you should have the basic knowledge of Delegates. Types of Generic Delegates in C# C# provides three built-in generic delegates, they are as follows:

  12. Background tasks with hosted services in ASP.NET Core

    A hosted service is a class with background task logic that implements the IHostedService interface. This article provides three hosted service examples: Background task that runs on a timer. Hosted service that activates a scoped service. The scoped service can use dependency injection (DI). Queued background tasks that run sequentially.

  13. Australian universities falling short on key measures to tackle sexual

    A third of universities do not have task forces or committees set up to address sexual violence and many are not meeting the mark when it comes to transparency, according to a new report.

  14. Banana industry turns to robots and AI for 'dangerous' packing-shed

    Robot arm to replace 'dangerous task' De-handing is the process of removing banana hands from the stem, and was identified by the ABGC as the "logical place to start" for automation.

  15. AI21 Labs Outperforms Generic LLMs With Task-Specific Models

    AI21 Labs, established in 2017 by tech visionaries Yoav Shoham, Ori Goshen and Amnon Shashua in Tel Aviv, Israel, has emerged as a leader in the field of generative AI and large language models ...

  16. How did cultural practices over Lunar New Year come about and what do

    For some Chinese communities observing Lunar New Year, adhering to specific rituals — from early preparations, to visiting etiquette and food choices — help set the tone for the rest of the year.

  17. Async method returning Task<T> with generic constraint in C#

    This is pretty much the current structure: public class Response { public bool Success { get; private set; } public static Response CreateErrorResponse () { return new Response { Success = false }; } } public interface ICommand<T> where T : Response { Task<T> ExecuteAsync (); } public abstract CommandBase : ICommand<T> where T: Response ...

  18. Generic types (generics) overview

    First introduced in .NET Framework 2.0, generics are essentially a "code template" that allows developers to define type-safe data structures without committing to an actual data type. For example, List<T> is a generic collection that can be declared and used with any type, such as List<int>, List<string>, or List<Person>.

  19. Generic Repository Pattern in ASP.NET Core MVC

    The Generic Repository Pattern in ASP.NET Core MVC using Entity Framework Core (EF Core) is a way to abstract data access logic into a generic class, making your code cleaner, more modular, and easier to maintain. This pattern is useful in large applications with many entities where we perform similar database operations on multiple entities.

  20. Taib Mahmud, Malaysian Tycoon Linked to Logging, Dies at 87

    Taib Mahmud, the politician and tycoon who became known for enabling the destruction of rainforests in Malaysia's biggest state in pursuit of economic development, has passed away due to illness.

  21. c#

    Generic interface returning Task<T> Ask Question Asked 6 years, 1 month ago Modified 5 years, 2 months ago Viewed 11k times 0 I would like to have an interface like this: public interface IQuery // I do NOT want IQuery<T> { Task<T> Result (); // Only this line can change } Is this possible?

  22. Controller action return types in ASP.NET Core web API

    ASP.NET Core includes the ActionResult<T> return type for web API controller actions. It enables returning a type deriving from ActionResult or return a specific type. ActionResult<T> offers the following benefits over the IActionResult type: The [ProducesResponseType] attribute's Type property can be excluded.

  23. Singapore Airlines' Quarterly Net Climbs on Robust Ticket Sales

    Singapore Airlines Ltd. slumped as much as 9.4% Wednesday after warning competitive pressures and rising costs could hurt its outlook.

  24. How do i convert from Task To List in this case?

    1 Answer. Sorted by: 1. A Task<T> is a Task wrapper around a value. Normally, you would unwrap it using await. However, in this case, the value you're unwrapping is something you want to display in a UI. So here you would want to have your constructor set up something to display in the meantime (an empty collection, or a "Loading..." indicator).

  25. How do I make the return type of a method generic?

    You have to convert the type of your return value of the method to the Generic type which you pass to the method during calling. public static T values<T>() {. Random random = new Random(); int number = random.Next(1, 4); return (T)Convert.ChangeType(number, typeof(T)); }