Parallel Process is not updating WPF Collection List - parallel-processing

In my WPF application, I have a Data Grid which populates with a ObservableCollection collection. Suppose I have such 10 student data in the grid. Each student is capable of doing 2 long running works or process and updates the status of the process back to the grid. I want to do those 2 process simultaneously. So I used Task and Parallel.Invoke methods. The work flow is as follows.
I populated the Student data collection in the Grid.
I clicked on a start button.
In the click event of the start button, i did the following code.
foreach (Student stud in StudentLists)
{
stud.Status = "started..";
Task.Factory.StartNew(() => StartProcess(stud));
}
In the StartProcess,
Parallel.Invoke(() =>
{
MarkService ms = new MarkService(stud_data);
Student s = ms.GetMarkProcess(); // This will return the stud_data in the above line
Student studitem = StudentLists.Where(x => x.RollID == s.RollID).FirstOrDefault(); // find the student in the grid
if (studitem != null)
{
studitem.Status = "Mark Got it"; // if find, updating the status
}
},
() =>
{
SentMarks(poll); // this is another method to be executed parallel
}
);
When executing all the 10 students process, each student in the grid became the same data.
Or only 2 or 1 student in the Grid is showing Status "Mark Got it". Other rows show "started.." status only.
Why this is not updating the collection.
I have used INotofyPropertyChanged and raisng the event when property updated.
In the XAML, each binding is used in Two way mode.
There is no error. But the 1 or 2 items in the student collection is updating some times. Sometimes the collection contains the last students data for all the 9 items.
It is not updating the exact student object in the Grid. what is wrong in my code ?
Any help in this case ???

