for loop desired instead of nested if statements - c#

I am building a website that does has a chat component to it. The code below receives from a stored procedure a list of messages with a lots of different paramaters. 1 of those is if a message is in reply to another, and if that is the case, duplicate the message that is being replied to over the message answer. If the message that was being replied to was also an answer to a previous message do the same ect. Now my issue is that I have not been able to figure out how to automate this part of the code without nesting if into one another until a point where I hope users won't reply in the same chain anymore.
To rephrase it, I go in a list in inverse order and check if the ReplyingTo is not null.
I then copy the row that has the same ID and ReplyingTo 1 row higher than the current row.
I then confirm that this new row has a ReplyingTo
If it does I copy that object 2 row higher than the current one.
and I would continue this way until I reached a certain point that the users would not reach.
If anyone got an idea on how to proceed I would be highly gracious. I have put an example of the type of data that would be given to this function below.
for (int i = publicChatCountList.Count-1 ; i > -1; i--)
{
if (publicChatCountList[i].ReplyingTo.HasValue)
{
Chat_Dto chatItem = new Chat_Dto();
long? ReplyingToId = publicChatCountList[i].ReplyingTo;
chatItem = publicChatCountList.Find(x => x.Id == ReplyingToId);
publicChatCountList.Insert(i+1, new Chat_Dto() {Text = chatItem.Text, IsPublic = chatItem.IsPublic, IsApproved = chatItem.IsApproved, ReplyingTo = chatItem.ReplyingTo });
publicChatCountList[i+1].Duplicate = true;
if (chatItem.ReplyingTo.HasValue)
{
Chat_Dto chatItem2 = new Chat_Dto();
long? ReplyingToId2 = chatItem.ReplyingTo;
chatItem2 = publicChatCountList.Find(x => x.Id == ReplyingToId2);
publicChatCountList.Insert(i + 2, new Chat_Dto() { Text = chatItem2.Text, IsPublic = chatItem2.IsPublic, IsApproved = chatItem2.IsApproved, ReplyingTo = chatItem2.ReplyingTo });
publicChatCountList[i + 2].Duplicate = true;
}
}
}

If I understood you correctly maybe running something like this to recursively get all replies would work:
private void Replies(Chat_Dto_List publicChatCountList,int i)
{
if (publicChatCountList[i].ReplyingTo.HasValue)
{
Chat_Dto chatItem = new Chat_Dto();
long? ReplyingToId = publicChatCountList[i].ReplyingTo;
chatItem = publicChatCountList.Find(x => x.Id == ReplyingToId);
publicChatCountList.Insert(i + 1, new Chat_Dto() { Text = chatItem.Text, IsPublic = chatItem.IsPublic, IsApproved = chatItem.IsApproved, ReplyingTo = chatItem.ReplyingTo });
publicChatCountList[i + 1].Duplicate = true;
if (chatItem.ReplyingTo.HasValue)
{
Replies(publicChatCountList, publicChatCountList.FindIndex(x => x.Id == chatItem.ReplyingTo))
}
}
}

Related

How to write LINQ query for fetching the specific records and generating new result at the same time?

I have to update one field in the row of the table after fetching two records from the same row. As an easiest practice I have fetched two records individually, created a new value and then updating that particular property through Entity framework. I think there is a better way to do the same thing with less code. If any body can suggest please.
if (objModel.amountpaid==0)
{
using (estatebranchEntities db=new estatebranchEntities())
{
int rentVar = Convert.ToInt32(db.PropertyDetails.Where(m => m.propertyid == objVM.propertyid).Select(m => m.rent).SingleOrDefault());
int balanceVar = Convert.ToInt32(db.PropertyDetails.Where(m => m.propertyid == objVM.propertyid).Select(m => m.balance).SingleOrDefault());
int balanceUpdateVar = (rentVar + balanceVar);
var propInfo = new PropertyDetail() { balance = balanceUpdateVar };
//var result = (from a in db.PropertyDetails
// where a.propertyid == objVM.propertyid
// select new PropertyDetail
// {
// rent = a.rent,
// balance = a.balance
// }).ToList();
db.PropertyDetails.Attach(propInfo);
db.Entry(propInfo).Property(z => z.balance).IsModified = true;
db.SaveChanges();
}
}
Here is what I think you can do.
Fetch the data once and update once.
using (estatebranchEntities db=new estatebranchEntities())
{
var propDetails = db.PropertyDetails.FirstOrDefault(m => m.propertyid == objVM.propertyid);
if (propDetails != null)
{
int rentVar = Convert.ToInt32(propDetails.rent);
int balanceVar = Convert.ToInt32(propDetails.balance);
int balanceUpdateVar = rentVar + balanceVar;
//now do the update
propDetails.balance = balanceUpdateVar;
db.Entry(proDetails).State = EntityState.Modified;
db.SaveChanges();
}
}
if you need to use the rentVar,balanceVar or the balanceUpdateVar, outside of the using statement then declare them outside it.

