Trouble Exporting WPF ListView Data to CSV with Filehelpers - c#

Have hit a wall with this so hopefully SO can be of help and I've not overlooked an obvious question previously answered. I'm trying export data from a ListView (actually SQLite data that's populating it via a list) to a new CSV file - no fancy filepicker as yet, just need to save the file locally (it's a Metro 8.1 App but being deployed to Surface 3, not RT). I've created a method based on examples I've found but it doesn't seem to be writing the file (have searched local machine after attempting export but nothing found). It's compiling fine and I'm not hitting any exceptions when debugging, also I'm using Filehelpers 2.0 as I couldn't get the current version to install (VS 2015 Community). 'Candidate' is the class for the datasource (DB/listview).
Class:
using SQLite;
using FileHelpers;
namespace SolutionName.Model
{
[Table("Candidates")]
[DelimitedRecord(",")]
[IgnoreEmptyLines()]
[IgnoreFirst()]
public class Candidate
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Title { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string AreasInterest { get; set; }
} // end class Candidate
} // end namespace
Method (called by a button):
private void WriteCSVFile(List<Candidate> dataSource)
{
//filehelper object
FileHelperEngine engine = new FileHelperEngine(typeof(Candidate));
List<Candidate> csv = new List<Candidate>();
//convert any datasource to csv based object
foreach (var item in dataSource)
{
Candidate temp = new Candidate();
temp.Title = item.Title;
temp.FirstName = item.FirstName;
temp.LastName = item.LastName;
temp.Email = item.Email;
temp.Phone = item.Phone;
temp.AreasInterest = item.AreasInterest;
csv.Add(temp);
} // end foreach
//give file a name and header text
engine.HeaderText = "Title,FirstName,LastName,Email,Phone,AreaInterest";
//save file locally
engine.WriteFile("export.csv", csv);
} // end method WriteCSVFile
Any pointers would be appreciated.

Testing: Passed
Version 3.2: No issues
Version 2.2: No issues
Using either version of FileHelpers this works as expected. I threw the following code into a test console and it ran through perfectly so my only suggestion now is that you are either not passing it data, or attempting to write to either a read-only or invalid location.
Do you see any exceptions in the Output tab of Visual Studio?
Have you confirmed you have data going into the dataSource parameter?
Have you confirmed the full path that you are writing the export.csv to?
Do you have the csv file open in Excel?
Note: that having the CSV open in Excel causes a full lock on the CSV file so you must exit Excel or close the file to be able to write to it
Code:
static void TestMain2(string[] args)
{
List<Candidate> source = new List<Candidate>()
{
new Candidate() { Id = 1, Email = "test1#test.com", Title = "Mr", FirstName = "Fred", LastName = "Flintstone", AreasInterest = "Area1", Phone = "+44 1234 123123" },
new Candidate() { Id = 3, Email = "test2#test.com", Title = "Mr", FirstName = "Barney", LastName = "Rubble", AreasInterest = "Area2", Phone = "+44 1234 231231" },
new Candidate() { Id = 2, Email = "test3#test.com", Title = "Mrs", FirstName = "Wilma", LastName = "Flintstone", AreasInterest = "Area3", Phone = "+44 1234 312312" }
};
WriteCSVFile(source);
}
private static void WriteCSVFile(List<Candidate> dataSource)
{
//filehelper object
FileHelperEngine engine = new FileHelperEngine(typeof(Candidate));
List<Candidate> csv = new List<Candidate>();
//convert any datasource to csv based object
foreach (var item in dataSource)
{
Candidate temp = new Candidate();
temp.Title = item.Title;
temp.FirstName = item.FirstName;
temp.LastName = item.LastName;
temp.Email = item.Email;
temp.Phone = item.Phone;
temp.AreasInterest = item.AreasInterest;
csv.Add(temp);
} // end foreach
//give file a name and header text
engine.HeaderText = "Title,FirstName,LastName,Email,Phone,AreaInterest";
//save file locally
engine.WriteFile("export.csv", csv);
} // end method WriteCSVFile
CSV File
Title,FirstName,LastName,Email,Phone,AreaInterest
0,Mr,Fred,Flintstone,test1#test.com,+44 1234 123123,Area1
0,Mr,Barney,Rubble,test2#test.com,+44 1234 231231,Area2
0,Mrs,Wilma,Flintstone,test3#test.com,+44 1234 312312,Area3
Notes:
The ID column wasn't copied over so this was always zero, but that may just have been because of your sample code.
I believe it's recommended to be using the generic FileHelperEngine rather than typeof() parameter on the base class since this initialises various methods/properties to utilise T rather than just a generic object.
You can try downloading the source to FileHelpers and linking your project directly to the library to debug what's going on internally.
You did previously mention that you have a System.*.dll referencing problem, check that you are using the Full Framework and not a Client one as that may cause that issue. I am not sure whether a W8 universal app allows that though.