The problem here is that your referencing a UI control from another thread. You should create an in memory data structure that a grid can use as DataSource (anything that implements IEnumerable). I would suggest using the ConcurrentBag data structure for parallelized code (http://msdn.microsoft.com/en-us/library/dd997305.aspx). Once you've updated each of the student records in the ConcurrentBag you set the grid's datasource to that bag.

Related

Vuetify v-data-table change a row color for a few seconds

We've just moved over from bootstrap to Vuetify, but i'm struggling with something.
We have some updates sent (over signalR) that update a list of jobs, i'd like to be able to target a job that has been changed and change the row color for that particular job for a few seconds so the operator can see its changed.
Has anyone any pointers on how we can do this on a Vuetify v-data-table
Thanks
I ran into the same problem. This solution is a bit crude and a bit too late, but may help someone else.
In this example I change the colour of the row permanently until the page reloads. The problem with a temporary highlight is that if the table is sorted there is no way to put the row in the visible part of the table - v-data-table will put it where it belongs in the sort, even if it's out of the view.
Collect the list of IDs on initial load.
Store the list inside data of the component.
Use a dynamic :class attribute to highlight rows if the ID is not in the list (added or edited rows)
Solution in detail
1. Use TR in the items template to add a conditional class.
<template slot="items" slot-scope="props">
<tr :class="newRecordClass(props.item.email, 'success')">
<td class="text-xs-center" >{{ props.item.email }}</td>
:class="newRecordClass(props.item.email, 'success')" will call custom method newRecordClass with the email as an ID of the row.
2. Add an additional array to store IDs in your data to store
data: {
hydrated: false,
originalEmails: [], <--- ID = email in my case
3. Populate the list of IDs on initial data load
update(data) {
data.hydrated = true; // data loaded flag
let dataCombined = Object.assign(this.data, data); // copy response data into the instance
if (dataCombined.originalEmails.length == 0 ) {
// collect all emails on the first load
dataCombined.originalEmails = dataCombined.listDeviceUsers.items.map( item => item.email)
}
return dataCombined;
}
Now the instance data.originalEmails has the list of IDs loaded initially. Any new additions won't be there.
4. Add a method to check if the ID is in the list
newRecordClass(email, cssClass) {
// Returns a class name for rows that were added after the initial load of the table
if (email == "" || this.data.originalEmails.length==0) return "" // initial loading of the table - no data yet
if (this.data.originalEmails.indexOf(email) < 0 ) return cssClass
}
:class="newRecordClass(..." binds class attribute on TR to newRecordClass method and is being called every time the table is updated. A better way of doing the check would be via a computed property (https://v2.vuejs.org/v2/guide/computed.html#Computed-Properties). Vue would only call it when the underlying data changed - a method is called every time regardless.
Removing the highlight
You can modify newRecordClass method to update the list of IDs with new IDs after a delay to change the colour to normal.
#bakersoft - Did you find a solution? I suspect there is an easier way to skin this cat.

Get Crystal Report data in session

I have noticed that crystal report runs the Linq query once again when the page index is changed, means when we load second page from first page?
So just wanted to know if we can get which page is loaded so that we can keep values in session.
Just a hint is required as I am not getting the desired results from Google.
Update:
I am sorry in a hurry I just clicked on a wrong tag.
So the problem is like:
This is my code below which I use fr running my crystal report:
var rpt = new Result();
List<class> lst1 = new DALMethod().Get();
rpt.SetDataSource(lst1);
CRReportViewer.ReportSource = rpt;
When I switch from page one to two or more, this method in DAL is called again taking the same time it took first time to load, so I just want to have the data in session when query runs first time, and next time when I get the page index, then I will show data from session.
Is there a way around by which I can get the page index in this c# code?
I had found the solution, hope this might help someone else:
I was using a generic list as a data source:
As soon as we get to know the page loads for the first time, I mean not a postback, we can initialize a list to be maintained in session.
After showing the report we can add the data source (which is a list type).
On Report page shift data will be taken from session.
if (!IsPostBack)
{
//clear session and create new session
Session["ReportGenericList"] = null;
}
List<class> datasourceLst=null;
if (Session["ReportGenericList"] != null)
{
datasourceLst= (List<class>)Session["ReportGenericList"];
}
else
{
datasourceLst = //call methods to fill datasource
Session["ReportGenericList"] = datasourceLst;
}

How to access 1:N relationship entities in a Microsoft CRM 2011 plugin?

I have a custom Board entity with a 1:N relationship to a custom Board Seat entity. I have a post-update plugin on the Board entity that tries to loop through all the Board Seats related to the Board being updated. I've tried both a pre and post image and the relationship is null on both even though the Board has several Board Seats associated with it.
var board = EntityImage.ToEntity<my_boards>();
foreach (var seat in board.board_to_boardseat_relationship)
{
// Process each seat
}
I'm using strongly typed entities and the type of board.board_to_boardseat_relationship is System.Collections.Generic.IEnumerable<my_boardseat>. It appears that the relationship simply isn't getting populated in either the pre or post image. When I register the image I select all attributes. Any idea how to populate this relationship?
Mike,
It doesn't get populated because all the attributes are only the attributes of the Entity. You'd have to use some kind of retrieve function to get all the seats associated with this particular board. They are never included in your Post- or Pre-Image (or Target) Below is some sample code:
Entity PostImage = (Entity)m_localcontext.PluginExecutionContext.PostEntityImages["PostImage"];
my_board board = PostImage.ToEntity();
var seatsList = orgContext.CreateQuery<my_boardseat>().Where(c => c.boardId.Id == board.boardId).ToList();
if (seatsList.Count > 0)
{
foreach (my_boardseat seat in seatsList)
{
//Your Code Here
}
}

jqgrid randId() produces duplicates after page reload

On my grid, after a user enters text on the bottom row, I am adding another row so they can fill out another row if needed. The grid will grow as needed by the user. This is working fine, however after a page reload and populating from db, the addrowdata() function does not honor existing row ids and creates duplicates, starting from 1 again, e.g. jqg1. It should look at existing row ids and create new unique ids. So if I have 5 rows already, it might start at jqg6. Here is the relevant code inside onCellSelect:
var records = jQuery("#table-1").jqGrid('getGridParam', 'records');
var lastRowId = jQuery("#table-1").jqGrid('getDataIDs')[records - 1];
if (lastRowId == id)
{
jQuery('#table-1').addRowData(undefined, {}, 'last');
}
I have also tried $.jgrid.randId() instead of undefined, same results as expected.
Thanks
Ryan
I think that the error is in the part where you fill grid with the data from the database. The data saved in the database has unique ids. The ids are not in the form jqg1, jqg2, ... So if should be no conflicts. You should just fill the id fields of the JSON with the ids from the database.
One more possibility is that you just specify the rowid parameter (the first parameter) of addRowData yourself. In the case you will have full control on the new ids of the rows added in the grid.
The code of $.jgrid.randId function is very easy. There are $.jgrid.uidPref initialized as 'jqg' and $.jgrid.guid initialized to 1. The $.jgrid.randId function do the following
$.jgrid.randId = function (prefix) {
return (prefix? prefix: $.jgrid.uidPref) + ($.jgrid.guid++);
}
If it is really required you can increase (but not decrease) the $.jgrid.guid value without any negative side effects.

How to prevent multiple users from adding an item to a Sharepoint list simultaneously

I am using a simple form to allow people to sign up for an event. Their details are saved to a Sharepoint list. I have a quota of people who can sign up for an event (say 100 people).
How can I prevent the 100th and the 101st person from signing up concurrently, causing the quota check to allow the 101st person to sign up (because the 100th person isn't in the list yet)?
Place the ItemAdding code inside a lock statement to make sure that only one thread at a time can enter the critical section of code:
private Object _lock = new Object();
public override void ItemAdding(SPItemEventProperties properties)
{
lock(_lock)
{
// check number of the list items and cancel the event if necessary
}
}
I came up with this idea of a solution for a farm with multiple WFEs - a shared resource (a row in a table in pseudo-code above) gets locked during the time the item is added to the list:
private Object _lock = new Object();
public override void ItemAdding(SPItemEventProperties properties)
{
try
{
// 1. begin a SQL Server transaction
// 2. UPDATE dbo.SEMAPHORE
// SET STATUS = 'Busy'
// WHERE PROCESS = 'EventSignup'
lock(_lock)
{
// 3. check number of the list items and cancel the event if necessary
}
}
finally
{
// 4. UPDATE dbo.SEMAPHORE
// SET STATUS = ''
// WHERE PROCESS = 'EventSignup'
// 5. commit a SQL Server transaction
}
}
I left the lock statement because I'm not sure what will happen if the same front-end server tries to add the item #100 and #101 - will the transaction lock the row or will it not because the same connection to SQL Server will be used?
So then you can use event receivers item adding method. at item adding, your item is not created, you can calculate the current count of signed up people. if it is bigger then 100 you can cancel item adding.
but sure, more than one item adding method can be fired, to prevent that you can calculate the current count of people and increase the count +1, and keep that value somewhere else (on a field on event item perhaps) and all item adding methods can check that value before adding the item.
item added method is too late for these operations.
this would be the solution i would use.
I guess if you are updating a column, lets say - "SignUp Count", then one of the users will get the Save Conflict issue. Whoever updated the value for the first time wins and the second one will fail.
Regards,
Nitin Rastogi

Resources