Distributing jobs fairly on accounts - distribution algorithm - c#

I'm working on a project that publish to a multiple websites using a multiple accounts. each account can publish to a specific website. as shown in the 'Dict' below. The problem is I'm trying to distribute the publishing jobs on accounts fairly and making a distance between accounts that has more than 1 job.
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public struct Job
{
public string Site { get; set; }
public string Account { get; set; }
}
private static readonly Dictionary<string, List<string>> Dict = new Dictionary<string, List<string>>();
private static List<string> _accounts;
public static void Main()
{
var sites = new List<string>{"Site-A", "Site-B", "Site-C", "Site-D", "Site-E"};
_accounts = new List<string>{"Account-A", "Account-B", "Account-C", "Account-D", "Account-E"};
// Permissions dictionary. specify accounts that has a permessions to publish on a particular Site
Dict.Add("Site-A", new List<string>{"Account-A", "Account-C"});
Dict.Add("Site-B", new List<string>{"Account-A", "Account-E"});
Dict.Add("Site-C", new List<string>{"Account-C", "Account-D"});
Dict.Add("Site-D", new List<string>{"Account-A"});
Dict.Add("Site-E", new List<string>{"Account-A"});
var jobs = new List<Job>();
foreach (var site in sites)
{
var job = new Job();
// Get an account that has a permissions to publish on 'site'
// checking against the permissions dictionary Dict
var account = GetAccountCanPost(Dict, site, _accounts);
job.Site = site;
job.Account = account;
jobs.Add(job);
}
var jobsCountForEachAccountDict = CalculateJobsCountForEachAccounts(jobs);
//////#### Now.. We need to re Order Jobs and swipe it here before send it to processing ####.....//////
foreach (var job in jobs)
{
Console.WriteLine(job.Account + " publish on " + job.Site);
}
}
public static Dictionary<string, int> CalculateJobsCountForEachAccounts(List<Job> jobs)
{
var dict = new Dictionary<string, int>();
foreach (var job in jobs)
{
if (dict.ContainsKey(job.Account))
dict[job.Account]++;
else
dict.Add(job.Account, 1);
}
return dict;
}
public static string GetAccountCanPost(Dictionary<string, List<string>> dict, string targetSite, List<string> accounts)
{
var accountIdsAssoc = GetAccountsIdsAssociatedWithCommunity(dict, targetSite);
var selectedId = PickRandom(accountIdsAssoc, new Random());
var account = accounts.FirstOrDefault(s => s == selectedId);
return account;
}
private static List<string> GetAccountsIdsAssociatedWithCommunity(Dictionary<string, List<string>> communitiesAccountsAssociationsDict, string communityId)
{
if (communitiesAccountsAssociationsDict.ContainsKey(communityId))
return communitiesAccountsAssociationsDict[communityId];
return null;
}
private static T PickRandom<T>(IList<T> list, Random random)
{
var index = random.Next(0, list.Count);
return list[(int) index];
}
}
when the jobs are created it is something similar to this: (Before re-adjusting the jobs distribution)
> Account-A Publish on Site-A
> Account-E Publish on Site-B
> Account-D Publish on Site-C
> Account-A Publish on Site-D
> Account-A Publish on Site-E
The publishing jobs created above are not fairly distributed to accounts, as you can see 'Account-A' has 3 jobs assigned while there's an other accounts can publish to the 'sites' as defined in 'Dict' so it should look something like:
> Account-C Publish on Site-A
> Account-E Publish on Site-B
> Account-A Publish on Site-D
> Account-D Publish on Site-C
> Account-A Publish on Site-E
In the output above the jobs are distributed fairly on accounts and also there's a distance between accounts that has more than 1 job
An example on distance between jobs:
> Account-A Publish on Site-A
> Account-E Publish on Site-B
> Account-D Publish on Site-C
> Account-A Publish on Site-D
> Account-A Publish on Site-E
Job 4 and 5 are being processed by Account-A. An account should not process two jobs sequentially, so it could be swapped with another job.
It will be highly appreciated if you could help. I need an algorithm that do the job distribution to get similar output. Performance is not important.
Thank you..

