Create async methods to load data from database using EF - entity-framework

how does one write an async method that gets data from database, using DbContext and .NET 4.0?
public Task<List<Product>> GetProductsAsync()
{
// Context.Set<Product>().ToList();
}
so we can await somewhere for result of this method
// somewhere
List<Product> products = await repository.GetProductsAsync();
What is important is that I do not want to use thread pool thread for this task, since this is an IO task, and I need to support framework 4.0, and EF6 had added support for async methods only in 4.5, if I am not mistaken.

Not possible, SaveAsync is wrapped with:
#if !NET40
Source: https://github.com/aspnet/EntityFramework6/blob/master/src/EntityFramework/DbContext.cs#L335

Installing Entity Framework 6 is available as a Nuget Package and works also with .NET 4. In EF6, async
DB calls can be done like this example:
public static async Task<List<Customer>> SelectAllAsync()
{
using (var context = new NorthWindEntities())
{
var query = from c in context.Customers
orderby c.CustomerID ascending
select c;
return await query.ToListAsync();
}
}
Here, a DbContext is instantiated in a using block and the query will search the Northwind database for all its customers and order ascending by primary key CustomerID. The method needs to be decorated with async keyword and must return a task of the desired type T, here a list of customers. The result will then be return by using the ToListAsync() method in this case and one must remember to use the await keyword. There are additional Async methods in EF6, such as SaveChangesAsync, SingleOrDefaultAsync and so on. Also, async methods in C# should following this naming conventions according to guidelines.
To get started test the code above in Visual Studio, just create a new Console Application solution, type: install-package EntityFramework in the Package Manager Console and add an edmx file pointing to a local Northwind database. The main method will then just call the method above like this:
class Program
{
static void Main(string[] args)
{
Stopwatch sw = Stopwatch.StartNew();
var task = CustomHelper.SelectAllAsync();
task.Wait();
Console.WriteLine("Got data!");
List<Customer> data = task.Result;
Console.WriteLine(data.Count);
Console.WriteLine("Async op took: " + sw.ElapsedMilliseconds);
sw.Stop();
sw.Start();
//data =
//var data = CustomHelper.SelectAll();
//Console.WriteLine("Got data!");
//Console.WriteLine(data.Count);
//Console.WriteLine("Sync operation took: " + sw.ElapsedMilliseconds);
Console.WriteLine("Press any key to continue ...");
Console.ReadKey();
}
}
I see that the async operation takes a bit longer than the sync operation, so the async db call will have some time penalty but allows asynchronous processing, not freezing the calling code awaiting the result in a blocking fashion.

Related

How do I filter a result set in Entity Framework Core?

I have a dinky little web service written in Entity Framework Core that returns a list of coils that have been, are being or will be annealed. I don't care about the ones that have been annealed. How do I put filter out the finished ones?
The Inventory table includes a column archived. I want to query for those rows for which the archived column contains zero. How do I do that?
The query is being performed in an extension method on the IEndpointRouteBuilder interface:
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Generic;
using System.Text.Json;
using CAPSWebServer.CapsDataModels;
namespace Microsoft.AspNetCore.Builder
{
public static class CAPSServiceEndpoint
{
public static void MapWebService(this IEndpointRouteBuilder app)
{
app.MapGet("caps/coils", async context =>
{
CapsDataContext data = context.RequestServices.GetService<CapsDataContext>();
var coils = data.Inventories;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonSerializer.Serialize<IEnumerable<Inventory>>(data.Inventories));
});
}
}
}
Assuming archived is a bool, you should be able to do:
await data.Inventories.Where(inventory => !inventory.Archived).ToListAsync();.
You'll most likely need a using for System.Linq and Microsoft.EntityFrameworkCore

Blazor Server with Entity Framework core, How call DB methods Asyncronously

