Compare Two Complex List of data - c#

When I am Trying to save current list of data into database, I need to get already existing data from database, and need to compare with current list of data.
I have two lists one is PreviousList(existing data from DB) and other is CurrentList(Modified data)
public class SoftClose
{
public int ID = -1;
public int AID = -1;
public int WFID = -1;
public string PREFIX;
public DateTime SCDATE;
public string STATUS;
}
In CurrentList I modified Prefix to D2 where ID=1 and added new row(Id=4)...
My req is
When I am trying to save CurrentList to Db,
If there is any new Prefix in CurrentList that is not there in PreviousList I need to insert that new row and need to change Status to ADD for that row.
I changed Prefix to D2 where Id = 1 in CurrentList. D1 is there is DB and but not in CurrentList so i need to delete it. So i need to change the status to DELETE for that record. I should not insert D2 record where id=1 becuase D2 is already there. If I changed to D5 where Id = 1 then I need to insert it because D5 is not there in DB So i need to change the status to UPDATE.
How to do this? What is the best approach to compare lists

here is a solution you could try:
List<SoftClose> previousList = new List<SoftClose>(){
new SoftClose(){ID=1, Status = "NO_CHANGE",AID="19", Prefix = "D1"},
new SoftClose(){ID=2, Status = "NO_CHANGE",AID="20", Prefix = "D2"},
new SoftClose(){ID=3, Status = "NO_CHANGE",AID="21", Prefix = "D3"}
};
List<SoftClose> currentList = new List<SoftClose>(){
new SoftClose(){ID=1, Status = "NO_CHANGE",AID="19", Prefix = "D2"},
new SoftClose(){ID=2, Status = "NO_CHANGE",AID="20", Prefix = "D2"},
new SoftClose(){ID=3, Status = "NO_CHANGE",AID="21", Prefix = "D6"},
new SoftClose(){ID=4, Status = "NO_CHANGE",AID="22", Prefix = "D4"},
new SoftClose(){ID=5, Status = "NO_CHANGE",AID="22", Prefix = "D5"}
};
var addlist = currentList.Where(c => previousList.All(p => !p.ID.Equals(c.ID) && !p.Prefix.Equals(c.Prefix)));
foreach(var n in addlist)
{
var index = currentList.FindIndex(p => p.Prefix.Equals(n.Prefix));
currentList[index].Status = "ADD";
}
var updateORdeletelist = currentList.Where(c => c.Status.Equals("NO_CHANGE") && previousList.Exists(p => p.ID.Equals(c.ID) && !p.Prefix.Equals(c.Prefix)));
foreach (var n in updateORdeletelist)
{
var index = currentList.FindIndex(p => p.Prefix.Equals(n.Prefix));
if (previousList.FindIndex(p => p.Prefix.Equals(n.Prefix)) < 0)
currentList[index].Status = "UPDATE";
else
currentList[index].Status = "DELETE";
}
foreach (var item in currentList)
{
Console.WriteLine($"Id:{item.ID}, Desc1:{item.Prefix}, Status:{item.Status}");
}
output
Id:1, Desc1:D2, Status:DELETE
Id:2, Desc1:D2, Status:NO_CHANGE
Id:3, Desc1:D6, Status:UPDATE
Id:4, Desc1:D4, Status:ADD
Id:5, Desc1:D5, Status:ADD