It can be improved, but this will do what you need:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp1
{
public struct Job
{
public string Site { get; set; }
public string Account { get; set; }
}
public class Program
{
public static void Main(string[] args)
{
Dictionary<string, List<string>> permissionsDict = new Dictionary<string, List<string>>();
permissionsDict.Add("Site-A", new List<string> { "Account-A", "Account-C" });
permissionsDict.Add("Site-B", new List<string> { "Account-A", "Account-E" });
permissionsDict.Add("Site-C", new List<string> { "Account-C", "Account-D" });
permissionsDict.Add("Site-D", new List<string> { "Account-A" });
permissionsDict.Add("Site-E", new List<string> { "Account-A" });
// get responsibilities rate for each account
Dictionary<string, int> responsibilitiesRate = GetResponsibilitiesRate(permissionsDict);
List<Job> jobs = new List<Job>();
// building jobs list
foreach (var permission in permissionsDict)
{
var job = new Job();
job.Site = permission.Key;
// for the current site, see what account has lower responsibility rate
int minResponsibilities = permission.Value.Min(x => responsibilitiesRate[x]);
string account = permission.Value.First(x => responsibilitiesRate[x] == minResponsibilities);
responsibilitiesRate[account]++;
job.Account = account;
jobs.Add(job);
}
// order jobs making sure distance between accounts has more than 1 job
jobs = RandomOrderResponsibilities(jobs);
foreach (var job in jobs)
{
Console.WriteLine(job.Account + " publish on " + job.Site);
}
Console.ReadLine();
}
private static Dictionary<string, int> GetResponsibilitiesRate(Dictionary<string, List<string>> dict)
{
Dictionary<string, int> responsibilitiesCount = new Dictionary<string, int>();
foreach (var kvp in dict)
{
foreach (var account in kvp.Value)
{
if (responsibilitiesCount.ContainsKey(account))
{
responsibilitiesCount[account]++;
}
else
{
responsibilitiesCount.Add(account, 1);
}
}
}
return responsibilitiesCount.OrderBy(x => x.Value).ToDictionary(x => x.Key, x => x.Value);
}
private static List<Job> RandomOrderResponsibilities(List<Job> jobs)
{
bool couldComplete = true;
var maxIterations = 1000;
var iterationCount = 0;
do
{
// shuffle
jobs = jobs.OrderBy(a => Guid.NewGuid()).ToList();
for (int i = 1; i < jobs.Count; i++)
{
if (jobs[i].Account == jobs[i - 1].Account)
{
couldComplete = false;
for (int j = i + 1; j < jobs.Count; j++)
{
if (jobs[j].Account != jobs[i].Account)
{
// swipe
var temp = jobs[i];
jobs[i] = jobs[j];
jobs[j] = temp;
couldComplete = true;
break;
}
}
}
}
iterationCount++;
} while (!couldComplete && iterationCount < maxIterations);
return jobs;
}
}
}
Output (random solution):
Account-A publish on Site-D
Account-C publish on Site-A
Account-A publish on Site-E
Account-D publish on Site-C
Account-E publish on Site-B

Related

Subscribe to an Mqtt broker and write into OPC server using windows Forms Application