I creating balzor server app with ASP.net core 3.1 and Entity framework core.
The problem is that since Db call takes a while to get back record and there is no built in async into EF core, i decided to delay pulling rows after few seconds once page loads.
But the problem is that my HTML does not update after code has run.
Without Task Delay i can see data. Why is it not updating my UI/HTML ?
Html
#if (_allRequestForHelp.Count > 0)
{
#foreach (var item in _allRequestForHelp)
{
}
}
protected override void OnInitialized()
{
telemetryClient.TrackPageView("RegisterForHelp");
// want to run this async other page hangs until the data comesback
Task.Delay(2000).ContinueWith(t => loadPreviousRequestForHelps());
base.OnInitialized();
}
private async Task loadPreviousRequestForHelps()
{
if (appStateInfo.IsAuthenticated)
{
_allRequestForHelp = (from b in dbContext.RequestHelp where b.UserID.ToString() == appStateInfo.UserID orderby b.UTCDateCreated descending select b).ToList();
StateHasChanged();
}
}
I used the async version OnInitialized
protected async override Task OnInitializedAsync()
everything else remains same and it is working now

Should DBContext be globally defined or explicitly created every time?

I'm a SQL guy who's tinkering with Web API and Entity Framework 6 and I keep receiving the error "The operation cannot be completed because the DbContext has been disposed" when I my code is:
namespace DataAccessLayer.Controllers
{
public class CommonController : ApiController
{
[Route("CorrespondenceTypes")]
[HttpGet]
public IQueryable GetCorrespondenceTypes()
{
using (var coreDB = new coreEntities())
{
var correspondenceType = coreDB.tblCorrespondenceTypes.Select(cor => new { cor.CorrespondenceTypeName });
return correspondenceType;
}
}
}
}
But if change my code around a little and try this it works:
namespace DataAccessLayer.Controllers
{
public class CommonController : ApiController
{
readonly coreEntities coreDB = new coreEntities();
[Route("CorrespondenceTypes")]
[HttpGet]
public IQueryable GetCorrespondenceTypes()
{
var correspondenceType = coreDB.tblCorrespondenceTypes.Select(cor => new { cor.CorrespondenceTypeName });
return correspondenceType;
}
}
}
My question is why does the second one work but not the first? Is it better practice to have a global connection string or call DBContext explicitly each time?
Your are getting error because you are returning the IQueryable for which Entity framework has yet not executed the query and DbContext has been disposed when that query needs to be executed.
Remember Entity framework will not execute query until collection is initialized or any method that does not support deferred execution. Visit this link for list of Linq deferred execution supported method.
why does the second one work but not the first?
In first code snippet you are returning an instance of IQuerable which has not executed DbQuery and then after it just fires dispose on your context (coreDB). So then after whenever your code iterate over the collection it tries to fire DbQuery but finds that context has already been destroyed so you are getting an error.
In second case when ever you are iterating over the collection coreDB context must be alive so you are not getting an error.
Is it better practice to have a global connection string or call DBContext explicitly each time?
Answer to this question is based on developers taste or his own comforts. You can use your context wrapped within using statements as below:
public IList GetCorrespondenceTypes()
{
using (var coreDB = new coreEntities())
{
var correspondenceType = coreDB.tblCorrespondenceTypes.Select(cor => new { cor.CorrespondenceTypeName });
return correspondenceType.ToList();
}
}
As shown in above code snippet if you would use ToList before returning it would execute query before your coreDB got destroyed. In this case you will have to make sure that you returned materialized response (i.e. returned response after executing the DbQuery).
Note: I have noticed most of the people choose the second way. Which targets context as an instance field or property.

SqlConnection.OpenAsync doesn't work on .net 4.5^

