I use the powerful FileHelpers Library. But is there a built-in way to search over the generated objets.
var engine = new FileHelperEngine<Text>();
var res = engine.ReadFile("myfile.csv");
string result = res["key"].value;
My csv is like : key;value
I mean, is it possible not to access objects with the array [0], [1], [12]...
maybe like in the code example.
Thanks a lot !
You can convert your resulting array to a dictionary via LINQ with:
var dictionary = validRecords.ToDictionary(r => r.Key, r => r.Value);
The following complete program demonstrates the approach.
[DelimitedRecord(",")]
public class ImportRecord
{
public string Key;
public string Value;
}
class Program
{
static void Main(string[] args)
{
var engine = new FileHelperEngine<ImportRecord>();
string fileAsString = #"Key1,Value1" + Environment.NewLine +
#"Key2,Value2" + Environment.NewLine;
ImportRecord[] validRecords = engine.ReadString(fileAsString);
var dictionary = validRecords.ToDictionary(r => r.Key, r => r.Value);
Assert.AreEqual(dictionary["Key1"], "Value1");
Assert.AreEqual(dictionary["Key2"], "Value2");
Console.ReadKey();
}
}
Related
This is a function to work with lists in string interpolation. It takes a List and an inner Func, and it appends the string result of the inner Func called for each member of the list, with a separator.
So the following builds a valid start of an Insert statement...
static void Main(string[] args)
{
var tableName = "customers";
var cols = new List<dynamic>
{
new { Name = "surname"},
new { Name = "firstname"},
new { Name = "dateOfBirth"}
};
Func<List<dynamic>, Func<dynamic, string>, string, string> ForEach = (list, func, separator) =>
{
var bldr = new StringBuilder();
var first = true;
foreach (var obj in list)
{
if (!first)
bldr.Append(separator);
first = false;
bldr.Append(func(obj));
}
return bldr.ToString();
};
var InsertStatement = $"Insert into { tableName } ( {ForEach(cols, col => col.Name, ", ")} )";
Console.WriteLine(InsertStatement);
Console.ReadLine();
}
Outputs...
Insert into customers ( surname, firstname, dateOfBirth )
It works for dynamic. How do I make it work for any type? The outer Func shouldn't care about the Type in the list, it just passes it through to the inner Func.
The .NET framework already gives you a generic function to achieve what you are trying to do String.Join and you can combine it with a LINQ Select statement, which will allow you to use a lambda on a generic type to select the property that you want to print. You can view the source code of these methods if you are interested as they are open source.
using System;
using System.Collections.Generic;
using System.Linq;
public class MyType
{
public string Name { get; set; }
}
public class Program
{
public static void Main()
{
var tableName = "customers";
var cols = new List<MyType>
{
new MyType { Name = "surname"},
new MyType { Name = "firstname"},
new MyType { Name = "dateOfBirth"}
};
var InsertStatement = $"Insert into { tableName } ( {String.Join(", ", cols.Select(col => col.Name))} )";
Console.WriteLine(InsertStatement);
}
}
Replace dynamic with object, or TValue with a type constraint stipulating it must be a class (where TValue : class), and call obj.ToString() instead of just obj
However, this doesn't guarantee it would "work with any type" - for that you need to know that those types all follow a contract to output the desired column name as their string representation. To get more specificity, require that your accepted types must implement some interface eg IColumnName and put that interface into the type constraint instead
You can create the text easily like this:
var query = $"INSERT INTO {tableName}({string.Join(",", cols.Select(x=>x.Name))})";
However, if for learning purpose you are going to handle the case using a generic method, you can create a generic function like the following and then easily use a for loop and strip additional separator using TrimEnd, or as a better option, like String.Join implementation of .NET Framework get enumerator like this:
string Join<TItem>(
IEnumerable<TItem> items, Func<TItem, string> itemTextSelecor, string separator)
{
var en = items.GetEnumerator();
if (!en.MoveNext())
return String.Empty;
var builder = new StringBuilder();
if (en.Current != null)
builder.Append(itemTextSelecor(en.Current));
while (en.MoveNext())
{
builder.Append(separator);
if (en.Current != null)
builder.Append(itemTextSelecor(en.Current));
}
return builder.ToString();
}
And use it this way:
var tableName = "customers";
var cols = new[]
{
new { Name = "surname"},
new { Name = "firstname"},
new { Name = "dateOfBirth"}
};
var InsertStatement = $"INSERT INTO {tableName} ({Join(cols, col => col.Name, ", ")})"
+ $"VALUES({Join(cols, col => $"#{col.Name}", ", ")})";
Upon completeing the tutorial here:
https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/sorting-filtering-and-paging-with-the-entity-framework-in-an-asp-net-mvc-application
I currently use the following
userAccesses = userAccesses.Where(s => s.employeeDetail.employeeNumber.ToUpper().Contains(searchValue.ToUpper()));
However I'd like to concatenate the knownas / surname columns and then do the contains on the concatenated items.
Could anyone explain/suggest an example syntax?
This is what I've attempted below but I'm certain my syntax is incorrect.
userAccesses = userAccesses.Where(s => s.employeeDetail.employeeNumber + " " + s.employeeDetail.knownas + " " + s.employeeDetail.surname).Contains(searchValue);
Thanks for the response everyone, final working version is below.
userAccesses.Where(x => (x.employeeDetail.employeeNumber + x.employeeDetail.knownas + x.employeeDetail.surname).Contains(searchValue));
You should go to this direction
public class Employee
{
public string knownas { get; set; }
public string userName { get; set; }
}
public void Test()
{
List<Employee> employess = new List<Employee>();
string searchvalue = "test";
var listEmplyer = employess.Where(x => (x.userName + x.knownas).Contains(searchvalue));
}
You'll simply need to concatenate the string and call Contains on that string.
userAccesses
.Where(s => $"{s.employeeDetail.employeeNumber} {s.employeeDetail.knownas} {s.employeeDetail.surname}".Contains(searchValue))
If you need an enumerable of strings as the result of the expression, you can also choose to use the following:
userAccesses
.Select(s => $"{s.employeeDetail.employeeNumber} {s.employeeDetail.knownas} {s.employeeDetail.surname}")
.Where(s => s.Contains(searchValue))
Searching through concatenated column values is weird...
I'd suggest smth like this:
var filtered = userAccesses.Where(s => s.employeeDetail.employeeNumber.Contains(searchValue)
|| s.employeeDetail.knownas.Contains(searchValue)
|| s.employeeDetail.surname.Contains(searchValue));
I want to use a string array stored in the web.config to easily change its values, this is in the format: full_w=670|small_w=100,q=low|tiny_h=30,c=true. Each template is split by the | (pipe) and then each of those sets comprises of a name (left of _) and its corresponding values (right of _), the values can be several and each separated by the , (comma). I think this possibly qualifies for a 3D array, I just can't seem to get an easy way to read this in a sensible manner. Any ideas or solutions as to the best way to read/manage the data from this string?
Basically, in the end I want to be able to call the template small and read its values which in this case are width=100 and quality=low.
Here's the function I wrote to parse one of these settings strings:
public static Dictionary<string, Dictionary<string, string>> getSettings(string settingsStr)
{
return settingsStr.Split('|').ToDictionary(
template => template.Split('_')[0],
template => template.Split('_')[1].Split(',').ToDictionary(
setting => setting.Split('=')[0],
setting => setting.Split('=')[1]));
}
It just uses a lot of string .Splitting and .ToDictionarying.
Here's the test, showing that it works:
var result = getSettings("full_w=670|small_w=100,q=low|tiny_h=30,c=true");
/*
result = {
[ "full" => [ "w" => "670" ] ]
[ "small" => [ "w" => "100", "q" => "low" ] ]
[ "tiny" => [ "h" => "30", "c" => "true" ] ]
}
*/
To read the values w and q from template small, you can do this:
int width = int.Parse(result["small"]["w"]);
string quality = result["small"]["q"];
Edit: As an added bonus, if you want to convert the Dictionary<string, Dictionary<string, string>> back into a single settings sting, you can use this method:
public static string getSettingsStr(Dictionary<string, Dictionary<string, string>> settings)
{
return string.Join("|",
settings.Select(kvp =>
kvp.Key + "_" + string.Join(",",
kvp.Value.Select(setting =>
setting.Key + "=" + setting.Value))));
}
Use:
string settingsStr = getSettingsStr(result);
// settingsStr = "full_w=670|small_w=100,q=low|tiny_h=30,c=true"
If you want to check that a specific template or setting exists, then use the .ContainsKey() method:
// If I have "Dictionary<string, Dictionary<string, string>> settings;"
int width = -1;
string quality = null;
if (settings.ContainsKey("small"))
{
if (settings["small"].ContainsKey("w"))
width = int.Parse(settings["small"]["w"]);
if (settings["small"].ContainsKey("q"))
quality = settings["small"]["q"];
}
Have you considered using plain old XML Serialization with your own plain old C# objects. Here is an example:
public class Program
{
static void Main(string[] args)
{
var data = new MyConfig[2];
for (int i = 0; i < 2; i++)
{
data[i] = new MyConfig { Name = "Name" + i };
data[i].Properties = new MyConfigAttribute[]
{
new MyConfigAttribute { Name = "Property Name " + i, Value = "Property Value " + i },
new MyConfigAttribute { Name = "2nd Property Name " + i, Value = "2nd Property Value " + i },
};
}
var serializer = new XmlSerializer(typeof(MyConfig[]));
using (StreamWriter tw = File.CreateText(#"c:\temp\myconfig.xml"))
{
serializer.Serialize(tw, data);
}
using (StreamReader tw = File.OpenText(#"c:\temp\myconfig.xml"))
{
var readBack = serializer.Deserialize(tw);
}
Console.ReadLine();
}
[XmlRoot("MY_CONFIG")]
public class MyConfig
{
[XmlElement("NAME")]
public string Name { get; set; }
[XmlArray]
[XmlArrayItem(typeof(MyConfigAttribute))]
public MyConfigAttribute[] Properties { get; set; }
}
[XmlRoot("MY_CONFIG_ATTRIBUTE")]
public class MyConfigAttribute
{
[XmlElement("ATTRIBUTE_NAME")]
public string Name { get; set; }
[XmlElement("ATTRIBUTE_VALUE")]
public string Value { get; set; }
}
}
Basically, you create a class to store your individual attributes (MyConfigAttribute in this case), wrap it in another class to provide your name for a group of related attributes (MyConfig in this case), then use normal XML Serialization to write the settings out to an individual XML file, like this section of the code
var serializer = new XmlSerializer(typeof(MyConfig[]));
using (StreamWriter tw = File.CreateText(#"c:\temp\myconfig.xml"))
{
serializer.Serialize(tw, data);
}
You can read it back to objects again using this section of the code:
using (StreamReader tw = File.OpenText(#"c:\temp\myconfig.xml"))
{
var readBack = serializer.Deserialize(tw);
}
The advantage of this is:
It is simple to understand and use
You can add features to your custom class, e.g. to add values to the array of properties, thereby lending itself to wrapping a custom screen around it.
Look up C# XML Serialization on Google!
I have a need to store lambda expressions in a database and execute them dynamically. The following snippet works well as is,
float result = dataRowArray.AsEnumerable().Sum(r => r.Field<float>("ColumnA") * r.Field<float>("ColumnB"));
but I'd like to store the following part in a string...
r => r.Field<float>("ColumnA") * r.Field<float>("ColumnB")
... and inject it dynamically as in:
float result = MySession.Current.dr.AsEnumerable().Sum(storedLambdaString);
I've done a ton of Googling but I'm coming up short. Any suggestions?
I suggest store the query in the database and execute using LINQ like below.
using (eEntities db = new eEntities())
{
string query = "select name,age,salary from employees"
var employees = db.Database.SqlQuery<Employee>(query).
}
Please note: Employee is here my custom class having properties same as queried.
Theres a nuget package that may help you "System.Linq.Dynamic". This sample code works...
public class TestRow
{
public Dictionary<string,float> Field { get; set; }
public TestRow()
{
this.Field = new Dictionary<string, float>();
this.Field.Add("ColumnA", 2);
this.Field.Add("ColumnB", 3);
}
}
class Program
{
static void Main(string[] args)
{
var testRow = new TestRow();
string expressionString = "r.Field[\"ColumnA\"] * r.Field[\"ColumnB\"]";
var parameter = Expression.Parameter(typeof(TestRow),"r");
var lambdaET = System.Linq.Dynamic.DynamicExpression.ParseLambda(new[] { parameter }, typeof(float), expressionString);
var result = lambdaET.Compile().DynamicInvoke(testRow);
Console.WriteLine(result);
Console.ReadLine();
}
}
Had a class:
class filedate
{
public int id;
public string fname;
}
Fill my list with values:
List<filedate> List = ReadList(sqlFiles);
string[] FolderFiles = System.IO.Directory.GetFiles(path2Copy);
Trying to get results:
var results = List.Where(filedate =>
FolderFiles.Any(x=>Path.GetFileNameWithoutExtension(x) ==
Path.GetFileNameWithoutExtension(filedate.fname)));
I have the same files in List and FolderFiles, but get no results in results. I am a newbie to Linq. Where is the problem?
update:
List: (count) > 1000
for example:
<1023, 'tr_F2opervag_2808_1644.dat'>
FolderFiles example:
"\\domain.corp.dns\share\folder\tr_F2opervag_2808_1644.dat"
Update 2:
found out my mistake! Comment with intersection was helpful! This code is working:
var results = List.Where(
(filedate x) =>
{
return ! FolderFiles.Any(xxx =>
Path.GetFileNameWithoutExtension(xxx) ==
Path.GetFileNameWithoutExtension(x.fname));
});
You're code works fine for me so there's something wrong with the format of your data in the List coming back from the database.
Post an example of an fname value from the filedata object. It needs to be a valid fully qualified path.
This works fine for me.
public class FileData{
public int id;
public string fname;
}
void Main()
{
List<FileData> list = new List<FileData>{
new FileData { id=1, fname="C:\\install.res.1042.dll"},
new FileData { id=2, fname="C:\\install.res.1041.dll" },
new FileData { id=3, fname="C:\\install.res.9999.dll"}
};
string[] FolderFiles = System.IO.Directory.GetFiles("C:\\");
var results = list
.Where(fd =>
FolderFiles.Any(x=>Path.GetFileNameWithoutExtension(x) ==
Path.GetFileNameWithoutExtension(fd.fname)));
Console.WriteLine(results);
}
If you need to find the difference this should work. This is available via Enumerable.Except.
var dbFiles = ReadList(sqlFiles);
var dbFilePaths =
dbFiles.Select(fdate =>
Path.GetFileNameWithoutExtension(fdate.fname).ToLower());
var fsFilePaths =
Directory
.GetFiles(path2Copy)
.Select(filePath =>
Path.GetFileNameWithoutExtension(filePath).ToLower());
var diff =
dbFilePaths
.Except(fsFilePaths)
.Join(dbFiles,
filePath => filePath,
fdate => fdate.fname,
(filePath, fdate) => fdate)
.ToList();