Retrieve single record to model with EF Linq, does it need to loop to populate?

Most of the time I retrieve multiple records so I would end up doing this
var rpmuser = new List<rpm_scrty_rpm_usr>();
I have my List collection of properties from poco
So I typically use select new in my Linq statement
Then I use a foreach and loop over the records in which the List would get model.Add(new instance in each loop)
However , do I really need to be doing all this looping to populate?
Bigger question when i have a single record should I be needing to even do a loop at all?
public bool UpdateAllUsers(string user, string hash, string salt)
{
bool status = false;
var rpmuser = new rpm_scrty_rpm_usr();
var query = (from t in db.rpm_usr
.Where(z => z.usr_id == "MillXZ")
select new
{
t.usr_id,
t.usr_lnm,
t.usr_pwd,
t.usr_fnm,
t.salt,
t.inact_ind,
t.lst_accs_dtm,
t.lst_pwd_chg_dtm,
t.tel,
t.wwid,
t.email_id,
t.dflt_ste_id,
t.apprvr_wwid,
t.chg_dtm,
t.chg_usr_id,
t.cre_dtm,
t.cre_usr_id,
});
foreach(var s in query)
{
rpmuser.wwid = s.wwid;
rpmuser.usr_pwd = s.usr_pwd;
rpmuser.usr_lnm = s.usr_lnm;
rpmuser.usr_id = s.usr_id;
rpmuser.usr_fnm = s.usr_fnm;
rpmuser.tel = s.tel;
rpmuser.salt = s.salt;
rpmuser.lst_pwd_chg_dtm = rpmuser.lst_pwd_chg_dtm;
rpmuser.lst_accs_dtm = s.lst_accs_dtm;
rpmuser.inact_ind = s.inact_ind;
rpmuser.email_id = s.email_id;
rpmuser.apprvr_wwid = s.apprvr_wwid;
rpmuser.chg_dtm = s.chg_dtm;
rpmuser.chg_usr_id = s.chg_usr_id;
rpmuser.cre_usr_id = s.cre_usr_id;
rpmuser.dflt_ste_id = s.dflt_ste_id;
rpmuser.cre_dtm = s.cre_dtm;
}
DateTime dateTime = DateTime.Now;
try
{
rpmuser = db.rpm_usr.Find(rpmuser.usr_id);
rpmuser.usr_pwd = hash;
rpmuser.salt = salt;
db.SaveChanges();
status = true;
}
catch (Exception ex)
{
status = false;
}
return status;
}
I am not exactly sure what you want. Your method says Update All, but only seems to be attempting to update one record. So why don't you just do this?
try
{
var rpmuser = db.rpm_usr.Single(z => z.usr_id == "MillXZ");
rpmuser.usr_pwd = hash;
rpmuser.salt = salt;
db.SaveChanges();
status = true;
}
catch (Exception ex)
{
status = false;
}
You have a lot of redundant declarations unless I am missing something. In the case of the list you will do something like this:
var query = db.rpm_usr.Where(z => z.usr_id == "...some string...");
foreach(var item in query)
{
rpmuser.usr_pwd = ...some value...;
rpmuser.salt = ...some value...;
}
db.SaveChanges();
I can't stress this enough, Murdock's answer is absolutely the right way to fix the code you've shown. You are writing way too much code for what you're trying to accomplish.
However, to answer your question about whether you need to loop in other situations, you can get away from having to loop by doing the projection into a new type as part of your LINQ-to-Entities query. The looping still happens, you just don't see it.
var query = db.rpm_usr
.Where(z => z.usr_id == "MillXZ")
.AsEnumerable()
.Select(z => new rpm_scrty_rpm_usr()
{
usr_id = z.usr_id,
usr_lnm = z.usr_lnm,
// etc...
});
You would then finish the query off with a .Single(), .SingleOrDefault(), or .ToList() depending on whether you expected exactly one, one or zero, or a list. For example, in this case if you might find one or zero users with the name "MillXZ" you would write the following.
var query = db.rpm_usr
.Where(z => z.usr_id == "MillXZ")
.AsEnumerable()
.Select(z => new rpm_scrty_rpm_usr()
{
usr_id = z.usr_id,
usr_lnm = z.usr_lnm,
// etc...
})
.SingleOrDefault();