I have a method with a signature like this:
internal async static Task<string> Get()
{
var SqlCon = await InitializeConnection();
return "Foo";
}
I call this method like this:
var x = Get().Result;
Description of other method
internal async static Task<SqlConnection> InitializeConnection()
{
SqlConnection sc;
sc = new SqlConnection();
sc.ConnectionString = #"Data Source=.\MSSQL;Initial Catalog=MyDB;Integrated Security=True;Async=True";
await sc.OpenAsync();//on this line the program long waits and doesn't connect
return sc;
}
I checked with different correct lines of connection without use of asynchrony and everything worked. How to fix it ? Thank you.
You are probably causing a deadlock by using Result. You should use await instead.
I explain this deadlock in detail on my blog. In essence, there are many contexts (such as UI or ASP.NET contexts) that only permit a single thread to execute at a time. By default, await will capture a context and resume the rest of the async method in that context. So, by (synchronously) blocking a thread in that context by calling Result, you are preventing the async method from completing.

EF6 alpha Async Await on an Entity Stored Procedure / Function Import?

I'd like to apply the new async await functionality to Stored Procedures / Function Imports imported in my Entity model, but have as yet been unable to with the EF6 alpha.
Is it yet possible in EF6 alpha2 (or the nightly build as of 20211) to call any of the new Async methods on an Entity Function Import (which calls a SQL Stored Procedure) that returns a collection of Complex Type? e.g.
private async Task<IList<Company>> getInfo (string id)
{
using (CustomEntity context = new CustomEntity())
{
var query = await context.customStoredProcedure(id).ToListAsync();
// ".ToListAsync()" method not available on above line
// OR ALTERNATIVELY
var query = await (from c in context.customStoredProcedure(id)
select new Company
{
Ident = c.id,
Name = c.name,
Country = c.country,
Sector = c.sector,
etc. etc....
}).ToListAsync();
// ".ToListAsync()" method or any "...Async" methods also not available this way
return query;
}
}
"ToListAsync", or any of the new async modified methods do not seem to be available to the above Entity Stored Procedure / Function Import; only the standard "ToList" or "AsNumerable" etc methods are available.
I followed this (http://entityframework.codeplex.com/wikipage?title=Updating%20Applications%20to%20use%20EF6) to make sure the code is referencing the new EF6 dlls and not EF5, as well as updated the various using statements. Aside from above, everything builds correctly. (.NET Framework 4.5)
The only time I can see the async methods is if instead of only importing stored procedures from the DB, I also import a table--then when referencing that table via the Entity context as above (context.SomeTable), some of the async methods appear in intellisense.
I'd really like to start using the new async await functionality on multiple Stored Procedures prior to returning data as JSON, but have not been able to get it to work so far.
Am I doing something wrong? Is async functionality not possible on Entity stored procedure / function imports? Thanks for your advice.
Now this is by no means the best solution. I added an extension method so that I could call await on my stored procedures. In the newer releases of EF6.1+ we should see this officially implemented. Until then a dummy extension method does the job.
static async Task<List<T>> ToListAsync<T>(this ObjectResult<T> source)
{
var list = new List<T>();
await Task.Run(() => list.AddRange(source.ToList()));
return list;
}
If you reflect version 6 of EF you will see that ObjectResult<T> actually implements IDbAsyncEnumerable<T>, IDbAsyncEnumerable. And the method for ToListAsync<T>(this IDbAsyncEnumerable<T> source) should be able to wire it up the same as a LINQ query.
Edit
When the ObjectResult is empty null is returned. You could add if (source == null) return new List<T>(); if you want to return an empty List instead of null.
This is an old thread, but I felt I should share. You should use APM then wrap the synchronous calls in a Task.
Example:
//declare the delegate
private delegate MyResult MySPDelegate();
// declare the synchronous method
private MyResult MySP()
{
// do work...
}
Then wrap the synchronous method in a Task:
// wraps the method in a task and returns the task.
public Task<MyResult> MySPAsync()
{
MySPDelegate caller = new MySPDelegate(MySP);
return Task.Factory.FromAsync(caller.BeginInvoke, caller.EndInvoke, null);
}
Call the async method when you want to execute:
var MyResult = await MySPAsync();
You can use up to three (3) parameters in the methods. Best practice is if you use more than three parameters; you should pass in a class.