|
Move it to a second thread: I'd suggest a BackgroundWorker as a simple to manage way to do it - it can report progress and completion via Events
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Doesn't that negate the whole purpose of Async/Await?
What I'm trying to understand is the correct way to use Async/Await.
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
As already mentioned by OriginalGriff the correct way would be using a worker thread
modified 28-Jan-21 4:34am.
|
|
|
|
|
For such an aggressive use of bold there, it's amusing that you called out the wrong person. Kevin Marois is the person who asked the question.
|
|
|
|
|
Sorry! the fast copy/paste operations may not always be 100% correct!
PS: I have edited my post.
|
|
|
|
|
I'm with you. BGW is underappreciated and often misunderstood; particularly the "reporting" part (object state versus the pseudo "% complete").
It was only in wine that he laid down no limit for himself, but he did not allow himself to be confused by it.
― Confucian Analects: Rules of Confucius about his food
|
|
|
|
|
The method you're overriding doesn't return a Task (or ValueTask ), so you can't make the override an async method.
You would need to make the method in the base class return either Task<bool> or ValueTask<bool> . The calling method would then need to wait for the returned task to complete before proceeding, either using await or ContinueWith .
It's also not helped by the fact that the methods you want to call on a background thread are synchronous methods. It would be better if they were async as well, so you could avoid explicitly spinning up a new Task to run them off-UI.
public override ValueTask<bool> SaveChanges()
{
bool isValid = Validate();
if (isValid)
{
var task = Company.Id == 0
? Task.Run(() => AppCore.BizObject.AddCompany(Company))
: Task.Run(() => AppCore.BizObject.UpdateCompany(Company));
await task;
}
return isValid;
} Understanding the Whys, Whats, and Whens of ValueTask | .NET Blog[^]
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Thank you!
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
Ok, so I've always been confused by the Client/Server design in an async app. Is this the right way to make a server method async?
Server Side
public interface IServerAPI
{
Task AddCompanyAsync(CompanyEntity entity);
Task UpdateCompanyAsync(CompanyEntity entity);
}
public class ServerAPI : IServerAPI
{
public async Task AddCompanyAsync(CompanyEntity entity)
{
var t = await Task.Factory.StartNew(() =>
{
return 1;
});
return t;
}
public async Task UpdateCompanyAsync(CompanyEntity entity)
{
await Task.Factory.StartNew(() =>
{
});
}
}
WPF Client
Some code ommitted. As you saw, the Save command is in a base class, so I can't replace it with an AsyncCommand. Instead I have it calling an async SaveChanges method:
public partial class MainWindow : Window, INotifyPropertyChanged
{
#region Base Class Abstract Code
private ICommand _SaveCommand;
public ICommand SaveCommand
{
get
{
if (_SaveCommand == null)
_SaveCommand = new RelayCommand(p => SaveExecuted(), p => SaveCanExecute());
return _SaveCommand;
}
}
private bool SaveCanExecute()
{
return true;
}
private async void SaveExecuted()
{
try
{
await SaveChanges();
}
catch (Exception e)
{
throw;
}
finally
{
}
}
#endregion Base Class Abstract Code
private async Task SaveChanges()
{
IServerAPI serverAPI = new ServerAPI();
if (Company.Id == 0)
{
var newId = await serverAPI.AddCompanyAsync(Company);
Company.Id = newId;
}
else
{
await serverAPI.UpdateCompanyAsync(Company);
}
}
}
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
Not really. Spinning up a new Task and awaiting it is only really useful if you want to move the processing onto a background thread. On the server, that doesn't make any sense.
On the server-side, you'd want to have async calls to the database. Entity Framework offers various async methods. Or, if you're using raw ADO.NET, use DbConnection.OpenAsync[^], DbCommand.ExecuteReaderAsync[^], etc.
On the client, you want the network call to be async , so that your code isn't blocked waiting for the network request to complete. If you're using HttpClient or RestClient , the default methods for calling the server will be async . If you're using WCF or HttpWebRequest , you might have to jump through a few hoops to make a nice async method.
You should generally avoid async void methods:
Avoid async void methods | You’ve Been Haacked[^]
Instead, follow David Fowler's advice[^] and store the Task returned from your async method in a discard variable:
private void SaveExecuted()
{
_ = SaveAsync();
}
private async Task SaveAsync()
{
try
{
IServerAPI serverAPI = new ServerAPI();
if (Company.Id == 0)
{
Company.Id = await serverAPI.AddCompanyAsync(Company);
}
else
{
await serverAPI.UpdateCompanyAsync(Company);
}
}
finally
{
}
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Richard Deeming wrote: Not really. Spinning up a new Task and awaiting it is only really useful if you want to move the processing onto a background thread. On the server, that doesn't make any sense.
OK, so what would the server methood look like? I'm a bit confused
In that Fowler article, he has this example:
public async Task<int> DoSomethingAsync()
{
var result = await CallDependencyAsync();
return result + 1;
}
I thought that the server method had to return a task to make it Async?
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
modified 29-Jan-21 18:12pm.
|
|
|
|
|
The server method can return a Task , if it's doing work that can be executed asynchronously. For example, talking to a database, calling another API, or accessing the file system.
Whether or not the server method returns a Task , the calling client can use a Task to call the server. The network request will be the asynchronous part of the call; the client doesn't care how the server handles the request.
Eg:
Server:
public int Foo()
{
return 42;
}
public async Task<int> BarAsync()
{
using (var connection = new SqlConnection("..."))
using (var command = new SqlCommand("...", connection))
{
await connection.OpenAsync();
object result = await command.ExecuteScalarAsync();
return Convert.ToInt32(result);
}
} Client:
private readonly HttpClient _client;
public async Task<int> FooAsync()
{
using (var response = await _client.GetAsync("/api/foo"))
{
response.EnsureSuccessStatusCode();
return await response.ReadAsAsync<int>();
}
}
public async Task<int> BarAsync()
{
using (var response = await _client.GetAsync("/api/bar"))
{
response.EnsureSuccessStatusCode();
return await response.ReadAsAsync<int>();
}
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Thanks.
What I'm after here is to make my WPF app responsive during Web API calls. You provided some asynv/await examples, but what the don't show is.. what is it that makes
command.ExecuteScalarAsync()
asynchronous? Isn't ExecuteScalarAsync just returning a task? This is my real question... What is it on the server that allows the client to await it?
I'm using Linq-To-Sql on my server. Right now, my DAL code is pretty straightforward:
public CompanyEntity GetCompanyById(int companyId)
{
using (var dc = GetDataContext())
{
var cust = (from c in dc.Companies
where c.Id == companyId
select new CompanyEntity
{
Id = c.Id,
CompanyName = c.CompanyName
}).FirstOrDefault();
return cust;
}
}
Then, in my WPF ViewModel I do
public override void Load(int id)
{
Customer = AppCore.BizObject.GetCustomerById(id);
}
But to make it truly async, I would need
public override async void Load(int id)
{
await Task.Factory.StartNew(() =>
{
var customer = AppCore.BizObject.GetCustomerByIdAsync(id);
});
}
I'm using a task to keep the UI responsive. But what should the server side method look like? Something close to this??
public Task<CompanyEntity> GetCompanyByIdAsync(int companyId)
{
using (var dc = GetDataContext())
{
var cust = (from c in dc.Companies
where c.Id == companyId
select new CompanyEntity
{
Id = c.Id,
CompanyName = c.CompanyName
}).FirstOrDefault();
return cust;
}
}
Thanks again!
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
Again, there are two parts to this.
On the server, ExecuteScalarAsync returns a Task , which uses an IO completion port behind the scenes to complete the task when the response has been received from the database. This allows the server to reuse the same thread-pool thread to service other requests whilst it waits for your database query to finish.
The client doesn't care what the server is doing. All it cares about is sending a request and receiving a response. But again, the network library can use an IO completion port behind the scenes so that your thread isn't tied up waiting for a response from the server. It doesn't matter to the client whether the server has implemented the API using Tasks , or using a synchronous method; the network communication is the target for the asynchronous code.
I think at least part of the confusion here stems from the fact that you're using the same interface for the server implementation of the API and the client code to call that API. It's perfectly valid to have an async method on the client calling a synchronous method on the server.
With Linq2Sql, it's not simple to make your server method async *. But that doesn't mean the client can't be async .
protected override void Load()
{
_ = LoadAsync();
}
private async Task LoadAsync()
{
var customer = await AppCore.BizObject.GetCustomerByIdAsync(id);
...
} Again, take note of the fact that you should avoid async void methods, and try to avoid spinning up a new Task just to push work off the UI thread.
* You'd have to manually convert the query to a SqlCommand and execute it by hand. Mike Taulty made a start on this back in 2007, before async /await was around:
LINQ to SQL: Asynchronously Executing Queries | Mike Taulty[^]
Perhaps try something like this:
public async Task<CompanyEntity> GetCompanyByIdAsync(int companyId)
{
using (var dc = GetDataContext())
{
var query = (from c in dc.Companies
where c.Id == companyId
select new CompanyEntity
{
Id = c.Id,
CompanyName = c.CompanyName
});
using (var command = (SqlCommand)dc.GetCommand(query))
using (var reader = await command.ExecuteReaderAsync())
{
return dc.Translate<CompanyEntity>(reader).FirstOrDefault();
}
}
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Richard Deeming wrote: It's perfectly valid to have an async method on the client calling a synchronous method on the server.
This is what I'm hung up on. To call a synchronous method on the server using async on the client, then the server method needs to return a Task, correct? Unless there's some other way?
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
You can call any server method using async on the client. The client doesn't know or care how the server handles the call. So long as the request and response payloads match what is expected, it doesn't matter whether the server uses a synchronous method, an async method, or quantum fluctuations in the migration pattern of swallows.
From the client's perspective, the network call is the async part. If you're using HttpClient or RestSharp, the network calls will be async by default. If you're using WebClient or WebRequest , there are TAP[^] overloads available. The WCF wizard provides an option to generate TAP methods in the proxy, or you can build a Task from the BeginFoo /EndFoo pair.
The only time you'll have problems is if you're trying to use the same interface to define the client and the server API. But even then, you can still have a synchronous server API - just omit the async keyword, and use Task.FromResult to return the result.
public Task<int> Foo()
{
return Task.FromResult(42);
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
I have a list of members (integer numbers):
List<int> emptyRows = new List<int>();
for (int i = 0; i < LastRow+1; i++)
{
if (worksheet.Cells[i, 0].Value.IsEmpty == true)
{
emptyRows.Add(i);
}
}
I want to subtract 1 from all the members (member1 -1, member2 -1, ...).
How can I do this using Linq?
Please guide me by giving an example code.
Thanks.
modified 27-Jan-21 11:56am.
|
|
|
|
|
You can't: for the same reason that you can't use a foreach loop to do it.
Integers are value types: every time you try to use one, you create a copy, there is no reference to it.
If you use a class, you can do it using the ForEach method:
public class foo
{
public int val { get; set; }
}
List<foo> foos = new List<foo>();
for (int i = 0; i < 10; i++)
{
foos.Add(new foo() { val = i });
}
foos.ForEach(f => f.val += 10);
foreach (foo foo in foos)
{
Console.WriteLine(foo.val);
}
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
I tried to ask a simplified mode of my problem. Let's explain it. I want to delete empty rows in DevExpress Spreadsheet by code. I tried this:
IWorkbook workbook = spreadsheetControl.Document;
Worksheet worksheet = workbook.Worksheets["Sheet1"];
CellRange range = worksheet.GetUsedRange();
int LastRow = range.BottomRowIndex;
List<int> emptyRows = new List<int>();
for (int i = 0; i < LastRow+1; i++)
{
if (worksheet.Cells[i, 0].Value.IsEmpty == true)
{
emptyRows.Add(i);
}
}
Search:
if (emptyRows.Count >= 1)
{
foreach (var item in emptyRows)
{
worksheet.Rows[item].Delete();
emptyRows.RemoveAt(item);
goto Update;
}
}
Update:
if (emptyRows.Count >= 1)
{
for (int i = 0; i < emptyRows.Count; i++)
{
emptyRows[i] = emptyRows[i]-1;
}
goto Search;
}
In the code above, I try to list empty rows. Then, I delete them based on the created list of Row Indexes. The problem is that each time a row is deleted, other empty row indexes are changed (row-1). I need to find a way to subtract each item in the list by 1 in each iteration. The second solution is to select empty rows and delete them at once. I couldn't use the second way too. Because, I don't know how to do multiple selection by code. Please help me.
|
|
|
|
|
Simple solution: don't go from the beginning to the end, deleted from the end to the beginning.
Run the loop backwards!
And do yourself a massive favour: stop using labels and goto. There is absolutely no need to use them in that code - have you not heard of break in a loop?
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
"Common sense is so rare these days, it should be classified as a super power" - Random T-shirt
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
foreach (var item in emptyRows.Reverse<int>())
{
worksheet.Rows[item].Delete();
}
I tried to use Linq namespace for reversing my list. It worked.
Now, I'm trying to use this method for deleting duplicates.
|
|
|
|
|
You don't need Linq to iterate a list in reverse:
for (int index = emptyRows.Count - 1; index >= 0; index--)
{
int item = emptyRows[index];
worksheet.Rows[item].Delete();
} Using Reverse will take longer and use more memory.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
You cannot use "foreach" to do this. "Foreach" uses an iterator to navigate over the collection. Looking at the documentation for Iterator says you cannot add/remove items to the collection being iterated over else you invalidate the iterator.
You must use an indexer, "for", to remove items from the collection. The best way to do that is to start at the end of the array (collection) and move backwards toward zero.
|
|
|
|
|
Hello,
I have Windows Forms C# desktop app with docked window layout.
Ie. few document window and few tool windows.
In that case I used WeifenLuo.WinFormsUI.Docking library.
My question is what is best way to realize event handling after document content change.
Let's consider case.
We can change content in active document window and we can just change active document window. In both cases tool windows should be refreshed based on active document window content (item selected ie.)
|
|
|
|
|
Hi I have a linq query in the following way where two column names from 2 different tables is same, which is giving me error, can somebody please help me in this? How to resolve with alias names if the names are same from 2 different tables and they are used either in select or in group - thank you so much in advance.
Here is my linq query:
ViolationsUniquePenaltyApplied = (from a in violations
join b in programs on a.ViolationTypeId equals b.ViolationTypeId
join d in branches on b.BranchId equals d.BranchId
join e in programs on a.ViolationSubtypeId equals e.ViolationTypeId into es from e in es.DefaultIfEmpty()
where a.ApplyUniquePenalty == true
group new { a.ViolationSubtypeId, e.ViolationTypeName, a?.ViolationTypeId, b?.ViolationTypeName, b?.ParentViolationId, d?.BranchId, d?.Name, a.PenaltyAssessed, a.ViolationId, a.ViolationNumber }
by new { a?.ViolationTypeId, b?.ViolationTypeName, d?.BranchId, d?.Name, a.ViolationId }
into g
select new
{
ViolationTypeId = g.FirstOrDefault().ViolationTypeId,
ViolationType = g.FirstOrDefault().ViolationTypeName,
ParentViolationType = (g.FirstOrDefault()?.ParentViolationId != null) ? (from e in programs where g.FirstOrDefault()?.ViolationTypeId == e.ParentViolationId select e.ViolationTypeName)?.FirstOrDefault() : g.FirstOrDefault().ViolationTypeName,
BranchId = g.FirstOrDefault().BranchId,
Branch = g.FirstOrDefault().Name,
PenaltyAssessed = g.Sum(pc => pc.PenaltyAssessed),
Cnt = g.Count(),
ViolationNumber = g.FirstOrDefault().ViolationNumber,
CountOrId = (g.Count() == 1) ? g.Count() : g.FirstOrDefault().ViolationId,
PenaltyPerUnit = g.Sum(pc => pc.PenaltyAssessed) / g.Count()
})
|
|
|
|
|