I'm trying to subscribe to an MQTT broker and write into an OPC server using windows forms application. But I have some problems I get always an exception "Object reference not set to an instance of an object." everything is good but when i arrived to write into an opc server avec subscribing and getting the mqtt message i got an exception. Below is the code This a method in the WCF service.
public void StartTransferFromMQTTtoOPC()
{
try
{
// Check if at least one transfer if configured
string jmsg;
while (TransferHelper.SubscribedBlockingCollection.TryTake(out jmsg))
{
// Check if any transfer is configured.
if (ExportedMQTTTransfers == null || ExportedMQTTTransfers.archList == null ||
ExportedMQTTTransfers.archList.Count == 0)
return;
// check which Mqtt Transfer is responsible for that topic
var message = JsonConvert.DeserializeObject<TopicStruct>(jmsg);
var mqqtmessage = message.Message;
var transfer = ExportedMQTTTransfers.archList.Find(x => x.Topic == message.TopicName);
// get Group Information and server information from the topic
//OPCGroup grp = transfer.OPCgrpsPerServer.TryGetValue()
//Load Configuration of specific topic Fields Mapping
Dictionary<string, string> fieldsmapping;
var first = transfer.OPCgrpsPerServer.First();
OPCGroup grp = first.Value[0];
fieldsmapping = grp.FieldsMapping;
StringBuilder payloadTransformer = new StringBuilder("{\"Iterator\": {\"#loop($.ListofValues)\": {");
foreach (KeyValuePair<string, string> entry in fieldsmapping)
{
payloadTransformer.Append($"\"{entry.Value}\":\"#currentvalueatpath($.{entry.Key})\",");
}
payloadTransformer.Length--;
payloadTransformer.Append("}}}");
string resPayload = JUST.JsonTransformer.Transform(payloadTransformer.ToString(), mqqtmessage);
var myItems = JsonConvert.DeserializeObject<ConsumedItem>(resPayload);
if (myItems is null)
{
return;
}
MappedTags = new Dictionary<string, Dictionary<string, string>>();
List<int> lstItemHandles = new List<int>();
//Read From a CSV file and put data in a Dictionary
string csvFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TagMappings.csv");
var rows = File.ReadAllLines(csvFile);
var totalcols = rows[0].Split(',').Length;
Dictionary<string, string> Y = new Dictionary<string, string>();
string columns = "", csvCell = "";
string[] column;
List<string> MQTTList = new List<string>();
List<string> csvList = new List<string>();
// List<string> GroupNameList = new List<string>();
foreach (var words in rows)
{
columns = words;
column = columns.Split(new[] { ',' }, StringSplitOptions.None);
for (int i = 0; i < column.Length; i++)
{
//if (columns.Split(',')[0].Equals("GroupName"))
//{
// csvCell = columns.Split(',')[i];
// Console.WriteLine(csvCell);
// GroupNameList.Add(csvCell);
//}
if (columns.Split(',')[0].Equals("OPCTag"))
{
csvCell = columns.Split(',')[i];
csvList.Add(csvCell);
}
if (columns.Split(',')[0].Equals("MQTTTag"))
{
csvCell = columns.Split(',')[i];
MQTTList.Add(csvCell);
}
}
Y = csvList.Zip(MQTTList, (k, v) => new { Key = k, Value = v }).ToDictionary(x => x.Value, x => x.Key);
MQTTServiceLogger.TraceLog(MessageType.Control, " Reading CSV file Successfully.");
}
//test Nadia
transfer.Name = "Group0";
MappedTags.Add(grp.GroupName, Y);
// check if retrieved item exist in current group
Dictionary<string, string> tagsmapped = new Dictionary<string, string>();
MappedTags.TryGetValue(transfer.Name, out tagsmapped);
List<OPCItemTransfer> ToBeWriteItems = new List<OPCItemTransfer>();
List<int> Handles = new List<int>();
List<object> Values = new List<object>();
List<string> Items = new List<string>();
foreach (var item in myItems.Iterator)
{
string itemname;
if (tagsmapped.ContainsKey(item.ItemID))
{
//override List of items
tagsmapped.TryGetValue(item.ItemID, out itemname);
item.ItemID = itemname;
ToBeWriteItems.Add(item);
//Items.Add(item.ItemValue);
MQTTServiceLogger.TraceLog(MessageType.Control, "Tags Mapping succeeded .");
}
}
if (ToBeWriteItems.Count == 0)
{
return;
}
OPCItemTransfer item1 = new OPCItemTransfer();
for (int i = 0; i < grp.listItems.Count; i++)
{
lstItemHandles.Add(grp.listItems[i].serverItemHandle);
item1 = ToBeWriteItems.Find(x => x.ItemID == grp.listItems[i].ItemID);
Values.Add(item1.ItemValue);
MQTTServiceLogger.TraceLog(MessageType.Control, "Getting items values Successfully.");
}
if (Values.Count > 0)
{
//2.write to OPC
switch (grp.grpWriteMode)
{
#region Synchronous write
case WriteModes.Synchronous:
{
int[] arrErr;
WriteSynch(grp.ServerIndex,
grp.GroupIndex, lstItemHandles.ToArray(),
Values.ToArray(), out arrErr);
break;
}
#endregion
#region Asynchronous write
case WriteModes.Asynchronous:
{
int cancelID;
int[] arrErr;
int transactionID = 0;
WriteAsynch(grp.ServerIndex, grp.GroupIndex, lstItemHandles.ToArray(),
Values.ToArray(), transactionID, out cancelID, out arrErr);
break;
}
#endregion
#region Synchronous write IO2
case WriteModes.SynchronousIO2:
{
int[] arrErr;
WriteSynch2(grp.ServerIndex,
grp.GroupIndex, lstItemHandles.ToArray(),
Values.ToArray(), out arrErr);
break;
}
#endregion
#region Asynchronous write IO3
case WriteModes.AsynchronousIO3:
{
int cancelID;
int[] arrErr;
int transactionID = 0;
WriteAsynch3(grp.ServerIndex, grp.GroupIndex, lstItemHandles.ToArray(),
Values.ToArray(), transactionID, out cancelID, out arrErr);
break;
}
#endregion
}
}
MQTTServiceLogger.TraceLog(MessageType.Error, "Error while trying to write in an OPC Server.");
}
}
catch (Exception Ex0)
{
//throw;
}
}

