I want to achieve the following:
public AddItem()
{
//Code goes here
db.Items.Add(item);
db.Savechanges();
}
public AddLog()
{
//Code goes here
db.Logs.Add(log);
db.Savechanges();
}
public Main()
{
if(AddItem() is success)
// I NEED TO RUN ADD LOGS BUT WITHOUT WAITING, I need it to run in the background.
Return True;
}
How can i do this? its not working with async functions, since the context will be disposed. I dont want to wait for it, just return the API and keep it adding in the database in background
Related
Is this a proper way of using async/await with EF for an API? If not, can you please show me some options? The code is compiling and shows no errors. The application is working fine, but I want to make sure that it runs asynchronous.
public class AdminController : ControllerBase
{
[HttpGet("Users")]
public async Task<IResult> Users()
{
return await Admin.GetUsers();
}
}
public static class Admin
{
internal static async Task<IResult> GetUsers()
{
using var context = new DataBaseContext();
List<User>? _users = context.users.ToList();
return (_users.Count == 0) ?
Results.NotFound() :
Results.Ok(_users);
}
}
Or should I use this instead?
public class AdminController : ControllerBase
{
[HttpGet("Users")]
public Task<IResult> Users()
{
return Admin.GetUsers();
}
}
public static class Admin
{
internal static async Task<IResult> GetUsers()
{
using var context = new DataBaseContext();
List<User>? _users = await context.users.ToListAsync();
return (_users.Count == 0) ?
Results.NotFound() :
Results.Ok(_users);
}
}
The second one is what you want to use. Your first option lacks an async signature meaning you cannot await your static async method. To understand why you must understand what async and await does at runtime. So you have a bunch of threads running in a thread pool and when you have a request come in, a thread gets used to run your code and if its lacking async and await it would run in a synchronous fashion. This means that the thread will be out of that pool until the end of processing. If that admin function took 10 seconds to process, that thread will be locked to that request for 10 seconds. In that same example if you mark it as async and await, your thread goes back into the pool while you await and a callback gets used to say "Hey I'm done" and completes the execution from that await. It becomes more important as your application gets more requests. Hope I explained it well enough
I am working on an ASP.NET Core Blazor application with .Net Core 3.0 (I am aware of 3.1, but due to Mordac I am stuck with this version for now).
I have a multiple-component page, and some of those components require access to the same data and need to all be updated when the collection is updated. I've been trying to use EventHandler-based callbacks, but those get invoked on their own threads at about the same time (if I understand correctly), causing the callbacks in the .razor components to attempt to make service calls to the context at the same time.
Note: I've tried making my DbContext`s lifetime transient, but I'm still getting the race conditions.
It's quite possible that I gotten myself into an async blender and don't know how to get out.
I've tentatively concluded that the event EventHandler methodology will not work here. I need some way to trigger "collection changed" updates to the components without triggering a race condition.
I've thought about updating the services involved in these race conditions with the following:
Replace every search function with a publically bindable collection property
Having every create/update/delete call update every single one of these collections
This would allow the components to bind directly to the collections that are changed, which I think will cause every binding to it in any component to update without the needing to be explicitly told, and this in turn would allow me to ditch the "collection changed" event handling entirely.
But I'm hesitant to try this and haven't done it yet because it would introduce a fair amount of overhead on each major service function.
Other ideas? Please help. If a collection has changed, I want Blazor components that rely on that collection to somehow be able to update, whether through notifications or binding or some other way.
The following code is a heavy simplification of what I've got, and it's still causing race conditions when the event handlers are invoked from the service.
Model
public class Model
{
public int Id { get; set; }
public string Msg { get; set; }
}
MyContext
public class MyContext : DbContext
{
public MyContext() : base()
{
Models = Set<Model>();
}
public MyContext(DbContextOptions<MyContext> options) : base(options)
{
Models = Set<Model>();
}
public DbSet<Model> Models { get; set; }
}
ModelService
public class ModelService
{
private readonly MyContext context;
private event EventHandler? CollectionChangedCallbacks;
public ModelService(MyContext context)
{
this.context = context;
}
public void RegisterCollectionChangedCallback(EventHandler callback)
{
CollectionChangedCallbacks += callback;
}
public void UnregisterCollectionChangedCallback(EventHandler callback)
{
CollectionChangedCallbacks -= callback;
}
public async Task<Model[]> FindAllAsync()
{
return await Task.FromResult(context.Models.ToArray());
}
public async Task CreateAsync(Model model)
{
context.Models.Add(model);
await context.SaveChangesAsync();
// No args necessary; the callbacks know what to do.
CollectionChangedCallbacks?.Invoke(this, EventArgs.Empty);
}
}
Startup.cs (excerpt)
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
string connString = Configuration["ConnectionStrings:DefaultConnection"];
services.AddDbContext<MyContext>(optionsBuilder => optionsBuilder.UseSqlServer(connString), ServiceLifetime.Transient);
services.AddScoped<ModelService>();
}
ParentPage.razor
#page "/simpleForm"
#using Data
#inject ModelService modelService
#implements IDisposable
#if (AllModels is null)
{
<p>Loading...</p>
}
else
{
#foreach (var model in AllModels)
{
<label>#model.Msg</label>
}
<label>Other view</label>
<ChildComponent></ChildComponent>
<button #onclick="(async () => await modelService.CreateAsync(new Model()))">Add</button>
}
#code {
private Model[] AllModels { get; set; } = null!;
public bool ShowForm { get; set; } = true;
private object disposeLock = new object();
private bool disposed = false;
public void Dispose()
{
lock (disposeLock)
{
disposed = true;
modelService.UnregisterCollectionChangedCallback(CollectionChangedCallback);
}
}
protected override async Task OnInitializedAsync()
{
AllModels = await modelService.FindAllAsync();
modelService.RegisterCollectionChangedCallback(CollectionChangedCallback);
}
private void CollectionChangedCallback(object? sender, EventArgs args)
{
// Feels dirty that I can't await this without changing the function signature. Adding async
// will make it unable to be registered as a callback.
InvokeAsync(async () =>
{
AllModels = await modelService.FindAllAsync();
// Protect against event-handler-invocation race conditions with disposing.
lock (disposeLock)
{
if (!disposed)
{
StateHasChanged();
}
}
});
}
}
ChildComponent.razor
Copy-paste (for the sake of demonstration) of ParentPage minus the label, ChildComponent, and model-adding button.
Note: I've also experimented with attempting to insert a block of code into the HTML portion of the component, but that didn't work either since I can't use an await there.
Possibly bad idea that I experimented with (and that still didn't avoid the threading collision):
#if (AllModels is null)
{
<p><em>Loading...</em></p>
#Load();
#*
Won't compile.
#((async () => await Load())());
*#
}
else
{
...every else
}
#code {
...Initialization, callbacks, etc.
// Note: Have to return _something_ or else the #Load() call won't compile.
private async Task<string> Load()
{
ActiveChargeCodes = await chargeCodeService.FindActiveAsync();
}
}
Please help. I'm experimenting in (for me) uncharted territory.
Since i'm currently in a situation that looks awfully lot like yours, let me share what i found out. My issue was "StateHasChanged()". Since i've seen that call in your code too, maybe the following helps:
i got a pretty simple callback handler:
case AEDCallbackType.Edit:
// show a notification in the UI
await ShowNotification(new NotificationMessage() { Severity = NotificationSeverity.Success, Summary = "Data Saved", Detail = "", Duration = 3000 });
// reload entity in local context to update UI
await dataService.ReloadCheckAfterEdit(_currentEntity.Id);
the notification function does this:
async Task ShowNotification(NotificationMessage message)
{
notificationService.Notify(message);
await InvokeAsync(() => { StateHasChanged(); });
}
the reload function does this:
public async Task ReloadCheckAfterEdit(int id)
{
Check entity = context.Checks.Find(id);
await context.Entry(entity).ReloadAsync();
}
The problem was the StateHasChanged() call. It tells the UI to re-render. The UI consists of a datagrid component. The datagrid calls a query in the dataservice, to fetch data from the DB.
This happens just right before "ReloadAsync" is called, which is "awaited". Once ReloadAsync actually executes, it happens in a different thread, causing the dreaded "A second operation started on this context before a previous operation completed" exception.
My Solution was to remove the StateHasChanged line completely from where it was, and call it once after everything else was completed. No more concurrent caller issues.
Good luck solving this, i feel your pain.
I'm not sure about this state. I need to get data from database asynchrony.
I have class DB
public class Db{
public async Task<ObservableCollection<Person>> GetAllPerson()
{
using (var context = new Db())
{
// get data and return ObservableCollection<Person>
}
}
}
In the ViewModel I call LoadData function.
public class VM{
public ObservableCollection<Person> Person { get; set; }
private readonly DB sqlRepository;
public VM()
{
sqlRepository=new DB();
LoadData();
}
private async void LoadData()
{
Person= await sqlRepository.GetAllPerson();
}
}
I got warning: Warning CS1998 This async method lacks 'await' operators and will run synchronously.
How can I run my function asynchronously?
Should I use ?
Person=await Task.Run(()=>this.sqlRepository.GetAllPerson());
How can I run my function asynchronously?
You're approaching your problem from the wrong direction. You're trying to go "outside in" - your ViewModel wants to load the database data asynchronously. And that's a fine way of describing the problem, but it's the wrong way to solve it.
To solve it more easily, start at the other end. Whatever methods are actually calling into the database (e.g., Entity Framework calls) should be made asynchronous first, and then let async grow out from there. Eventually you'll end up with something like:
public async Task<ObservableCollection<Person>> GetAllPersonAsync()
{
using (var context = new Db())
{
// This code wasn't shown in the question.
// But from the compiler warning, it was probably using something like
// var people = People.ToList();
// return new ObservableCollection<Person>(people);
// And the async version should be:
var people = await People.ToListAsync();
return new ObservableCollection<Person>(people);
}
}
Which you could consume as:
private async void LoadData()
{
Person = await sqlRepository.GetAllPersonAsync();
}
But I recommend consuming it via NotifyTask as described in my MVVM async data binding article. That approach would give you the ability to data-bind busy spinners and whatnot.
Should I use [Task.Run]?
No. That's "fake asynchrony" - where your code acts like it's asynchronous but it's really just synchronously running on a background thread.
I am working on an application (ASP.NET MVC5) which saves a pile of data to the database in one go. The method which saves the data takes time to do it and I do not want to block user interface.
Here I have created a test program which will sleep for 10 sec and I do not want to return any result from this program.
public Task SaveFunc()
{
Thread.Sleep(10000);
return null;
}
public void ShowFunction()
{
SaveFunc();
retrun "Your request is under process";
}
Now, how do I call SaveFunc in such a way that I do not have to wait for the result.
You should use the async method Task.Delay since Thead.Sleep is synchronous and blocks the current context. You also need to return a Task from the method instead of null and await the Task to wait until it ends. In the mean time, your program can run as is:
public Task SaveFunc()
{
return Task.Delay(10000);
}
public async void ShowFunction()
{
await SaveFunc().ConfigureAwait(false);
}
This answer asumes you are using ASP.NET MVC - if this is not the case please update your question:
Since .NET 4.5.2 you can do the following:
public ActionResult ShowFunction()
{
HostingEnvironment.QueueBackgroundWorkItem(cancellationToken =>
{
// Some long-running job
});
return Content("Your request is under process");
}
If you are still on an old .NET version you can do something like:
public ActionResult ShowFunction()
{
ThreadPool.QueueUserWorkItem(c =>
{
// Some long-running job
});
return Content("Your request is under process");
}
but the execution of ThreadPool.QueueUserWorkItem can be canceled by an AppDomain-recycle so you need to take care of such scenarios. Since .NET 4.0 you can also use Task.Factory.StartNew(() => { /*...*/ }); and get a Task to work with.
I have a WCF service running as a windows service. This service references a DLL containing a class X with a public method func1. func1 calls another method func2(private) asynchronously using tasks(TPL). func2 performs a long running task independently. The setup is :
WCF
public string wcfFunc()
{
X obj = new X();
return obj.func1();
}
DLL
public class X
{
static bool flag;
public X()
{
flag = true;
}
public string func1()
{
if (!flag)
return "Already in action";
Task t = null;
t = Task.Factory.StartNew(() => func2(),TaskCreationOptions.LongRunning);
return "started";
}
void func2()
{
try
{
flag = false;
//Does a long running database processing work through .Net code
}
catch (Exception)
{
}
finally
{
flag = true;
}
}
}
The WCF function is called from a website. The website is used by multiple users. No two execution of the database processing func2 is allowed. Any user can trigger it. But during an execution, if any other user attempts to trigger it, it should show that the processing is already running.
I tried to use a static variable 'flag' to check it, but it does not seem to be working.
Any solutions? Thanks in advance.
You can read the following article, to prevent multiple calls to the WCF service method, you will need to first ensure that only one instances of your service can be created in addition to setting the concurrency mode.
In short, Make the following changes to your ServiceBehavior:
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Single, InstanceContextMode=InstanceContextMode.Single)]
public class YourService: IYourService
{...}
NOTE : This will disable concurrency in all the methods exposed by your service, If you do not want that you will have to move the needed method to a separate service and then configure it as above.