Two CSV files to one output, join and add data on column(s)

I'm working in C# (.Net 4) and I am trying to do several things:
I have 2 files ("Offline.csv","online.csv"), and I'm having those files make one "master" file (called "Attendance.csv")
Both offline.csv and online.csv contain similar data---
My Offline.csv file has:
(ID),(TimeInMin),(DateWithoutSlashes yyymmdd)
01,10,20151201
01,05,20151202
02,11,20151201
03,11,20151202
My Online.csv file has
(ID),(TimeInMin),(DateWithoutSlashes yyymmdd)
01,70,20151201
02,20,20151202
03,22,20151202
After my program is ran, the Attendance.csv should look something like:
(Same headers)
01,80,20151201
01,05,20121502 (notice the date from offline.csv, which doesn't exist in the online.csv)
02,31,20151201
03,33,20151202
So what I'm trying to do is:
Compare the data from both the offline.csv and online.csv files. If data matches on the "ID" and "Date" columns, add the minutes together (column 2) and put them as a row in the Attendance.csv file
However, IF the offline.csv contains rows that the online.csv doesn't have, then put all those other records into the Attendance.csv on their own. Perform the same action with the online.csv, being mindful to not duplicate minutes that were already merged together from step #1
I don't know if that all makes sense, but I hope it does :X
I have been beating my head against the wall all day with this, and I don't know what else to look at.
With all that said, here is what I have so far:
I have created my own class, called "aoitime", it looks as follows:
public class aoitime
{
public string ID { get; set; }
public string online { get; set; }
public string offline { get; set; }
public string dtonline { get; set; }
public string dtoffline { get; set; }
public string date { get; set; }
}
I then use IEnumerable in a different function, looks similar to ...
IEnumerable<aoitime> together =
from online in onlinefile
let onlineFields = online.Split(',')
from id in offlinefile
let offlineFields = id.Split(',')
where (onlineFields[0] == offlineFields[0] && onlineFields[2] == offlineFields[2]) || (!offlineFields[1].Contains(""))
orderby onlineFields[0]
select new aoitime
{
ID = onlineFields[0],
online = onlineFields[1],
offline = offlineFields[1],
dtonline = onlineFields[2],
dtoffline = offlineFields[2],
date = onlineFields[2]
};
StreamWriter Attendance = new StreamWriter(destination);
Attendance.Write("SIS_NUMBER,MINUTES,DATE" + Environment.NewLine);
foreach (aoitime att in together)
{
int date = int.Parse(att.date);
int dateonline = int.Parse(att.dtonline);
int dateoffline = int.Parse(att.dtoffline);
int online = int.Parse(att.online);
int offline = int.Parse(att.offline);
int total = (online + offline);
Console.WriteLine("Writing total time now: "+online);
Attendance.Write(att.ID + "," + total + "," date + Environment.NewLine);
}
I then tried creating another IEnumerable class spawn that looks similar to the one above, but instead using "where offlineFields[2] != onlineFields[2]" but I get unpredictable results. I just don't know where else to look or what else to do.
Please be gentle, I'm very much new to programming in general (I promise this isn't for a classroom assignment :-)
thanks so much for any advice and reading this book!
You are almost there. I wrote this code, so hopefully you will be able to learn something from it.
First you only need one entity class for this. Note the ToString method. You will see how it's used later.
public class Attendance
{
public int Id { get; set; }
public int TimeInMinutes { get; set; }
public string Date { get; set; }
public override string ToString()
{
return string.Format("{0},{1},{2}", Id, TimeInMinutes, Date);
}
}
Now the code to parse your files and create the new file. Read my comments in the code.
var onlineEntries = File.ReadAllLines(#"c:\online.txt");//read online file
var validOnlineEntries = onlineEntries.Where(l => !l.Contains("(")); //remove first line
var onlineRecords = validOnlineEntries.Select(r => new Attendance()
{
Id = int.Parse(r.Split(new[] {","}, StringSplitOptions.None)[0]),
TimeInMinutes = int.Parse(r.Split(new[] {","}, StringSplitOptions.None)[1]),
Date = r.Split(new[] {","}, StringSplitOptions.None)[2],
}).ToList();//popultae Attendance class
var offlineEntries = File.ReadAllLines(#"c:\offline.txt"); //read online file
var validOfflineEntries = offlineEntries.Where(l => !l.Contains("(")); //remove first line
var offlineRecords = validOfflineEntries.Select(r => new Attendance()
{
Id = int.Parse(r.Split(new[] { "," }, StringSplitOptions.None)[0]),
TimeInMinutes = int.Parse(r.Split(new[] { "," }, StringSplitOptions.None)[1]),
Date = r.Split(new[] { "," }, StringSplitOptions.None)[2],
}).ToList();//popultae Attendance class
var commonRecords = (from n in onlineRecords
join f in offlineRecords on new {n.Date, n.Id } equals new {f.Date, f.Id} //if Date and Id are equal
select new { n.Id, TimeInMinutes = (n.TimeInMinutes + f.TimeInMinutes), n.Date }).OrderBy(x => x.Id).Distinct().ToList(); //add Online and Off line time
var newRecords = commonRecords.Select(r => new Attendance()
{
Id = r.Id,
TimeInMinutes = r.TimeInMinutes,
Date = r.Date,
}); //Poulate attendance again. So we can call toString method
onlineRecords.AddRange(offlineRecords); //merge online and offline
var recs = onlineRecords.Distinct().Where(r => !newRecords.Any(o => o.Date == r.Date && o.Id == r.Id)).ToList(); //remove already added items from merged online and offline collection
newRecords.AddRange(recs);//add filtered merged collection to new records
newRecords = newRecords.OrderBy(r => r.Id).ToList();//order new records by id
File.WriteAllLines(#"C:\newFile.txt", newRecords.Select(l => l.ToString()).ToList()); //write new file.
Just to add this as an answer, I am selecting #Kosala-w's suggestion as an answer. My code now looks very identical to what he posted, except I modified the ID to a string format because the integers used for the IDs are pretty lenghty.
I thank both people who answered this question, and I appreciate the SO community! Have a good day :-)
public class Attendance
{
public string Id { get; set; }
public int TimeInMinutes { get; set; }
public int Code { get; set; }
public string Date { get; set; }
public override string ToString()
{
return string.Format("{0},{1},{2}", Id, TimeInMinutes, Date);
}
}
I also have more rows that I have to handle in the Attendance sheet than I stated in my original question (I didn't worry about those because I wasn't concerned that I'd have a hard time getting what I needed.)
Anyway, the code below is what I used, again, thanks Kosala.
private void createAttendance()
{
try
{
txtStatus.ResetText();
txtStatus.Text += "Creating Attendance file. Please wait.";
string destination = (#"C:\asdf\Attendance.csv");
barStatus.Caption = "Processing Attendance file. Please wait.";
if (File.Exists(destination))
File.Delete(destination);
var validOnlineEntries = File.ReadAllLines(#"C:\asdf\online.csv");//read online file
//var validOnlineEntries = onlineEntries.Where(l => !l.Contains("(")); //remove first line
var onlineRecords = validOnlineEntries.Select(r => new Attendance()
{
Id = (r.Split(new[] { "," }, StringSplitOptions.None)[0] + ",202" + "," + txtYear.Text),
TimeInMinutes = int.Parse(r.Split(new[] { "," }, StringSplitOptions.None)[1]),
Date = r.Split(new[] { "," }, StringSplitOptions.None)[2],
}).ToList();//populate Attendance class
var validOfflineEntries = File.ReadAllLines(#"C:\asdf\offline.csv"); //read online file
//var validOfflineEntries = offlineEntries.Where(l => !l.Contains("(")); //remove first line
var offlineRecords = validOfflineEntries.Select(r => new Attendance()
{
Id = (r.Split(new[] { "," }, StringSplitOptions.None)[0] + ",202" + "," + txtYear.Text),
TimeInMinutes = int.Parse(r.Split(new[] { "," }, StringSplitOptions.None)[1]),
Date = r.Split(new[] { "," }, StringSplitOptions.None)[2],
}).ToList();//populate Attendance class
var commonRecords = (from n in onlineRecords
join f in offlineRecords on new { n.Date, n.Id } equals new { f.Date, f.Id } //if Date and Id are equal
select new { n.Id, TimeInMinutes = (n.TimeInMinutes + f.TimeInMinutes), n.Date }).OrderBy(x => x.Id).Distinct().ToList(); //add Online and Off line time
var newRecords = commonRecords.Select(r => new Attendance()
{
Id = r.Id,
TimeInMinutes = r.TimeInMinutes,
Date = r.Date,
}).ToList(); //Populate attendance again. So we can call toString method
onlineRecords.AddRange(offlineRecords); //merge online and offline
var recs = onlineRecords.Distinct().Where(r => !newRecords.Any(o => o.Date == r.Date && o.Id == r.Id)).ToList(); //remove already added items from merged online and offline collection
newRecords.AddRange(recs);//add filtered merged collection to new records
newRecords = newRecords.OrderBy(r => r.Id).ToList();//order new records by id
StreamWriter Attendance = new StreamWriter(destination);
//Attendance.Write("SIS_NUMBER,SCHOOL_CODE,SCHOOL_YEAR,ABSENCE_DATE,ABSENCE_REASON1,ABSENCE_REASON2,MINUTES_ATTEND,NOTE,ABS_FTE1,ABS_FTE2" + Environment.NewLine);
Attendance.Write("SIS_NUMBER,SCHOOL_CODE,SCHOOL_YEAR,MINUTES_ATTEND,ABSENCE_DATE,ABSENCE_REASON2,ABSENCE_REASON1,NOTE,ABS_FTE1,ABS_FTE2" + Environment.NewLine);
Attendance.Dispose();
File.AppendAllLines(destination, newRecords.Select(l => l.ToString()).ToList()); //write new file.
Convert_CSV_To_Excel();
}
catch(Exception ex)
{
barStatus.Caption = ("ERROR: "+ex.Message.ToString());
}
}
I plan to do some more fine tuning, but this sure got me in the right direction!
The first thing that I'd do is define a simpler class to hold your aoitimes. For example:
public class aoitime
{
public string ID { get; set; }
public int TimeInMinutes { get; set; }
public string DateWithoutSlashes { get; set; }
}
Then, you'll want to parse the string from the csv file into that class. I figure that that's an implementation detail that you can probably figure out on your own. If not, leave a comment and I can post more details.
Next, the tricky part is that you want not only a join, but you want the exceptions as well. The join logic is fairly simple:
var matches = from offline in offlineItems
join online in onlineItems
on
new {offline.ID, offline.DateWithoutSlashes} equals
new {online.ID, online.DateWithoutSlashes}
select new aoitime
{
ID = offline.ID,
TimeInMinutes = offline.TimeInMinutes + online.TimeInMinutes,
DateWithoutSlashes = offline.DateWithoutSlashes
};
(Notice there that you're using anonymous objects in the "ON" join condition). But the hard part is how to get the exceptions. LINQ is set up to do inner joins or equijoins, but I'm not sure about outer joins. At least I haven't seen it.
So one solution might be to use the LINQ join to get the matches and then another LINQ query to get those that don't match and then combine those two collections and write them out to a file.
Another solution might be to go back to basics and do the iteration logic yourself. LINQ is just elegant iteration logic and if it doesn't do what you need it to do, you might need to do it yourself.
For example, let's say that you have your collection of online and offline items and you want to iterate through them and do the comparison:
List<aoitime> offlineItems = <some method that produces this list>
List<aoitime> onlineItems = <some method that produces this list>
List<aoitime> attendanceItems = new List<aoitime>();
//For simplicity, assuming that you have the same number of elements in each list
for (int i = 0; i < offlineItems.Count; i++)
{
aoitime offline = offlineItems[i];
aoitime online = onlineItems[i];
if(offline.ID == online.ID && offline.DateWithoutSlashes = online.DateWithoutSlashes)
{
//Create your new object and add it to the attendance items collection.
}
else
{
//Process the exceptions and add them individually to the attendance items collection.
}
}
So you do the iteration and processing yourself and have control over the whole process. Does that make sense? If not, let me know in a comment and I can add more.

Entity Framework not always pulls data

I have been working on a website and for the first time started using Entity Framework 6. The application is MVC4, C#.
The issue I am having is that EF not always pulls data from the table. For example I will load a page and get an empty result. But if I refresh the page then the data from database will be shown. If I refresh again it goes empty again.
Basically, it's been pulling data every second time (and been very consistent in this behaviour).
I'm really at lost, I even tried to pull data from view instead of the table, but the behaviour stayed consistent.
Here is the relevant code from my application
#region returns list of new files uploaded in the last 24 hours
[HttpPost]
[InitializeSimpleMembership]
public ActionResult LastUploadedFiles()
{
var path = Server.MapPath("~/" + this.uploadPath + "/");
FileListModel filesList = new FileListModel();
using (CCGFileShareEntities db = new CCGFileShareEntities())
{
db.Configuration.AutoDetectChangesEnabled = false;
db.Configuration.ValidateOnSaveEnabled = false;
DateTime daydiff = DateTime.Now.AddHours(-24);
filesList.files = (from r in db.viewFileUploads
where r.file_upload_date >= daydiff && r.file_deleted == 0
//orderby r.file_upload_date descending
select new FileModels()
{
id = r.id,
fileName = r.file_name,
file_description = (r.file_description == null ? "" : r.file_description),
upload_extention = r.upload_extention,
upload_folder = r.upload_folder,
file_upload_date = r.file_upload_date,
upload_owner_id = r.upload_owner_id.ToString(),
file_size = r.file_size.ToString(),
description_is_visible = false
}).ToList();
//get owner and convert date
FileHelpers fhelpers = new FileHelpers();
for (int i = 0; i < filesList.files.Count(); i++)
{
filesList.files[i].file_upload_date_string = filesList.files[i].file_upload_date.ToString("MMM dd, yyyy");
int ownerID = int.Parse(filesList.files[i].upload_owner_id);
filesList.files[i].fileOnwerName = fhelpers.getUserName(ownerID);
}
filesList.currentUserID = WebSecurity.GetUserId(User.Identity.Name);
//public string upload_owner_id { get; set; }
}
return Json(filesList);
}
I've no idea why it doesn't pull data every single time as it supposed to do.
I'm really at loss and would appreciate your help.
Thanks in advance,

C# Duplicate Data Problem

I am having problems with duplicate data being inserted in to the database, am I passing a wrong parameter in the IEnumerable<Location>?
It doesn't bring up any errors when I debug the app.
IEnumerable<Location> locations = context.Locations.Where(l => l.FacebookID == facebookID);
if (locations.Count() == 0)
{
Location newLocation = new Location();
newLocation.FacebookID = locationID;
newLocation.Description = locationValue;
IGeoCoder geoCoder = new GoogleGeoCoder(GoogleAPIKey);
Address[] addresses = geoCoder.GeoCode(locationValue);
if (addresses.Length > 0)
{
// Let's assume the first one is good enough
Address address = addresses[0];
newLocation.Latitude= address.Coordinates.Latitude.ToString();
newLocation.Longitude = address.Coordinates.Longitude.ToString();
// Use location.Latitude and location.Longitude
}
context.Locations.AddObject(newLocation);
context.SaveChanges();
}
I am guessing you did not mean to do this:
newLocation.FacebookID = locationID;
But rather this:
newLocation.FacebookID = facebookID;
Basically you are creating multiple records, with the same facebookId, as you actually used the locationID instead.

Categories

Resources