There is a tool called Side by Side SQL Comparer in C# at https://www.codeproject.com/Articles/27122/Side-by-Side-SQL-Comparer-in-C.
basic use of the component:
using (TextReader tr = new StreamReader(#"c:\1.sql"))
{
sideBySideRichTextBox1.LeftText = tr.ReadToEnd();
}
using (TextReader tr = new StreamReader(#"c:\2.sql"))
{
sideBySideRichTextBox1.RightText = tr.ReadToEnd();
}
sideBySideRichTextBox1.CompareText();
You load the left and right sides to their respective variables sideBySideRichTextBox1.LeftText and sideBySideRichTextBox1.RightText and compare them with sideBySideRichTextBox1.CompareText();
In your case the 1.sql and 2.sql would be your PreviousList and CurrentList -database files.
There is more detailed documentation at the project-site.

Related

SaveChanges() inside foreach, to get ID inserted

I'm trying to insert inside a foreach and then get the ID inserted to insert this ID in another table (head and detail). And I have 2 foreach inside another. I'm using entity framework.
var remisiones = dbLocal.Database.SqlQuery<datosConsulta>(qDocumento).ToList();
foreach (var x in remisiones)
{
var bcDoc = new bcDocumentos();
bcDoc.cfecha = Convert.ToDateTime(x.CFECHA);
dbLocal.bcDocumentos.Add(bcDoc);
dbLocal.SaveChanges();
int idBcDocumento = bcDoc.id_documento; // I NEED TO GET THIS ID INSERTED
foreach (var z in movimientos)
{
var bcMovs = new bcMovimientos();
bcMovs.id_documento = idBcDocumento; // TO INSERT HERE IN THE DETAIL TABLE
dbLocal.bcMovimientos.Add(bcMovs);
dbLocal.SaveChanges();
int idBcMovimiento = bcMovs.id_movimiento; // AND THEN GET THE NEW ID FO THE DETAIL
}
}
If you have specified the id_documento field in the database as Identity, you can use the following code, otherwise change id_documento to Identity.
int idBcDocumento = dbLocal.bcDocumentos.OrderByDescending(p => p.id_documento).First().id_documento;
full code
var remisiones = dbLocal.Database.SqlQuery<datosConsulta>(qDocumento).ToList();
foreach (var x in remisiones)
{
var bcDoc = new bcDocumentos();
bcDoc.cfecha = Convert.ToDateTime(x.CFECHA);
dbLocal.bcDocumentos.Add(bcDoc);
dbLocal.SaveChanges();
int idBcDocumento = dbLocal.bcDocumentos.OrderByDescending(p => p.id_documento).First().id_documento; // I NEED TO GET THIS ID INSERTED
foreach (var z in movimientos)
{
var bcMovs = new bcMovimientos();
bcMovs.id_documento = idBcDocumento; // TO INSERT HERE IN THE DETAIL TABLE
dbLocal.bcMovimientos.Add(bcMovs);
dbLocal.SaveChanges();
int idBcMovimiento = dbLocal.bcMovs.OrderByDescending(p => p.id_movimiento).First().id_movimiento; // AND THEN GET THE NEW ID FO THE DETAIL
}
}

csvHelper Put values from different lines into one filed in Database

I have a .csv file structured like so, first row is the header column, for each SomeID I need to add the NetCharges together(or substract if the code calls for it) and put each item into its own column by the SomeCode column.
Heres the file I receive;
SomeID,OrderNumber,Code,NetCharge,Total
23473,30388,LI 126.0000, 132.00
96021, 000111, LI, 130.00, 126.00
23473,30388,FU 6.0000, 132.00
4571A,10452,LI,4100.0000, 4325.0000
4571A,10452,FU,150.00,4325.0000
4571A,10452,DT,75.00,4325.0000
I need to insert the data to my sql table which is structured like this. This is what I'm aiming for:
ID OrderNumber LICode LICodeValue FUCode FUCodeValue DTCode, DTCodeValue, total
23473 30388n LI 126.000 FU 6.0000 NULL NULL 132.0000
4571A 10452 LI 4100.0000 FU 150.0000 DT 75.00 4325.0000
My SomeID will not always be grouped together like the 4571A id is.I basically need to iterate over this file and create one record for each SomeID. I cannot seem to find a way with csvHelper. I'm using C# and csvHelper. I have trid this so far but I cannot get back to the SomeId after passing on to the nexr one:
using (var reader = new StreamReader( "C:\testFiles\some.csv" ))
using (var csv = new CsvReader( reader, CultureInfo.InvariantCulture ))
{
var badRecords = new List<string>();
var isRecordBad = false;
csv.Configuration.HasHeaderRecord = true;
csv.Configuration.HeaderValidated = null;
csv.Configuration.IgnoreBlankLines = true;
csv.Configuration.Delimiter = ",";
csv.Configuration.BadDataFound = context =>
{
isRecordBad = true;
badRecords.Add( context.RawRecord );
};
csv.Configuration.MissingFieldFound = ( s, i, context ) =>
{
isRecordBad = true;
badRecords.Add( context.RawRecord );
};
List<DataFile> dataFile = csv.GetRecords<DataFile>().ToList();
//initialize variable
string lastSomeId = "";
if (!isRecordBad)
{
foreach (var item in dataFile)
{
// check if its same record
if (lastSomeId != item.SomeID)
{
MyClass someClass = new MyClass();
lastSomeId = item.SomeID;
//decimal? LI = 0;//was going to use these as vars for calculations not sure I need them???
//decimal? DSC = 0;
//decimal? FU = 0;
someClass.Id = lastSomeId;
someClass.OrdNum = item.OrderNumber;
if (item.Code == "LI")
{
someClass.LICode = item.Code;
someClass.LICodeValue = item.NetCharge;
}
if (item.Code == "DT")
{
someClass.DTCode = item.Code;
someClass.DTCodeValue = item.NetCharge
}
if (item.Code == "FU")
{
someClass.FUCode = item.Code;
someClass.FUCodeValue = item.NetCharge;
}
someClass.Total = (someClass.LICodeValue + someClass.FUCodeValue);
//check for other values to calculate
//insert record to DB
}
else
{
//Insert into db after maipulation of values
}
}
}
isRecordBad = false;
}//END Using
Any clues would be greatly appreciated. Thank you in advance.

How to add distinct value in database using Entity Framework

IEnumerable<WebsiteWebPage> data = GetWebPages();
foreach (var value in data)
{
if (value.WebPage.Contains(".htm"))
{
WebsiteWebPage pagesinfo = new WebsiteWebPage();
pagesinfo.WebPage = value.WebPage;
pagesinfo.WebsiteId = websiteid;
db.WebsiteWebPages.Add(pagesinfo);
}
}
db.SaveChanges();
I want to add only distinct values to database in above code. Kindly help me how to do it as I am not able to find any solution.
IEnumerable<WebsiteWebPage> data = GetWebPages();
foreach (var value in data)
{
if (value.WebPage.Contains(".htm"))
{
var a = db.WebsiteWebPages.Where(i => i.WebPage == value.WebPage.ToString()).ToList();
if (a.Count == 0)
{
WebsiteWebPage pagesinfo = new WebsiteWebPage();
pagesinfo.WebPage = value.WebPage;
pagesinfo.WebsiteId = websiteid;
db.WebsiteWebPages.Add(pagesinfo);
db.SaveChanges();
}
}
}
This is the code that I used to add distinct data.I hope it helps
In addition to the code sample Furkan Öztürk supplied, Make sure your DB has a constraint so that you cannot enter duplicate values in the column. Belt and braces approach.
I assume that by "distinct values" you mean "distinct value.WebPage values":
// get existing values (if you ever need this)
var existingWebPages = db.WebsiteWebPages.Select(v => v.WebPage);
// get your pages
var webPages = GetWebPages().Where(v => v.WebPage.Contains(".htm"));
// get distinct WebPage values except existing ones
var distinctWebPages = webPages.Select(v => v.WebPage).Distinct().Except(existingWebPages);
// create WebsiteWebPage objects
var websiteWebPages = distinctWebPages.Select(v =>
new WebsiteWebPage { WebPage = v, WebsiteId = websiteid});
// save all at once
db.WebsiteWebPages.AddRange(websiteWebPages);
db.SaveChanges();
Assuming that you need them to be unique by WebPage and WebSiteId
IEnumerable<WebsiteWebPage> data = GetWebPages();
foreach (var value in data)
{
if (value.WebPage.Contains(".htm"))
{
WebsiteWebPage pagesinfo = new WebsiteWebPage();
if (db.WebsiteWebPages.All(c=>c.WebPage != value.WebPage|| c.WebsiteId != websiteid))
{
pagesinfo.WebPage = value.WebPage;
pagesinfo.WebsiteId = websiteid;
db.WebsiteWebPages.Add(pagesinfo);
}
}
}
db.SaveChanges();
UPDATE
To optimize this (given that your table contains much more data than your current list), override your equals in WebsiteWebPage class to define your uniqueness criteria then:
var myWebsiteWebPages = data.select(x=> new WebsiteWebPage { WebPage = x.WebPage, WebsiteId = websiteid}).Distinct();
var duplicates = db.WebsiteWebPages.Where(x=> myWebsiteWebPage.Contains(x));
db.WebsiteWebPages.AddRange(myWebsiteWebPages.Where(x=> !duplicates.Contains(x)));
this is a one database query to retrieve ONLY duplicates and then removing them from the list
You can use the following code,
IEnumerable<WebsiteWebPage> data = GetWebPages();
var templist = new List<WebsiteWebPage>();
foreach (var value in data)
{
if (value.WebPage.Contains(".htm"))
{
WebsiteWebPage pagesinfo = new WebsiteWebPage();
pagesinfo.WebPage = value.WebPage;
pagesinfo.WebsiteId = websiteid;
templist.Add(pagesinfo);
}
}
var distinctList = templist.GroupBy(x => x.WebsiteId).Select(group => group.First()).ToList();
db.WebsiteWebPages.AddRange(distinctList);
db.SaveChanges();
Or you can use MoreLINQ here to filter distinct the list by parameter like,
var res = tempList.Distinct(x=>x.WebsiteId).ToList();
db.WebsiteWebPages.AddRange(res);
db.SaveChanges();

How to avoid repeating when I get data from stored procedure?

I get a list from stored procedure with its child date I want to avoid repeating when I add items to the the list
Here is my code:
var cmd = db.Database.Connection.CreateCommand();
cmd.CommandText = "[dbo].[T]";
db.Database.Connection.Open();
var reader1 = cmd.ExecuteReader();
var objectContext1 = ((IObjectContextAdapter)db).ObjectContext;
var usersLi = objectContext1.Translate<GetAllNewsForUser>(reader1).ToList();
List<GetAllNewsForUser> lll = new List<GetAllNewsForUser>();
foreach (var item in usersLi)
{
NewsInfo newsInfo = new NewsInfo();
GetAllNewsForUser g = new GetAllNewsForUser();
g.UserName = item.UserName;
NewsInfo nnn = new NewsInfo();
nnn.NewsTitle = item.NewsTitle;
nnn.NewsId = item.NewsId;
g.NewsInfos.Add(nnn);
lll.Add(g);
}
are there any unique identifiers for g, for example a user Id. in which case you could easily use the following:
if (!lll.Any(x=>x.UserId ==g.UserId)) //include system.linq
{
lll.Add(g);
}
You could also look at modifying your stored procedure to not return duplicates.
EDIT ----
Just twigged as to what you actually after, sorry.....
Its just a rough draft but i think this is what you require.
List<GetAllNewsForUser> lll = new List<GetAllNewsForUser>();
foreach (var item in usersLi)
{
NewsInfo nnn = new NewsInfo();
nnn.NewsTitle = item.NewsTitle;
nnn.NewsId = item.NewsId;
if (!lll.Any(x=> x.UserName == item.UserName)
{
GetAllNewsForUser g = new GetAllNewsForUser();
g.UserName = item.UserName;
g.NewsInfos.Add(nnn);
lll.Add(g);
}
else
{
lll.Where(x=>x.UserName == item.Username).FirstOrDefault().NewsInfos.Add(nnn)
}
}
From what I understand so far, you are trying to avoid adding same rows in the list(based on value of some column).
LINQ might help here.
usersLi.GroupBy(e => new {
UserID = e.UserUD
}).Select(g => g.First());

Trying to find the quickest (least costly) way to loop values in from a .csv through two tables

The application I am building allows a user to upload a .csv file, which will ultimately fill in fields of an existing SQL table where the Ids match. First, I am using LinqToCsv and a foreach loop to import the .csv into a temporary table. Then I have another foreach loop that loops the fields from the temporary table into an existing table where the Ids match. The only way I have gotten this to work consistently and successfully is nesting the second foreach loop within the first:
[HttpPost]
public ActionResult UploadValidationTable(HttpPostedFileBase csvFile)
{
var inputFileDescription = new CsvFileDescription
{
SeparatorChar = ',',
FirstLineHasColumnNames = true
};
var cc = new CsvContext();
var filePath = uploadFile(csvFile.InputStream);
var model = cc.Read<Credit>(filePath, inputFileDescription);
try
{
var entity = new TestEntities();
foreach (var item in model)
{
var tc = new TemporaryCsvUpload
{
Id = item.Id,
CreditInvoiceAmount = item.CreditInvoiceAmount,
CreditInvoiceDate = item.CreditInvoiceDate,
CreditInvoiceNumber = item.CreditInvoiceNumber,
CreditDeniedDate = item.CreditDeniedDate,
CreditDeniedReasonId = item.CreditDeniedReasonId,
CreditDeniedNotes = item.CreditDeniedNotes
};
entity.TemporaryCsvUploads.Add(tc);
var idMatches = entity.Authorizations.ToList().Where(x => x.Id == tc.Id);
foreach (var number in idMatches)
{
number.CreditInvoiceDate = tc.CreditInvoiceDate;
number.CreditInvoiceNumber = tc.CreditInvoiceNumber;
number.CreditInvoiceAmount = tc.CreditInvoiceAmount;
number.CreditDeniedDate = tc.CreditDeniedDate;
number.CreditDeniedReasonId = tc.CreditDeniedReasonId;
number.CreditDeniedNotes = tc.CreditDeniedNotes;
}
}
entity.SaveChanges();
entity.Database.ExecuteSqlCommand("TRUNCATE TABLE TemporaryCsvUpload");
TempData["Success"] = "Updated Successfully";
}
catch (LINQtoCSVException)
{
TempData["Error"] = "Upload Error: Ensure you have the correct header fields and that the file is of .csv format.";
}
return View("Upload");
}
The issue is speed. It takes about 1 minute and 49 seconds to search through an SQL table of 7000 entries, match the ids, and fill in the fields.
So, I looked at this and thought that the second loop really didn't need to be nested. I switched up the code like so:
[HttpPost]
public ActionResult UploadValidationTable(HttpPostedFileBase csvFile)
{
var inputFileDescription = new CsvFileDescription
{
SeparatorChar = ',',
FirstLineHasColumnNames = true
};
var cc = new CsvContext();
var filePath = uploadFile(csvFile.InputStream);
var model = cc.Read<Credit>(filePath, inputFileDescription);
try
{
var entity = new TestEntities();
var tc = new TemporaryCsvUpload();
foreach (var item in model)
{
tc.Id = item.Id;
tc.CreditInvoiceAmount = item.CreditInvoiceAmount;
tc.CreditInvoiceDate = item.CreditInvoiceDate;
tc.CreditInvoiceNumber = item.CreditInvoiceNumber;
tc.CreditDeniedDate = item.CreditDeniedDate;
tc.CreditDeniedReasonId = item.CreditDeniedReasonId;
tc.CreditDeniedNotes = item.CreditDeniedNotes;
entity.TemporaryCsvUploads.Add(tc);
}
var idMatches = entity.Authorizations.ToList().Where(x => x.Id == tc.Id);
foreach (var number in idMatches)
{
number.CreditInvoiceDate = tc.CreditInvoiceDate;
number.CreditInvoiceNumber = tc.CreditInvoiceNumber;
number.CreditInvoiceAmount = tc.CreditInvoiceAmount;
number.CreditDeniedDate = tc.CreditDeniedDate;
number.CreditDeniedReasonId = tc.CreditDeniedReasonId;
number.CreditDeniedNotes = tc.CreditDeniedNotes;
}
entity.SaveChanges();
entity.Database.ExecuteSqlCommand("TRUNCATE TABLE TemporaryCsvUpload");
TempData["Success"] = "Updated Successfully";
}
catch (LINQtoCSVException)
{
TempData["Error"] = "Upload Error: Ensure you have the correct header fields and that the file is of .csv format.";
}
return View("Upload");
}
This time around, it only took 19 seconds to complete. A vast improvement on the first. But when I checked the database, only one row of the 7 that should match was filled in. Can anybody spot a reason why the second code block would not be filling in all the rows it should be? Or a better way to optimize the first block? Thanks!

Categories