Related

I'm trying to bulk add a csv file to my local DB

I'm trying to read in the contents of a csv file into my DBcontext object.
Here's the declaration:
public DbSet<Wine> Wines { get; set; }
All the relevent, configurations were set. But this code just doesn't seem to be working. And I', not getting any errors either.
Wines.AddRange(from wine in File.ReadLines("C:\\Users\\cochiagha\\source\\repos\\C_sharp_Projects\\wine.csv") // Read a row
let wineVar = wine.Split(',')
select new Wine
{
Name = wineVar[0],
AlcoholContentIndex = float.Parse(wineVar[1]),
MalicAcidConentration = float.Parse(wineVar[2]),
AshIndex = float.Parse(wineVar[3]),
AlcalinityIndexofAsh = float.Parse(wineVar[4]),
MagnesiumReading = float.Parse(wineVar[5]),
TotalPhenolIndex = float.Parse(wineVar[6]),
FlavanoidIndex = float.Parse(wineVar[7]),
NonFlavanoidPhenolIndex = float.Parse(wineVar[8]),
ProanthocyaninsIndex = float.Parse(wineVar[9]),
ColorIntensityIndex = float.Parse(wineVar[10]),
HueIndex = float.Parse(wineVar[11]),
DilutionIndex = float.Parse(wineVar[12]),
ProlineIndex = float.Parse(wineVar[13]),
});
I instantiated an instance of the db context in my Controller file and called the function under a method I qualified as a 'post'. And my changes were saved after.
// bulk read
[HttpGet("[action]")]
public void BulkRead()
{
_WineApiDBContext.readCSV();
_WineApiDBContext.SaveChanges();
}
If you need more clarification, I'd be glad to provide that too.

LiteDB Collection returned but query returns null