Elastic Search MoreLikeThis Query Never Returns Results

I must be doing something fundamentally wrong here. I'm trying to get a "More Like This" query working in a search engine project we have that uses Elastic Search. The idea is that the CMS can write tags (like categories) to the page in a Meta tag or something, and we would read those into Elastic and use them to drive a "more like this" search based upon an input document id.
So if the input document has tags of catfish, chicken, goat I would expect Elastic Search to find other documents that share those tags and not return ones for racecar and airplane.
I've built a proof of concept console app by:
Getting a local Elastic Search 6.6.1 instance running in Docker by following the instructions on https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html
Creating a new .NET Framework 4.6.1 Console App
Adding the NuGet packages for NEST 6.5.0 and ElasticSearch.Net 6.5.0
Then I created a new elastic index that contains objects (Type "MyThing") that have a "Tags" property. This tag is a random comma-delimited set of words from a set of possible values. I've inserted anywhere from 100 to 5000 items in the index in testing. I've tried more and fewer possible words in the set.
No matter what I try the MoreLikeThis query never returns anything, and I don't understand why.
Query that isn't returning results:
var result = EsClient.Search<MyThing>(s => s
.Index(DEFAULT_INDEX)
.Query(esQuery =>
{
var mainQuery = esQuery
.MoreLikeThis(mlt => mlt
.Include(true)
.Fields(f => f.Field(ff => ff.Tags, 5))
.Like(l => l.Document(d => d.Id(id)))
);
return mainQuery;
}
Full "program.cs" source:
using Nest;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Test_MoreLikeThis_ES6
{
class Program
{
public class MyThing
{
public string Tags { get; set; }
}
const string ELASTIC_SERVER = "http://localhost:9200";
const string DEFAULT_INDEX = "my_index";
const int NUM_RECORDS = 1000;
private static Uri es_node = new Uri(ELASTIC_SERVER);
private static ConnectionSettings settings = new ConnectionSettings(es_node).DefaultIndex(DEFAULT_INDEX);
private static ElasticClient EsClient = new ElasticClient(settings);
private static Random rnd = new Random();
static void Main(string[] args)
{
Console.WriteLine("Rebuild index? (y):");
var answer = Console.ReadLine().ToLower();
if (answer == "y")
{
RebuildIndex();
for (int i = 0; i < NUM_RECORDS; i++)
{
AddToIndex();
}
}
Console.WriteLine("");
Console.WriteLine("Getting a Thing...");
var aThingId = GetARandomThingId();
Console.WriteLine("");
Console.WriteLine("Looking for something similar to document with id " + aThingId);
Console.WriteLine("");
Console.WriteLine("");
GetMoreLikeAThing(aThingId);
}
private static string GetARandomThingId()
{
var firstdocQuery = EsClient
.Search<MyThing>(s =>
s.Size(1)
.Query(q => {
return q.FunctionScore(fs => fs.Functions(fn => fn.RandomScore(rs => rs.Seed(DateTime.Now.Ticks).Field("_seq_no"))));
})
);
if (!firstdocQuery.IsValid || firstdocQuery.Hits.Count == 0) return null;
var hit = firstdocQuery.Hits.First();
Console.WriteLine("Found a thing with id '" + hit.Id + "' and tags: " + hit.Source.Tags);
return hit.Id;
}
private static void GetMoreLikeAThing(string id)
{
var result = EsClient.Search<MyThing>(s => s
.Index(DEFAULT_INDEX)
.Query(esQuery =>
{
var mainQuery = esQuery
.MoreLikeThis(mlt => mlt
.Include(true)
.Fields(f => f.Field(ff => ff.Tags, 5))
.Like(l => l.Document(d => d.Id(id)))
);
return mainQuery;
}
));
if (result.IsValid)
{
if (result.Hits.Count > 0)
{
Console.WriteLine("These things are similar:");
foreach (var hit in result.Hits)
{
Console.WriteLine(" " + hit.Id + " : " + hit.Source.Tags);
}
}
else
{
Console.WriteLine("No similar things found.");
}
}
else
{
Console.WriteLine("There was an error running the ES query.");
}
Console.WriteLine("");
Console.WriteLine("Enter (y) to get another thing, or anything else to exit");
var y = Console.ReadLine().ToLower();
if (y == "y")
{
var aThingId = GetARandomThingId();
GetMoreLikeAThing(aThingId);
}
Console.WriteLine("");
Console.WriteLine("Any key to exit...");
Console.ReadKey();
}
private static void RebuildIndex()
{
var existsResponse = EsClient.IndexExists(DEFAULT_INDEX);
if (existsResponse.Exists) //delete existing mapping (and data)
{
EsClient.DeleteIndex(DEFAULT_INDEX);
}
var rebuildResponse = EsClient.CreateIndex(DEFAULT_INDEX, c => c.Settings(s => s.NumberOfReplicas(1).NumberOfShards(5)));
var response2 = EsClient.Map<MyThing>(m => m.AutoMap());
}
private static void AddToIndex()
{
var myThing = new MyThing();
var tags = new List<string> {
"catfish",
"tractor",
"racecar",
"airplane",
"chicken",
"goat",
"pig",
"horse",
"goose",
"duck"
};
var randNum = rnd.Next(0, tags.Count);
//get randNum random tags
var rand = tags.OrderBy(o => Guid.NewGuid().ToString()).Take(randNum);
myThing.Tags = string.Join(", ", rand);
var ir = new IndexRequest<MyThing>(myThing);
var indexResponse = EsClient.Index(ir);
Console.WriteLine("Index response: " + indexResponse.Id + " : " + string.Join(" " , myThing.Tags));
}
}
}
The issue here is that the default min_term_freq value of 2 will never be satisfied for any of the terms of the prototype document because all documents contain only each tag (term) once. If you drop min_term_freq to 1, you'll get results. Might also want to set min_doc_freq to 1 too, and combine with a query that excludes the prototype document.
Here's an example to play with
const string ELASTIC_SERVER = "http://localhost:9200";
const string DEFAULT_INDEX = "my_index";
const int NUM_RECORDS = 1000;
private static readonly Random _random = new Random();
private static readonly IReadOnlyList<string> Tags =
new List<string>
{
"catfish",
"tractor",
"racecar",
"airplane",
"chicken",
"goat",
"pig",
"horse",
"goose",
"duck"
};
private static ElasticClient _client;
private static void Main()
{
var pool = new SingleNodeConnectionPool(new Uri(ELASTIC_SERVER));
var settings = new ConnectionSettings(pool)
.DefaultIndex(DEFAULT_INDEX);
_client = new ElasticClient(settings);
Console.WriteLine("Rebuild index? (y):");
var answer = Console.ReadLine().ToLower();
if (answer == "y")
{
RebuildIndex();
AddToIndex();
}
Console.WriteLine();
Console.WriteLine("Getting a Thing...");
var aThingId = GetARandomThingId();
Console.WriteLine();
Console.WriteLine("Looking for something similar to document with id " + aThingId);
Console.WriteLine();
Console.WriteLine();
GetMoreLikeAThing(aThingId);
}
public class MyThing
{
public List<string> Tags { get; set; }
}
private static string GetARandomThingId()
{
var firstdocQuery = _client
.Search<MyThing>(s =>
s.Size(1)
.Query(q => q
.FunctionScore(fs => fs
.Functions(fn => fn
.RandomScore(rs => rs
.Seed(DateTime.Now.Ticks)
.Field("_seq_no")
)
)
)
)
);
if (!firstdocQuery.IsValid || firstdocQuery.Hits.Count == 0) return null;
var hit = firstdocQuery.Hits.First();
Console.WriteLine($"Found a thing with id '{hit.Id}' and tags: {string.Join(", ", hit.Source.Tags)}");
return hit.Id;
}
private static void GetMoreLikeAThing(string id)
{
var result = _client.Search<MyThing>(s => s
.Index(DEFAULT_INDEX)
.Query(esQuery => esQuery
.MoreLikeThis(mlt => mlt
.Include(true)
.Fields(f => f.Field(ff => ff.Tags))
.Like(l => l.Document(d => d.Id(id)))
.MinTermFrequency(1)
.MinDocumentFrequency(1)
) && !esQuery
.Ids(ids => ids
.Values(id)
)
)
);
if (result.IsValid)
{
if (result.Hits.Count > 0)
{
Console.WriteLine("These things are similar:");
foreach (var hit in result.Hits)
{
Console.WriteLine($" {hit.Id}: {string.Join(", ", hit.Source.Tags)}");
}
}
else
{
Console.WriteLine("No similar things found.");
}
}
else
{
Console.WriteLine("There was an error running the ES query.");
}
Console.WriteLine();
Console.WriteLine("Enter (y) to get another thing, or anything else to exit");
var y = Console.ReadLine().ToLower();
if (y == "y")
{
var aThingId = GetARandomThingId();
GetMoreLikeAThing(aThingId);
}
Console.WriteLine();
Console.WriteLine("Any key to exit...");
}
private static void RebuildIndex()
{
var existsResponse = _client.IndexExists(DEFAULT_INDEX);
if (existsResponse.Exists) //delete existing mapping (and data)
{
_client.DeleteIndex(DEFAULT_INDEX);
}
var rebuildResponse = _client.CreateIndex(DEFAULT_INDEX, c => c
.Settings(s => s
.NumberOfShards(1)
)
.Mappings(m => m
.Map<MyThing>(mm => mm.AutoMap())
)
);
}
private static void AddToIndex()
{
var bulkAllObservable = _client.BulkAll(GetMyThings(), b => b
.RefreshOnCompleted()
.Size(1000));
var waitHandle = new ManualResetEvent(false);
Exception exception = null;
var bulkAllObserver = new BulkAllObserver(
onNext: r =>
{
Console.WriteLine($"Indexed page {r.Page}");
},
onError: e =>
{
exception = e;
waitHandle.Set();
},
onCompleted: () => waitHandle.Set());
bulkAllObservable.Subscribe(bulkAllObserver);
waitHandle.WaitOne();
if (exception != null)
{
throw exception;
}
}
private static IEnumerable<MyThing> GetMyThings()
{
for (int i = 0; i < NUM_RECORDS; i++)
{
var randomTags = Tags.OrderBy(o => Guid.NewGuid().ToString())
.Take(_random.Next(0, Tags.Count))
.OrderBy(t => t)
.ToList();
yield return new MyThing { Tags = randomTags };
}
}
And here's an example output
Found a thing with id 'Ugg9LGkBPK3n91HQD1d5' and tags: airplane, goat
These things are similar:
4wg9LGkBPK3n91HQD1l5: airplane, goat
9Ag9LGkBPK3n91HQD1l5: airplane, goat
Vgg9LGkBPK3n91HQD1d5: airplane, goat, goose
sQg9LGkBPK3n91HQD1d5: airplane, duck, goat
lQg9LGkBPK3n91HQD1h5: airplane, catfish, goat
9gg9LGkBPK3n91HQD1l5: airplane, catfish, goat
FQg9LGkBPK3n91HQD1p5: airplane, goat, goose
Jwg9LGkBPK3n91HQD1p5: airplane, goat, goose
Fwg9LGkBPK3n91HQD1d5: airplane, duck, goat, tractor
Kwg9LGkBPK3n91HQD1d5: airplane, goat, goose, horse

Parallel execution issue

I need a little education here with regards to the execution of parallel tasks.
I have created a small fiddle:
https://dotnetfiddle.net/JO2a4m
What I am trying to do send a few accounts to process in batches to another method and creating a unit of work (task) for each batch but when I execute the tasks, it only executes the last task which was added. This is something I am trying to break my head around.
Code:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public class Program
{
public static void Main()
{
var accounts = GenerateAccount();
var accountsProcess = new List<Account>();
var taskList = new List<Task>();
var batch = 4;
var count = 0;
foreach (var account in accounts)
{
if (count == batch)
{
taskList.Add(new Task(() => ProcessAccount(accountsProcess)));
count = 0;
accountsProcess.Clear();
}
count++;
accountsProcess.Add(account);
}
Parallel.ForEach(taskList, t =>
{
t.Start();
}
);
Task.WaitAll(taskList.ToArray());
if (accountsProcess.Count > 0)
ProcessAccount(accountsProcess);
}
public static List<Account> GenerateAccount()
{
var accounts = new List<Account>();
var first = "First";
var second = "Second";
for (int i = 0; i <= 1000; i++)
{
var account = new Account();
account.first = first + i;
account.second = second + i;
accounts.Add(account);
}
return accounts;
}
public static void ProcessAccount(List<Account> accounts)
{
Console.WriteLine(accounts.Count);
foreach (var account in accounts)
{
Console.WriteLine(account.first + account.second);
}
}
}
public class Account
{
public string first;
public string second;
}
foreach (var account in accounts)
{
if (count == batch)
{
taskList.Add(new Task(() => ProcessAccount(accountsProcess)));
count = 0;
accountsProcess.Clear();
}
count++;
accountsProcess.Add(account);
}
The issue is that all of the Tasks are sharing the same List<Account> object.
I would suggest changing the code to:
foreach (var account in accounts)
{
if (count == batch)
{
var bob = accountsProcess;
taskList.Add(new Task(() => ProcessAccount(bob)));
count = 0;
accountsProcess = new List<Account>();
}
count++;
accountsProcess.Add(account);
}
By using bob and assigning a new List to accountsProcess we ensure each Task gets its own List - rather than sharing a single List.
Also, consider using MoreLINQ's Batch rather than rolling your own.

How to do the sequential ordering?

I have looked into this Q/A , though it is working too some extent but not as expected. I want it to happen sequentially.How to do that?
Thanks in advance.
You can use Enumerable.Zip to combine the agents and accounts together (after repeating the list of agents to match or exceed the number of accounts). Then GroupBy agent.
var repeatCount = lstAccounts.Count / lstAgents.Count + 1;
var agents = Enumerable.Repeat(lstAgents, repeatCount).SelectMany(x => x);
// agents = { "Agent1", "Agent2", "Agent3", "Agent1", "Agent2", "Agent3" }
// lstAccounts = { "1001" , "1002" , "1003" , "1004" , "1005" }
var result = agents
.Zip(lstAccounts, (agent, account) => new { Agent = agent, Account = account })
.GroupBy(x => x.Agent)
.Select(g => new { Agent = g.Key, Accounts = g.Select(x => x.Account).ToList() })
.ToList();
It might not be the fastest way to do it, but it's short and readable.
Edit
Another way (probably nicer) to achieve the same result is to start by mapping each account to an index of agent using index % lstAgents.Count.
var result = lstAccounts
.Select((acc, index) => new { AgentIndex = index % lstAgents.Count, Account = acc })
.GroupBy(x => x.AgentIndex)
.Select(g => new { Agent = lstAgents[g.Key], Accounts = g.Select(x => x.Account).ToList() })
.ToList();
The algorithm is very similar to the one proposed by varocarbas, but expressed in a functional (not imperative) way.
I think that conventional loops are the best approach here: easy-to-build, clear and very scalable-/modifiable-friendly. For example:
Dictionary<string, List<string>> results = new Dictionary<string, List<string>>();
int i = -1;
while (i < lstAccounts.Count - 1)
{
for (int i2 = 0; i2 < lstAgents.Count; i2++)
{
i = i + 1;
string curAccount = lstAccounts[i];
string curAgent = lstAgents[i2];
if (!results.ContainsKey(curAgent)) results.Add(curAgent, new List<string>());
results[curAgent].Add(curAccount);
if (i >= lstAccounts.Count - 1) break;
}
}
Additionally, note that this approach is quite fast. As a reference: around 4-5 times faster (results after a simplistic test with one of the provided inputs and a Stopwatch) than the alternative proposed by Jakub in his answer.
You can try this approach with linq extention. Split extension method will split the accounts list into "n" parts (number of agents) so that you can assign each part to agents.
class Program
{
static void Main(string[] args)
{
List<string> lstAgents = new List<string>() { "Agent1", "Agent2","Agent3" };
List<string> lstAccounts = new List<string>() { "1001", "1002" ,"1003", "1004", "1005" };
var op = lstAccounts.Split(lstAgents.Count);
int i = 0;
foreach (var accounts in op)
{
//Get agent
Console.WriteLine("Account(s) for Agent: ", lstAgents[i]);
foreach (var acc in accounts)
{
Console.WriteLine(acc);
}
Console.WriteLine(Environment.NewLine);
i++;
}
Console.ReadKey();
}
}
static class LinqExtensions
{
public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> list, int parts)
{
int i = 0;
var splits = from item in list
group item by i++ % parts into part
select part.AsEnumerable();
return splits;
}
}

get the total sum of all domain controllers

I hope someone could show me how to get to total numbers of domains from
my list? I am trying to get the total so I can implement a progress bar. I was able to
get them to count up but unable to just get the sum.
Below is where I am at currently. I am thinking I need to convert dc to int first?
public void findalldomains()
{
Dictionary<string, List<string>> findD = new Dictionary<string, List<string>>();
List<int> mydomains = new List<int>();
Domain domain = Domain.GetCurrentDomain();
using (domain)
{
StringBuilder tempText = new StringBuilder();
foreach (DomainController dc in
domain.FindAllDiscoverableDomainControllers())
{
tempText.Append(dc.Name);
tempText.Append(Environment.NewLine);
listBox1.Items.Add(dc);
int t = 0;
int domainCnt = Domain.GetCurrentDomain().FindAllDiscoverableDomainControllers().Count;
t += domainCnt;
Console.WriteLine(t);
}
}
}
// without loop and list
public static void findalldomains()
{
int t = 0;
// Console.WriteLine(dc.Name);
int domainCnt = Domain.GetCurrentDomain().FindAllDiscoverableDomainControllers().Count;
t += domainCnt;
Console.WriteLine(t);
}
How about trying
int domainCnt = Domain.GetCurrentDomain().FindAllDiscoverableDomainControllers().Count;

Categories