collection automatically triggers method - c#

I am looking for a collection object that calls a method by it self after the count reaches 1000. Here in the example when the count of element reach 1000 i want to process them.
Example:
internal static Collection<string> elements=new collection<string>
public void someMethod()
{
XDocument XD = null;
XD = XDocument.Load("https://somewebservice.asp");
var Value = XD.Descendants().where(x => x == "someAttribute").FirstorDefault();
if(value != null)
{
elements.Add(value);
}
if(elemets.count() == 1000)
{
// Do Something
// But i dont want to check the count every time
// I want the collection object to do the thing.. is there any method
// that can do this?
}
}

You can use an ObservableCollection which fires an event when the colleciton is changed. You can check the count at this time and do something.
var collection = new ObservableCollection<int>();
collection.CollectionChanged += (s, args) =>
{
if (collection.Count == 1000)
DoSomething();
};

Related

How do i access specific list object from a function? C#

Im new to programming and this might be some rookie problem that im having, but id appreaciate a hand.
void Sort()
{
List<Lag> lagen = new List<Lag>() { AIK, DIF, MLM, BJK };
List<Lag> sorted = lagen.OrderByDescending(x => x.poäng)
.ThenByDescending(x => x.målSK)
.ThenByDescending(x => x.mål)
.ToList();
Print(sorted);
}
This is my sorting function, where i take one list, and turn it into a list called "sorted".
What i want to do now is let the user pick one of the objects in the sorted list by entering a number. This far iw written the following code. Where int hemlag, and int bortlag are objects in the sorted list.
And now i want to change the value of "Lag hemmalag" & "Lag bortalag" depending on what numbers (what team) the user put in.
So for example, if the user input "int hemlag" is 2.. i want hemmalag = to be the 2nd object in the list "sorted". And now i run into a problem. Cause i cant access that sorted list from this function.
My theories are that it might have to do something with returning that list from the sorting function, but i have not yet found a way to do that...
void ChangeStats(int hemlag, int bortlag, int mål, int insläpp)
{
Sortera();
Lag hemmalag = AIK;
Lag bortalag = AIK;
if (hemlag == 1) { ; }
if (hemlag == 2) { hemmalag = DIF; }
if (hemlag == 3) { hemmalag = MLM; }
if (hemlag == 4) { hemmalag = BJK; }
if (bortlag == 1) { bortalag = AIK; }
if (bortlag == 2) { bortalag = DIF; }
if (bortlag == 3) { bortalag = MLM; }
if (bortlag == 4) { bortalag = BJK; }
hemmalag.mål += mål;
hemmalag.insläppta += insläpp;
bortalag.insläppta += mål;
bortalag.mål += insläpp;
if (mål > insläpp)
{
hemmalag.poäng += 3;
hemmalag.vinster++;
hemmalag.spel++;
bortalag.förlorade++;
bortalag.spel++;
}
if (mål < insläpp)
{
bortalag.poäng += 3;
bortalag.vinster++;
bortalag.spel++;
hemmalag.förlorade++;
hemmalag.spel++;
}
if (mål == insläpp)
{
bortalag.lika++;
bortalag.poäng++;
hemmalag.lika++;
bortalag.poäng++;
}
Console.WriteLine("Stats changed");
Console.WriteLine("---");
Save();
Sortera();
}
Help appreciated, cheers!
A good practice when programming is to try to ensure that a function has a specific purpose, and only does that specific thing. In your case your Sort-function actually does three things, create the list, sort it, and print it. So lets rewrite your sort-function to return the sorted value:
List<Lag> Sort(IEnumerable<Lag> lagen)
{
return lagen.OrderByDescending(x => x.poäng)
.ThenByDescending(x => x.målSK)
.ThenByDescending(x => x.mål)
.ToList();
}
This uses the IEnumerable<Lag> to accept any kind of sequence of Lag, List, array, HashSet etc. It helps make the code more flexible to accept a wider type of arguments.
Assuming you got the printing and user input correct, the change stats function should probably look something like:
List<Lag> ChangeStats(List<Lag> lagen, int hemlagIndex, int bortlagIndex, int mål, int insläpp){
var hemlag = lagen[hemlagIndex];
var bortalag = lagen[bortlagIndex];
// Do the stat-update logic
...
return lagen
}
You should probably also make your safe-method take a sequence of Lag as input, and move sorting and saving outside the ChangeStats method. Again try to make sure each method has a specific purpose.
These examples only uses method parameters for all the data. This is often a good thing since it makes it more obvious what data the method is using. But in some cases it might be preferable to use a field in the class instead, something like:
public class MinaLag{
private List<Lag> lagen = new List<Lag>(){ AIK, DIF, MLM, BJK };
public void Sort(){
lagen = lagen.OrderBy(...);
}
public void ChangeStats(int hemlagIndex, int bortlagIndex, int mål, int insläpp){
var hemlag = lagen[hemlagIndex];
var bortalag = lagen[bortlagIndex];
...
}
public void Print(){...}
public void Save(Stream s){...}
public static MinLag Load(Stream s){...}
}
This wraps a list of the objects and provides methods to do all the required operations on them, removing the need for the user give and receive the list for each called method.
Here is my example on the global scope list. I'm not 100% this can sort the issue but I'm confident.
class Example {
private List<Lag> Lagen {
get;
set;
} // Global Scope - Make it public if you need to access it from another
// class.
public Example() {
this.Lagen = new List<Lag>{AIK, DIF, MLM,
BJK}; // Assign intial values on class execution
}
void Sort() {
// Everything else will be the same but now you can access it from anywhere
// within the class
List<Lag> sorted = Lagen.OrderByDescending(x => x.poäng)
.ThenByDescending(x => x.målSK)
.ThenByDescending(x => x.mål)
.ToList();
Print(sorted);
}
}
your description is very convoluted and I'm not fully getting what you're up to, but...
You can return the sorted list from the Sort() function by changing the return type from void to List and at its bottom just having line like return sorted
You can also consider leaving void as return type but passing original list to it and turning it to a sorted one inside. List is an object so you'll get it back in the calling function and can further work with it there.
private List<Lag> lagen = new List<Lag>() { AIK, DIF, MLM, BJK };
private void Sortera()
{
lagen = lagen.OrderByDescending(x => x.poäng)
.ThenByDescending(x => x.målSK)
.ThenByDescending(x => x.mål)
.ToList();
Print(lagen);
}
private void ChangeStats(int hemlag, int bortlag, int mål, int insläpp)
{
Sortera();
Lag hemmalag = AIK;
Lag bortalag = AIK;
if (hemlag == 1) {; }
if (hemlag == 2) { lagen[1] = AIK }
if (hemlag == 3) { lagen[2] = MLM; }
if (hemlag == 4) { lagen[3] = BJK; }
if (bortlag == 1) { lagen[0] = AIK; }
if (bortlag == 2) { lagen[1] = DIF; }
if (bortlag == 3) { lagen[2] = MLM; }
if (bortlag == 4) { lagen[3] = BJK; }
etc.....
I'm still not sure what the rest of the solution means but this way you can change your list items

Entity Framework and copying 100 rows at the time

I'm writing my first Service Worker to handle copying all records from one table to another every 60 seconds. At the moment everything works like a charm, but after deploying I wan't to make sure that all future data will be copied 100 records at one time, until no more records left to copy - and than 60 seconds break.
Fallowing code will copy every record from given date at once, and than wait 60 seconds.
public void CopyLatestData(DateTime from)
{
using (var context = new EdDbContext())
{
var dataToCopy = context.ELMAH_Errors.Where(x => x.TimeUtc > from).ToList();
var result = dataToCopy.Select(x => new parsed_errors(x)).ToList();
context.parsed_errors.AddRange(result);
context.SaveChanges();
}
}
This is how I handle timer
private static void startTimer()
{
if (timer == null)
{
timer = new Timer();
timer.Interval = 60 * 1000;
timer.Elapsed += timer_ElapsedActions;
timer.Start();
}
}
private static void timer_ElapsedActions(object sender, ElapsedEventArgs e)
{
if (isTaskAlreadyRunning)
{
return;
}
lock (locker)
{
if (!isTaskAlreadyRunning)
{
isTaskAlreadyRunning = true;
try
{
copyDataFromRemoteDatabaseToLocalDatabase();
parseCopiedData();
}
finally
{
isTaskAlreadyRunning = false;
}
}
}
what is the easiest way to achieve what I need?
my suggestion would be add a boolean in your Entity like AlreadyCopied or IsCopied. Then use that as a marker.
public bool CopyLatestData()
{
using (var context = new EdDbContext())
{
var query = context.ELMAH_Errors.Where(x => !x.IsCopied).AsQueryable();
//if (query.Any())
if (query.Count() != 0) //this will check if some are left not copied
{
var dataToCopy = query.Take(100).ToList(); //this will only take the first 100
var result = dataToCopy.Select(x => new parsed_errors(x)).ToList();
context.parsed_errors.AddRange(result);
dataToCopy.ForEach(x => x.IsCopied = true); //this will update the records to IsCopied = true
context.SaveChanges();
if (query.Any())
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
}
then in your timer stop timer if the bool value of the if (Copylatestdata() == false)
private static void timer_ElapsedActions(object sender, ElapsedEventArgs e)
{
var stillHasRecords = CopyLatestData();
if (!stillHasRecords)
{
Timer timer = (Timer)sender; //get the sender object
timer.Stop(); // stop the sender
}
}
You could try using .Take() to limit the number of rows returned by the query, that way every 60 seconds it will return a maximum of 100 records. If fewer than 100 records are present it will only return what is there.
var dataToCopy = context.ELMAH_Errors.Where(x => x.TimeUtc > from).Take(100).ToList();
An alternative to 'IsProcessed' would be to keep 'latestProcessed' and then retrieve records that have timestamps after that, in batches of 100.
(Something like the code below, sorry for bad formatting, I'm typing on a phone)
while( keepGoing) {
var dataToCopy = context.ELMAH_Errors
.Where(x => x.TimeUtc > from)
.Where(x => x.TimeUTc > lastProcessedUtc)
.Take(100)
.OrderBy( x => x.TimeUtc)
.ToList();
(...)
if( dataToCopy.Any() ) lastProcessedUtc = data.Last().TimeUtc;
keepGoing = dataToCopy.Any(); //Or .Count() == 100

C# property assign bug

I'm working on a WPF-MVVM project and I implement asynchronous infinite loops in some background threads. What I have done in the ViewModel is
public TestVM()
{
LineIO_Task();
//some other work
}
and LineIO_Task is defined as
public void LineIO_Task()
{
foreach (Line l in Lines)
{
Task GetP = new Task(() => { EnPost(l); }, TaskCreationOptions.LongRunning);
GetP.Start();
//EnPost(Lines[i]);
}
}
Lines is an ObservableCollection that is initialized in TestVm.There are 7 items in Lines. And EnPost is defined as
public async void EnPost(Line l)
{
try
{
int last = 0;
while (true)
{
int pno = l.Com.ReadPostNo();//read a serial port
if (pno != last && pno != -1 && pno != 0)
{
Dispatcher.Invoke(() =>
{
Post p = AssignedPost.First(x => x.Num == pno);
if (p == null)
{
LocalDb.InsertPost(l.Num, new Post(pno));
}
else
{
if (p.ToLine != l.Num)
{
//update UI
Lines.First(n => n.Num == p.ToLine).Posts.First(y => y.Num == p.Num).IsValid = true;
Lines.First(n => n.Num == p.ToLine).OnPropertyChanged("RealPostCount");
}
}
});
last = pno;
}
await Task.Delay(150);
}
}
catch (Exception ex)
{
//
}
}
AssignedPost is an ObservableCollection of Post. Post has int properties ToLine and Num.
And I have a function called Assign, which add items into AssignedPost.
private int Assign(Post p)
{
int ln;
//evaluate ln by some algorithm
p.ToLine = ln;
AssignedPost.Add(p);
//some other work
return ln;
}
The problem is that the item has been added into AssignedPost, i.e., Post p = AssignedPost.First(x => x.Num == pno); p is not null. But p.ToLine, which is supposed to be assigned before the insertion, is always 0. So the bug makes the UI update raises an error. I can't see why. Does it have something to do with multithreading?
You cannot touch the UI thread from any other thread or you run into problems.
When you want to update something for the UI you need to do:
View.Dispatcher.InvokeAsync(a.Invoke, p);
Which will marshall the call to the UI thread to avoid this.
If your using something like an ObservableCollection you can use collection synchronization to call it from another thread and the calls to INotifyPropertyChanged will be marshalled to the UI thread for you.
BindingOperations.EnableCollectionSynchronization(Entries);
Just make sure to turn it off when your done to avoid a memory leak like so:
BindingOperations.DisableCollectionSynchronization(Entries);

C# Filter Items In A List According To Multiple Criteria

First, what my situation here is...
My SomeObject has a property string Status which I am interested in for this scenario.
Status property can contain "Open", "Closed", "Finished" values exactly.
I have a method called FilterObjects which returns a List<SomeObject>
Method accepts an argument same as its return type, List<SomeObject>
Method is supposed to filter according to following cases explained below and return the list of objects.
The List<SomeObject> I am sending as argument to my method is guaranteed to be in order (through their ID and type).
The cases are (all related to the string Status property I mentioned):
If any item in the list contains Status = "Finished"; then eliminate all other elements that was in the original list and return only the object that has the "Finished" status.
If any item does NOT contain Status = Finished but contains "CLOSED", I need to check if there is any other item that has the value of "Open" after that "CLOSED" one. You can think of this as a "a task can be closed, but can be reopened. But once it is finished, it cannot be reopened".
If it contains a "CLOSED" and does not have any "OPEN" after that item, I will ignore all the items before CLOSED and only return CLOSED object. If it contains "OPEN" after any closed, I need to return anything AFTER that CLOSED, by excluding itself.
I also tried explain the same thing with my awesome MS Paint skills.
The object itself is not really a problem, but my method is something like this:
private List<SomeObject> FilterObjects(List<SomeObject> objectList)
{
var objects = objectList;
var returnList = new List<SomeObject>();
foreach (var obj in objects)
{
if (obj.Status == "Finished")
{
returnList.Add(obj);
return returnList;
}
}
return new List<SomeObject>();
}
Long story short, what would be the best and most efficient way to apply all this logic in this single method? Honestly, I couldn't go further than the first case I already implemented, which is the FINISHED. Could this whole thing be done with some LINQ magic?
It is guaranteed that I receive an ordered list AND I will never get items more than a couple of hundred so the collection will never be massive.
Many thanks in advance for the help.
You can try something like that:
private List<SomeObject> FilterObjects(List<SomeObject> objectList)
{
SomeObject finished = objectList.FirstOrDefault(o => o.Status.Equals("Finished"));
if (finished != null) { return new List<SomeObject> { finished }; }
List<SomeObject> closed = objectList.SkipWhile(o => !o.Status.Equals("Closed")).ToList();
if (closed.Count == 1) { return closed; }
if (closed.Count > 1) { return closed.Skip(1).ToList(); }
// if you need a new list object than return new List<SomeObject>(objectList);
return objectList;
}
I really wouldn't bother using Linq for this, as you will either create an overly complicated instruction to manage or you will require several loop iterations. I would go for something like this instead:
private List<SomeObject> FilterObjects(List<SomeObject> objectList)
{
int lastClosed = -1;
for (int i = 0; i < objectList.Count; i++)
{
if (objectList[i].Status == "Closed")
lastClosed = i;
else if (objectList[i].Status == "Finished")
return new List<SomeObject>() { objectList[i] };
}
if (lastClosed > -1)
if (lastClosed == objectList.Count - 1)
return new List<SomeObject>() { objectList[lastClosed] };
else
return objectList.Skip(lastClosed + 1).ToList();
else
return objectList;
}
EDIT: slightly changed the last bit of code so that it won't trigger an exception if the objectList is empty
LINQ is not well suited and inefficient for scenarios where you need to apply logic based on previous / next elements of a sequence.
The optimal way to apply your logic is to use a single loop and track the Closed status and the position where the status change occurred. At the end you'll return a single element at that position if the last status is Closed, or a range starting at that position otherwise.
static List<SomeObject> FilterObjects(List<SomeObject> objectList)
{
int pos = 0;
bool closed = false;
for (int i = 0; i < objectList.Count; i++)
{
var item = objectList[i];
if (item.Status == "Finished")
return new List<SomeObject> { item };
if (item.Status == (closed ? "Opened" : "Closed"))
{
pos = i;
closed = !closed;
}
}
return objectList.GetRange(pos, closed ? 1 : objectList.Count - pos);
}
I did it this way:
public static IEnumerable<SomeObject> convert(this IEnumerable<SomeObject> input)
{
var finished = input.FirstOrDefault(x => x.Status == "Finished");
if (finished != null)
{
return new List<SomeObject> {finished};
}
return input.Aggregate(new List<SomeObject>(), (a, b) =>
{
if (!a.Any())
{
a.Add(b);
}
else if (b.Status == "Open")
{
if (a.Last().Status == "Closed")
{
a.Remove(a.Last());
}
a.Add(b);
}
else if (b.Status == "Closed")
{
a = new List<SomeObject> {b};
}
return a;
});
}
You can write a method like this. This is bare minimum you will have to add null check and exception handling.
public List<SomeCls> GetResult(List<SomeCls> lstData)
{
List<SomeCls> lstResult;
if(lstData.Any(x=>x.Status=="Finished"))
{
lstResult = lstData.Where(x=>x.Status=="Finished").ToList();
}
else if(lstData.Any(x=>x.Status=="Closed"))
{
// Here assuming that there is only one Closed in whole list
int index = lstData.FindIndex(0,lstData.Count(),x=>x.Status=="Closed");
lstResult = lstData.GetRange(index,lstData.Count()-index);
if(lstResult.Count()!=1) // check if it contains Open.
{
lstResult = lstResult.Where(x=>x.Status=="Open").ToList();
}
}
else // Only Open
{
lstResult = lstData;
}
return lstResult;
}
something like this :
private List<SomeObject> FilterObjects(List<SomeObject> objectList)
{
if (objectList.Where(x => x.Status == "Finished").Any())
{
return objectList.Where(x => x.Status == "Finished").ToList();
}
else if (objectList.Where(x => x.Status == "Closed").Any())
{
if (objectList.FindIndex(x => x.Status == "Closed") == objectList.Count() - 1)
{
return objectList.Where(x => x.Status == "Closed").ToList();
}
else
{
return objectList.GetRange(objectList.FindIndex(x => x.Status == "Closed") + 1, objectList.Count() - (objectList.FindIndex(x => x.Status == "Closed") + 1));
}
}
return objectList;
}

how to stop looping in file in c#?

I'm Trying to save a customer in file but when i'm saving it, it keeps looping the person i entered i do not know where i'm doing it wrong .The problem is i'm thinking my logic is good but i know i'm doing something wrong somewhere which i can not find it .If you could help me i really appreciate it.
public partial class admin : Window
{
accounts acclist = new accounts();
customers cuslist = new customers();
public admin(string pin, accounts myacc, customers mycus)
{
InitializeComponent();
acclist = myacc;
cuslist = mycus;
}
public void saveaccount()
{
using (StreamWriter writer = new StreamWriter("account.txt"))
{
for (int i = 0; i < acclist.Count; i++)
{
var info = new List<string>
{
acclist[i].accounttype.ToString(),
acclist[i].PIN,
acclist[i].accountnumber,
acclist[i].accountbalance.ToString()
};
var account = String.Join(";", info);
writer.WriteLine(account);
}
}
}
//save to customer file
public void savefile()
{
using (StreamWriter writer = new StreamWriter("customer.txt"))
{
for (int i = 0; i < cuslist.Count; i++)
{
var info = new List<string>
{
cuslist[i].NAME.ToString(),
cuslist[i].pin,
};
var customer = String.Join(";", info);
writer.WriteLine(customer);
}
}
}
// add user
private void Sub_Click(object sender, RoutedEventArgs e)
{
customer newCus = new customer();
account newAcc= new account();
try
{
newCus.NAME = Nameadd.Text;
newCus.pin = pinadd.Text;
newAcc.PIN = pinadd.Text;
newAcc.accountnumber = Accountnumadd.Text;
newAcc.accounttype = 'C';
for (int i = 0; i < acclist.Count; i++)
{
{
if(newAcc.accounttype == 'C')
{
newAcc.PIN = pinadd.Text;
newAcc.accountnumber = Accountnumadd.Text;
newAcc.accounttype = 'S';
}
}
cuslist.add(newCus);
acclist.add(newAcc);
savefile();
saveaccount();
}
}
catch(Exception error)
{
MessageBox.Show(error.Message);
}
}
}
In your "save" event, your doing a for loop over the "count" of elements in your account list. That account list variable is global in scope to your class. The problem is that in your loop, you're adding to that list... so really the list you're iterating over is mutating right under-neath you. As you add that customer, the loop starts next iteration, checks the "count", and ends only when "i" is equal in value to the count. However each pass you're adding to the count... so technically you'll never reach the end as the accList.Count is constantly increasing by 1 each pass.
private void Sub_Click(object sender, RoutedEventArgs e)
{
customer newCus = new customer();
account newAcc= new account();
try
{
newCus.NAME = Nameadd.Text;
newCus.pin = pinadd.Text;
newAcc.PIN = pinadd.Text;
newAcc.accountnumber = Accountnumadd.Text;
newAcc.accounttype = 'C';
for (int i = 0; i < acclist.Count; i++) // here you are checking acclist.Count... each iteration this increases by 1, thus i will never technically ever be equivalent to acclist.Count
{
{
if(newAcc.accounttype == 'C')
{
newAcc.PIN = pinadd.Text;
newAcc.accountnumber = Accountnumadd.Text;
newAcc.accounttype = 'S';
}
}
cuslist.add(newCus);
acclist.add(newAcc); // <-- this is where you're adding to acclist each time. However inside this loop you're constantly increasing its size... thus your infinite loop you're hitting.
savefile();
saveaccount();
}
}
catch(Exception error)
{
MessageBox.Show(error.Message);
}
}
I suggest for one, you use a ForEach statement as it's been suggested. Also, use a separate list to hold your "new" accounts. If you need to add to acclist for whatever reason, then after you've added to your new list, iterate over that and add each one of those back to your acclist. This way you avoid mutating the very object you're looping over.
Another way is to first store the "count" into a variable and check against that so it never changes.
var myCounter = acclist.Count
for (int i = 0; i < myCounter ; i++)
{
...
But, I don't know which option is best for you as I obviously don't know the larger context of what it is you need to ultimately do. :) Either solution should stop your infinite loop though.
Consider the use of a foreach loop. This loop is best used for applications like the one in your savefile() method. It's great for applications where you want to perform an action for every item in a list or collection.
See here: https://msdn.microsoft.com/en-us/library/ttw7t8t6.aspx?f=255&MSPPError=-2147217396
EDIT: The requested example:
List<string> FruitBasket = new List<string>();
FruitBasket.Add("apple");
FruitBasket.Add("banana");
FruitBasket.Add("orange");
foreach (string fruit in FruitBasket)
{
Console.WriteLine(fruit);
}
and this results in an output of:
apple, banana, orange.
In the foreach loop you need to declare a variable of the same type as your collection (so in this example I had a List of strings so I made my variable a string, and then they keyword in assigns this variable to each item in that collection, one at a time, starting from the beginning.

Categories