I've done a fair bit of research, but I can't find anyone who has the same problem as me (sadly). I am using LiteDB to create a NoSQL database.
When the program first runs and the database is created, the query in the example below works just fine. When I restart the program, it fails saying that it is null. The weird thing is, if I do a count it returns 8 records. So something exists - why can't I pull it out?
Here is my code:
public class ExternalTools
{
public int Id { get; set; }
public string Name { get; set; }
public string[] Types { get; set; }
}
public void GetAll()
{
var localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
var folderPath = localFolder.Path;
var filePath = Path.Combine(folderPath, #"MyData4.db");
using (var db = new LiteDatabase(filePath))
{
Tools = db.GetCollection<ExternalTools>("externalTools");
if (Tools.Count() == 0)
{
CreateToolList();
// Index document using document Name property
Tools.EnsureIndex(x => x.Name);
}
}
Debug.WriteLine(Tools.Count());
var temp = Tools.FindAll(); // null error
var test = Tools.FindById(1); // another null error
Debug.WriteLine(test.Name); //
}
Thanks!
Well, I figured it out (so many hours of debugging wasted!) My code is in the wrong spot, if I move it into the using statement it works just fine. I suspect this has to do with the fact that on the first run it's adding stuff into the colection so it has the proper reference. Regardless, this code works:
using (var db = new LiteDatabase(filePath))
{
Tools = db.GetCollection<ExternalTools>("externalTools");
if (Tools.Count() == 0)
{
CreateToolList();
// Index document using document Name property
Tools.EnsureIndex(x => x.Name);
}
Debug.WriteLine(Tools.Count());
var temp = Tools.FindAll(); // null error
var test = Tools.FindById(1); // another null error
Debug.WriteLine(test.Name); //
}

C# Reading through CSV file to rename multiple files with new names

I would like to use a list of old and the corresponding new names in a CSV file(source of CSV is a Excel sheet), in order to rename files. Obviously replace the old name with the new name specified for each case.
For Example:
Find what Replace With
C:\Users\Documents\Pump Station.doc C:\Users\Documents\Awesome Pump Station.doc
C:\Users\Documents\Pump Selection.doc C:\Users\Documents\Great Pump Selection.doc
C:\Users\Documents\Pump Sizing Calc.xlsx C:\Users\Documents\Hectic Pump Sizing Calc.xlsx
I am very new to coding and I am having trouble finishing this off. This is what I have so far. I do not necessarily need to even put the list user interface (which it currently does). Ultimately I would like to loop through the rows in my CSV file, check if the old name specified exists and if so, rename it to the new name specified.
I really appreciate any help in advance and sorry for any rookie errors I may have made in my code below.
public class OldNew
{
public string oldFile { get; set; }
public string newFile { get; set; }
}
public static class OldNewService
{
public static new List<OldNew>ReadFile(string filepath)
{
var lines = File.ReadAllLines(filepath);
var data = from l in lines.Skip(1)
let split = l.Split(',')
select new OldNew
{
oldFile = split[0],
newFile = split[1],
};
return data.ToList();
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = OldNewService.ReadFile(#"C:\Users\cch\Documents\Batch Edit\Lookup Table.csv");
}
}
}
In my opinion, a better solution would be to use a plain old foreach and not a call to ToList().ForEach().
var lines = File.ReadAllLines(filepath);
var data = from l in lines.Skip(1)
let split = l.Split(',')
select new OldNew
{
oldFile = split[0],
newFile = split[1],
};
foreach(var f in data)
{
if (File.Exists(f.oldFile)
{
File.Move(f.oldFile, f.newFile);
}
}
See: http://blogs.msdn.com/b/ericlippert/archive/2009/05/18/foreach-vs-foreach.aspx for an explanation.
From what I understand, you want to get the new value of the file if an old one exists. To get the new value of the file from your list, try something like:
data.ForEach(d =>
{
if (!string.IsNullOrEmpty(d.oldFile))
{
File.Move(d.oldFile, d.newFile);
}
});
Wouldn't it make sense to rename the old filename if a new one exists?
Hope this helps.

RavenDB throws a JSON deserialisation error when retrieving document

I've just completed a round of refactoring of my application, which has resulted in my removing a project that was no longer required and moving its classes into a different project. A side effect of this is that my User class, which is stored in RavenDB, has a collection property of a type moved to the new assembly. As soon as I attempt to query the session for the User class I get a Json deserialisation error. The issue is touched upon here but the answers don't address my issue. Here's the offending property:
{
"OAuthAccounts": {
"$type": "System.Collections.ObjectModel.Collection`1[
[Friendorsement.Contracts.Membership.IOAuthAccount,
Friendorsement.Contracts]], mscorlib",
"$values": []
},
}
OAuthAccounts is a collection property of User that used to map here:
System.Collections.ObjectModel.Collection`1[[Friendorsement.Contracts.Membership.IOAuthAccount, Friendorsement.Contracts]]
It now maps here:
System.Collections.ObjectModel.Collection`1[[Friendorsement.Domain.Membership.IOAuthAccount, Friendorsement.Domain]]
Friendorsement.Contracts no longer exists. All of its types are now in Friendorsement.Domain
I've tried using store.DatabaseCommands.StartsWith("User", "", 0, 128) but that didn't return anything.
I've tried looking at UpdateByIndex but not got very far with it:
store.DatabaseCommands.UpdateByIndex("Raven/DocumentsByEntityName",
new IndexQuery {Query = "Tag:Users"},
new[]
{
new PatchRequest { // unsure what to set here }
});
I'm using Raven 2.0
Below is a simple sample application that shows you the patching Metadata. While your example is a little different this should be a good starting point
namespace SO19941925
{
internal class Program
{
private static void Main(string[] args)
{
IDocumentStore store = new DocumentStore
{
Url = "http://localhost:8080",
DefaultDatabase = "SO19941925"
}.Initialize();
using (IDocumentSession session = store.OpenSession())
{
for (int i = 0; i < 10; i++)
{
session.Store(new User {Name = "User" + i});
}
session.SaveChanges();
}
using (IDocumentSession session = store.OpenSession())
{
List<User> users = session.Query<User>().Customize(x => x.WaitForNonStaleResultsAsOfNow()).ToList();
Console.WriteLine("{0} SO19941925.Users", users.Count);
}
Operation s = store.DatabaseCommands.UpdateByIndex("Raven/DocumentsByEntityName",
new IndexQuery {Query = "Tag:Users"},
new ScriptedPatchRequest
{
Script = #"this['#metadata']['Raven-Clr-Type'] = 'SO19941925.Models.User, SO19941925';"
}, true
);
s.WaitForCompletion();
using (IDocumentSession session = store.OpenSession())
{
List<Models.User> users =
session.Query<Models.User>().Customize(x => x.WaitForNonStaleResultsAsOfNow()).ToList();
Console.WriteLine("{0} SO19941925.Models.Users", users.Count);
}
Console.ReadLine();
}
}
internal class User
{
public string Name { get; set; }
}
}
namespace SO19941925.Models
{
internal class User
{
public string Name { get; set; }
}
}
UPDATE: Based on the initial answer above, here is the code that actually solves the OP question:
store.DatabaseCommands.UpdateByIndex("Raven/DocumentsByEntityName",
new IndexQuery {Query = "Tag:Users"},
new ScriptedPatchRequest
{
Script = #"this['OAuthAccounts']['$type'] =
'System.Collections.ObjectModel.Collection`1[
[Friendorsement.Domain.Membership.IFlexOAuthAccount,
Friendorsement.Domain]], mscorlib';",
}, true
);
Here are two possible solutions:
Option 1: Depending on what state your project is in, for example if you are still in development, you could easily just delete that collection out of RavenDB from the Raven Studio and recreate all those User documents. All the new User documents should then have the correct class name and assembly and should then deserialize correctly. Obviously, if you are already in production, this probably won't be a good option.
Option 2: Depending on how many User documents you have, you should be able to manually edit each one to specify the correct C# class name and assembly, so that they will be deserialized correctly. Again, if you have too many objects to manually modify, this may not be a good option; however, if there are just a few, it shouldn't be too bad to open each one up go to the metadata tab and paste the correct value for "Raven-Entity-Name" and "Raven-Clr-Type".
I ended up doing this:
Advanced.DatabaseCommands.UpdateByIndex(
"Raven/DocumentsByEntityName",
new IndexQuery {Query = "Tag:Album"},
new []{ new PatchRequest() {
Type = PatchCommandType.Modify,
Name = "#metadata",
Nested= new []{
new PatchRequest{
Name= "Raven-Clr-Type",
Type = PatchCommandType.Set,
Value = "Core.Model.Album, Core" }}}},
false);

Get data from an object and put it in a textbox

I have an object which contains 2 pieces of information in objData[0]. The information is System_ID and Network_ID. The data is coming from a query to a database.
I want to get the data out of the object and display it in two separate text boxes, one for system_ID and one for Network_ID. Right now I am putting them into a combo box.
See below my code:
//get network ID and systenm name
private void cmbAddItem_SelectedIndexChanged(object sender, EventArgs e)
{
FASystems fSys = new FASystems(sConn);
object objData = fSys.getSystemNetworkIDFriendlyName(cmbAddItem.Text.ToString());
cmbNetworkID.DataSource = objData;
cmbNetworkID.DisplayMember = "Network_ID";
cmbSysName.DataSource = objData;
cmbSysName.DisplayMember = "System_Name";
// txtNetworkID.Text = objData[0].Network_ID;
}
Assuming your C# compiler is 3.0 or up use the var keyword on the api call
var objData = fSys.getSystemNetworkIDFriendlyName(cmbAddItem.Text.ToString());
Let's assume you're correct that there is an array now in objData with a type in it that has at least Network_ID as a member...
txtNetworkID.Text = objData[0].Network_ID;
should work then.
Can you post the function declaration for getSystemNetworkIDFriendlyName and show how you are populating the return type?
I recommend creating a new class to store the NetworkID and SystemID
class SystemInfo
{
public string NetworkID { get; set; }
public string SystemId { get; set; }
}
Rewrite the function getSystemNetworkIDFriendlyName to return an instance of SystemInfo. Then populating your textbox becomes:
FASystems fSys = new FASystems(sConn);
SystemInfo inf o= fSys.getSystemNetworkIDFriendlyName(cmbAddItem.Text.ToString());
txtNetworkID.Text = info.NetworkID;
Hope this helps,
KenC

Categories