Reading, modifying and writing a CSV with CSVHelper (variable scope with 'using' ) - c#

This is a general coding question of how I can share the same list of data between the stages of reading, modifying and writing.
I'm a novice and new to C# too, so I'm struggling. I was expecting to be able to: read the CSV into the variable records, modify one or more of the records and then write out the modified data as a new CSV, but there is a scope issue with the using function.
The code below won't compile because records is out of scope in both the foreach loop that aims to modify some data and again when I'm trying to write the modified file.
I have tried various things to make records a more global variable but they have all failed and I am out of my depth.
I'm not even sure that this is the best way to approach the problem, so any advice would be appreciated.
private void Btn_Merge_Click(object sender, EventArgs e)
{
// Read the CSV into 'records'
StreamReader reader = new StreamReader(textBox_Shopify.Text);
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
var records = csv.GetRecords<Contact>();
}
// We now need to find the record for a specific person and change it
foreach (Contact customer in records)
{
if (customer.Email == "john.dow#business.com") // Hard-coded while testing
{
string Tags = customer.Tags; // Get the current tags
// If the Lead Marking tag is not already there, add it
if (!Tags.Contains("Send me Lead Marketing"))
{
// If there are tags already there, append a semi-colon separator
if (customer.Tags.Length > 0)
{
customer.Tags += ";";
}
customer.Tags += "Send me Lead Marketing";
MessageBox.Show(customer.Email + " Tags: " + customer.Tags); //Just while I'm testing
}
}
// If the customer is not in the list, add them as a new record
// To do...
}
// We can now write out the modified file
using (var writer = new StreamWriter(#"C:\temp\Output.csv"))
using (var outputCSV = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
outputCSV.WriteRecords(records);
}
}
public class Contact
{
[Name("First Name")] // This 'attribute' allows the class property First_Name to be matched to the header "First Name"
public string First_Name { get; set; }
[Name("Last Name")]
public string Last_Name { get; set; }
public string Email { get; set; }
[Name("Accepts Email Marketing")]
public string Accepts_Email_Marketing { get; set; }
public string Company { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string Province { get; set; }
[Name("Province Code")]
public string Province_Code { get; set; }
public string Country { get; set; }
[Name("Country Code")]
public string Country_Code { get; set; }
public string Zip { get; set; }
public string Phone { get; set; }
[Name("Accepts SMS Marketing")]
public string Accepts_SMS_Marketing { get; set; }
[Name("Total Spent")]
public string Total_Spent { get; set; }
[Name("Total Orders")]
public string Total_Orders { get; set; }
public string Tags { get; set; }
public string Note { get; set; }
[Name("Tax Exempt")]
public string Tax_Exempt { get; set; }
}

#Mureinik is halfway there. Since CsvHelper will only yield records as you iterate them, you will also need to call ToList() or in some other way iterate the records within the using statement.
IEnumerable<Contact> records;
// Read the CSV into 'records'
StreamReader reader = new StreamReader(textBox_Shopify.Text);
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
records = csv.GetRecords<Contact>().ToList();
}

You could define records in the scope of the entire method:
private void Btn_Merge_Click(object sender, EventArgs e)
{
IEnumerable<Contact> records;
// result of the code...

Related

Add class properties from the List and put them into the string together with a comma as a delimiter

Although the thing I want to do seems be really trivial I can not find a way to achieve what I want. I know there exist multiple questions how to put class properties into the list together and separate it by a comma like that on SO, but none of them seemed to be relevant to my case.
I have a class Form defined as follows:
public class Form
{
public string CustomerName { get; set; }
public string CustomerAdress { get; set; }
public string CustomerNumber { get; set; }
public string OfficeAdress { get; set; }
public string Date { get; set; }
public Boolean FunctionalTest { get; set; }
public string Signature { get; set; }
public Form()
{
}
}
In the MainPage.xaml.cs, I create a List<Form> with the Form class properties and subsequently I would like to create a string with all of those class properties separated by a comma. For that case I use basic Join method with Select which converts any kinds of objects to string.
I do that by createCSV method inside MainPage.xaml.cs :
void createCSV()
{
var records = new List<Form>
{
new Form {CustomerName = customerName.Text,
CustomerAdress = customerAdress.Text,
CustomerNumber = customerNumber.Text,
OfficeAdress = officeAdress.Text,
Date = date.Date.ToString("MM/dd/yyyy"),
FunctionalTest = testPicker.ToString()=="YES" ? true : false,
Signature = signature.Text
}
};
string results = String.Join(",", (object)records.Select(o => o.ToString()));
}
The problem is instead of the desirable outcome which is:"Mark Brown,123 High Level Street,01578454521,43 Falmouth Road,12/15/2020,false,Brown"
I am getting: "System.Linq.Enumerable+SelectListIterator'2[MyApp.Form,System.String]"
PS. As you have noticed I am newbie in C#. Instead of non constructive criticism of the code, please for a valuable reply which would help me to understand what am I doing wrong.
Thanks in advance
In the Form class, You can override the ToString() method and use System.Reflection to get your comma string.
Form.cs
public class Form
{
public string CustomerName { get; set; }
public string CustomerAdress { get; set; }
public string CustomerNumber { get; set; }
public string OfficeAdress { get; set; }
public string Date { get; set; }
public bool FunctionalTest { get; set; }
public string Signature { get; set; }
public override string ToString()
{
string modelString = string.Empty;
PropertyInfo[] properties = typeof(Form).GetProperties();
foreach (PropertyInfo property in properties)
{
var value = property.GetValue(this); // you should add a null check here before doing value.ToString as it will break on null
modelString += value.ToString() + ",";
}
return modelString;
}
}
Code
List<string> CSVDataList = new List<string>();
List<Form> FormList = new List<Form>();
...
foreach (var data in FormList)
{
CSVDataList.Add(data.ToString());
}
Now you have a list of string CSVDataList with each Form object's data in comma style
P.S.
for DateTime
var value = property.GetValue(this);
if(value is DateTime date)
{
modelString += date.ToString("dd.MM.yyyy") + ",";
}

C# with MySQL Add Data to Binding Source stop at record #8

I always failed to load DataGridView customers. They always freeze on record #8
I have a Customers class under my apps namespace like this
public class Customers
{
public string No { get; set; }
public string ID { get; set; }
public string NoSPU { get; set; }
public string Name { get; set; }
public string Telp { get; set; }
public string Kavling { get; set; }
public string Tipe { get; set; }
public string Pokok { get; set; }
public string Bunga { get; set; }
}
And this is my code to add item to customersBindingSource in my DataGridView and I put in formLoad event
string query = "select * from customer";
customersBindingSource.Clear();
Int32 i = 0;
MySqlDataReader reader = dx.findQuery(query);
while (reader.Read())
{
i++;
customersBindingSource.Add(new Customers() {
No = i.ToString(),
ID = reader.GetString("id"),
NoSPU = reader.GetString("nospu"),
Name = reader.GetString("nama"),
Telp = reader.GetString("telp"),
Kavling = reader.GetString("kavling"),
Tipe = reader.GetString("tipe")
});
MessageBox.Show(i.ToString()+" OKE");
}
reader.Close();
If I'm trying with data less than 8 on customers table it always work, but when I add new customers more than 8 it's always freeze, customers forms doesn't show up. Are there's a limitation or something wrong with my code?
try like this way:
// Create and populate the list of DemoCustomer objects
// which will supply data to the DataGridView.
List<DemoCustomer> customerList = new List<DemoCustomer>();
customerList.Add(DemoCustomer.CreateNewCustomer());
customerList.Add(DemoCustomer.CreateNewCustomer());
customerList.Add(DemoCustomer.CreateNewCustomer());
// Bind the list to the BindingSource.
this.customersBindingSource.DataSource = customerList;
hope it can help you.

Error during update data on mongo db

I have this error during save on update data in my mongo db.
this is erorr:
Save can only be used with documents that have an Id.
this is my model:
public ModelKorisici () { }
[BsonId(IdGenerator = typeof(CombGuidGenerator))] // posajvljivaa se greška kod BSON tipa podataka kod ID-a,preuzoteo s dokumentacije drivera 1.5
public Guid Id { get; set; }
[BsonElement("ime")]
public string ime { get; set; }
[BsonElement("prezime")]
public string prezime { get; set; }
[BsonElement("lozinka")]
public string lozinka { get; set; }
[BsonElement("email")]
public string email { get; set; }
[BsonElement("kor_ime")]
public string kor_ime { get; set; }
[BsonElement("uloga")]
public string uloga { get; set; }
}
My code for update is here:
public void urediKorisnika(ModelKorisici korisnik)
{
MongoCollection<ModelKorisici> kljenti = GetTasksCollection();
kljenti.Save(kljenti);
}
Can anyone help?
I'm not sure what are you trying to do but your error is in following code block
public void urediKorisnika(ModelKorisici korisnik)
{
MongoCollection<ModelKorisici> kljenti = GetTasksCollection();
kljenti.Save(kljenti);
}
Here are you trying to store collection to itself. I believe GetTasksCollection() return collection from MongoDB and then you are trying to store it into itself again. see kljenti.Save(kljenti);
You need to create instance of ModelKorisici and save it into kljenti
Your code should look something like:
public void urediKorisnika(ModelKorisici korisnik)
{
MongoCollection<ModelKorisici> kljenti = GetTasksCollection();
var model = new ModelKorisici();
model.ime = ...
...
kljenti.Save(model);
}

Parse XML with Linq with multiple child elements

This is my first question on SO, please let me know if I am doing anything wrong!
I am trying to parse an XML similar to this:
<LiveUpdate>
<CityID>F0A21EA2</CityID>
<CityName>CityTown</CityName>
<UserName>john</UserName>
<ApplicationDetails>
<ApplicationDetail
Application="AC"
Licensed="true"
Version="2015.2"
Patch="0001"
/>
<ApplicationDetail
Application="AP"
Licensed="true"
Version="2015.2"
Patch="0002"
/>
</ApplicationDetails>
</LiveUpdate>
I have classes that look like this:
public class Client
{
public string cityID { get; set; }
public string cityName { get; set; }
public string userName { get; set; }
public List<Apps> appList { get; set; }
}
public class Apps
{
public string app { get; set; }
public string licensed { get; set; }
public string version { get; set; }
public string patch { get; set; }
}
I need to be able to have a client class with a list of all the application details to be iterated over.
So far the best I've come up with is:
XDocument xml = XDocument.Load(#"C:\blah\Desktop\1.xml");
var liveUpdate = xml.Root;
var clients = (from e in liveUpdate.Elements()
select new Client()
{
cityID = e.Element("CityID").Value,
cityName = e.Element("CityName").Value,
userName = e.Element("UserName").Value,
appList = e.Elements("ApplicationDetails")
.Select(a => new Apps()
{
app = a.Element("Application").Value,
licensed = a.Element("Licensed").Value,
version = a.Element("Version").Value,
patch = a.Element("Patch").Value
}).ToList()
});
However, I'm currently running into an error that says Object reference not set to an instance of an object.
I've seen some similar examples on here, but not that deal with data before the multiple children.
I'm fairly new to XML and Linq so any help here would be greatly appreciated!
Your XML only contains one LiveUpdate tag, so rather than iterating over all of the elements inside of it, you just want to look at the Root element.
In ApplicationDetails, Application, Licensed and such are attributes, not elements. Use .Attribute() to access them.
ApplicationDetails is a single tag, and inside it you have ApplicationDetail tags.
There is no DateTime element in your LiveUpdate tag.
This works:
var liveUpdate = xml.Root;
var e = liveUpdate;
var clients = new Client()
{
cityID = e.Element("CityID").Value,
cityName = e.Element("CityName").Value,
userName = e.Element("UserName").Value,
//dateTime = e.Element("DateTime").Value,
appList = e.Element("ApplicationDetails").Elements("ApplicationDetail")
.Select(a => new Apps()
{
app = a.Attribute("Application").Value,
licensed = a.Attribute("Licensed").Value,
version = a.Attribute("Version").Value,
patch = a.Attribute("Patch").Value
}).ToList()
};
Since you have already defined a class into which you wish to deserialize, you can use XmlSerializer to deserialize it for you.
First, let's rename some of your property names to more closely match the XML and c# naming conventions:
[XmlRoot("LiveUpdate")]
public class Client
{
public string CityID { get; set; }
public string CityName { get; set; }
public string UserName { get; set; }
[XmlArray("ApplicationDetails")]
[XmlArrayItem("ApplicationDetail")]
public List<Apps> AppList { get; set; }
}
public class Apps
{
[XmlAttribute]
public string Application { get; set; }
[XmlAttribute]
public bool Licensed { get; set; }
[XmlAttribute]
public string Version { get; set; }
[XmlAttribute]
public string Patch { get; set; }
}
Then add the following extension methods:
public static class XmlSerializationHelper
{
public static T LoadFromXML<T>(this string xmlString)
{
using (StringReader reader = new StringReader(xmlString))
{
object result = new XmlSerializer(typeof(T)).Deserialize(reader);
if (result is T)
{
return (T)result;
}
}
return default(T);
}
public static T LoadFromFile<T>(string filename)
{
using (var fs = new FileStream(filename, FileMode.Open))
{
object result = new XmlSerializer(typeof(T)).Deserialize(fs);
if (result is T)
{
return (T)result;
}
}
return default(T);
}
}
Now you can deserialize from your XML file as follows:
string fileName = #"C:\blah\Desktop\1.xml";
var client = XmlSerializationHelper.LoadFromFile<Client>(fileName);
I manually updated your Client class to map correctly to the provided XML, but if you wanted to do it automatically, see here: Generate C# class from XML.

How to insert data from MS Access to SQL after checking whether the data exists or not in the database using Entity Framework

Hopefully, the question header is clear enough to tell that I'm trying to read an Access file and upload the data to the database but checking at first whether the data already exists or not in the database.
I receive a daily report from a third-party company in Access file. I'm trying to create a windows service that will check for the file every morning, and if the new file exist, then it'll read and upload the data to the database. I'm trying to use Entity Framework. I read the article on Navigation Property, but I'm still confused on that; I never used navigation property before. Here are my models:
[Table("ClaimsTable")]
public partial class ClaimsTable
{
[Key]
[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
public int ClaimsID { get; set; }
public string EOPAID { get; set; }
public string AuthID { get; set; }
public string PAStatus { get; set; }
public string UserName { get; set; }
[DataType(DataType.Date)]
public DateTime EffectiveDate { get; set; }
[DataType(DataType.Date)]
public DateTime EndDate { get; set; }
public string RecordType { get; set; }
public int RxID { get; set; }
public int MemberID { get; set; }
public int PrescriberID { get; set; }
public string EditNumber { get; set; }
public string OriginSource { get; set; }
public string OriginMethod { get; set; }
/*
[ForeignKey("RxID")]
public virtual RxTable Prescription { get; set; }
[ForeignKey("MemberID")]
public virtual MembersTable Member { get; set; }
[ForeignKey("PrescriberID")]
public virtual PrescribersTable Prescriber { get; set; }
*/
}
...
[Table("MembersTable")]
public partial class MembersTable
{
[Key]
[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
public int MemberID { get; set; }
[DataType(DataType.Date), Display(Name= "Date of Birth"), DisplayFormat(DataFormatString="{0:mm/dd/yyyy}", ApplyFormatInEditMode=true)]
public DateTime DateofBirth { get; set; }
public string CardholderID { get; set; }
public string MemberFirstName { get; set; }
public string MemberLastName { get; set; }
//public virtual ICollection<AddressTable> Address { get; set; }
}
...
[Table("PrescribersTable")]
public partial class PrescribersTable
{
[Key]
[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
public int PrescriberID { get; set; }
public string NPI { get; set; }
public string PrescriberFirstName { get; set; }
public string PrescriberLastName { get; set; }
public string PhysicianType { get; set; }
//public ICollection<AddressTable> Address { get; set; }
}
....
using(OleDbConnection conn = new OleDbConnection(strDSN))
{
OleDbDataReader reader = null;
OleDbCommand command = new OleDbCommand("Select * from table", conn);
try
{
conn.Open();
}
catch(OleDbException o)
{
return o.Message;
}
reader = command.ExecuteReader();
List<ClaimsTable> Claim = new List<ClaimsTable>();
List<PrescribersTable> PrescriberInDB = new List<PrescribersTable>();
List<MembersTable> MembersInDB = new List<MembersTable>();
while(reader.Read())
{
PrescriberInDB = context.Prescribers.ToList();
MembersInDB = context.Members.ToList();
//CREATE LOCAL VARIABLE
string recordType = //check if the member and the prescriber exist in the database
int prescriberID = 0;
int prodID = 0;
int memberID = 0;
int drugID = 0;
int RxID = 0;
int claimID = 0;
//check if the member and the prescriber exist in the object before inserted into the database.
//the data will be uploaded to the database in bulk
//int newPrescriberID = Prescriber.Where(x => x.PrescriberFirstName == reader["Prescriber First Name"] && x.PrescriberLastName == reader["Prescriber Last Name"] && x.NPI == reader["Prescribing Physician"]).Select(x => x.PrescriberID).FirstOrDefault();
//int newMemberID = Member.Where(x => x.MemberFirstName == reader["Member First Name"] && x.MemberLastName == reader["Member Last Name"] && x.CardholderID == reader["CardhHolder"]).Select(x => x.MemberID).FirstOrDefault();
//insert the data if it doesn't exist
if(!PresciberExist(prescriberFirstName, prescriberLastName, npi, PrescriberInDB))
{
var prescriber = new PrescribersTable()
{
PrescriberFirstName = prescriberFirstName,
PrescriberLastName = prescriberLastName,
NPI = npi,
PhysicianType = physicianType
};
context.Prescribers.Add(prescriber);
context.SaveChanges();
prescriberID = GetPrescriberID(prescriberFirstName, prescriberLastName, physicianType, PrescriberInDB);
}
if(!MemberExist(memberFirstName, memberLastName, cardholderID, MembersInDB))
{
var member = new MembersTable()
{
MemberFirstName = memberFirstName,
MemberLastName = memberLastName,
CardholderID = cardholderID,
DateofBirth = dob
};
context.Members.Add(member);
context.SaveChanges();
memberID = GetMemberID(memberFirstName, memberLastName, cardholderID, MembersInDB);
}
}
}
return "Done uploading";
}
private bool MemberExist(string memberFirstName, string memberLastName, string cardholderID, List<MembersTable> MembersInDB)
{
return MembersInDB.Exists(x => x.MemberFirstName == memberFirstName && x.MemberLastName == memberLastName && x.CardholderID == cardholderID);
}
private bool PresciberExist(string prescriberFirstName, string prescriberLastName, string npi, List<PrescribersTable> PrescriberInDB)
{
return PrescriberInDB.Exists(x => x.PrescriberFirstName == prescriberFirstName && x.PrescriberLastName == prescriberLastName && x.NPI == npi);
}
The access database contains sensitive information, so I won't be able to add those data as an example. But here's a made up data for test. The data contains claims of patients.
Now, because there are many drugs and many claims for the same patient, and many patients for a prescriber.. I broke the database as it's shown above. Needs improvement? I welcome suggestion. The reason I did this is because I don't want my database to have repeated records which will make managing really troubling. This way, I'll have unique members in memberstable, unique prescribers in prescriberstable and so on and so forth.
The challenge I'm facing is that when I read the data from the access database, I'm assuming it reads row-wise. The code should first check the database whether the member exist or not. If it does, then get the member id which is an identity column. If it doesn't, then it should insert the member's info only, and then get the memberID. Similarly, I do the same thing with the prescriber's data. Check and insert if needed. This is the long way, and this is the only way I could figure out how to do it.
I know this is not a very good programming. I'm just an analyst who unfortunately has to do a lot of programming. And I'm learning as I go. With that said, there's a lot of ways to improve this code - I just don't know any. Can you point me to the right direction? Also, an example of how to check and insert the data if it doesn't exist in the database using navigation property. Currently, the data is read and uploaded just fine, but I saw in the database that it didn't quite do what I wanted it to do. It still added a couple of already existing members. I seriously needs some help.

Categories

Resources