I've been building an email generator function. It started as a regex function, but quickly moved over to reflection to make it as generic as possible. The idea is to have the email generator function pull in information from a messagedata class. It started as a simple task, as the function only had to change out a few static items, but as the function got more complex and the templates being sent out needed tables of information, then the function I had built was not enough.
I've extended the function to use a foreach loop to run through the template and replace text based on a list from the messageData class. I've been trying to get the list created in messageData to implement it into the emailGenerator function.
I've got:
string value = match.Groups["value"].Value;
// Code removed to shorten length
var items = (IEnumerable) value;
But it's not gathering the information from the messageData class. I'm thinking that maybe I need to get the value into a list?
Here is the EmailGenerator function:
public class EmailGenerator : IEmailGenerator
{
private string mergeTemplate(string template, object obj)
{
var operationMatches = operationParser.Matches(template).Cast<Match>().Reverse().ToList();
foreach (var match in operationMatches)
{
string operation = match.Groups["operation"].Value;
string value = match.Groups["value"].Value;
var propertyInfo = obj.GetType().GetProperty(value);
object dataValue = propertyInfo.GetValue(obj, null);
if (operation == "endforeach")
{
string foreachToken = "$foreach " + value + "$";
var startIndex = template.LastIndexOf(foreachToken, match.Index);
var templateBlock = template.Substring(startIndex + foreachToken.Length, match.Index - startIndex - foreachToken.Length);
var items = (IEnumerable) value;
string blockResult = "";
foreach (object item in items)
{
blockResult += this.mergeTemplate(templateBlock, item);
}
template = template.Remove(startIndex, match.Index - startIndex).Insert(startIndex, blockResult);
}
}
}
And here is the messageData class. It gets the information from a DTO.
** EDIT: Removed unnecessary code.
public class messageData : IMailObject
{
public List<messageItemData> Items
{
get
{
var items = new List<messageItemData>();
foreach (var docDTO in this.recipientDTO.InfoList)
{
items.Add(new messageItemData(docDTO));
}
}
}
}
public class messageItemData
{
// Properties
}
What I'm trying to accomplish is that the emailGenerator function is made generic enough to be reusable for other email templates later down the road, gathering the replacement information from the messageData class and the list it contains.
So, I finally found the answer. The code was 99% working. It was as simple as changing var items = (IEnumerable) value; for var items = (IEnumerable) dataValue;
Related
I am developing a C# .Net MVC application and trying to implement a generic search method for entity fields. As our pages are growing i don't want to code a search method each time a new page is added.
For that, i am using Dynamic.Core LINQ Queries : Check it at : https://dynamic-linq.net/basic-simple-query
the way i implemented it works the following way : at user input in the view, the app sends an ajax request to the controller telling it to search that specific value and then display the new list where the previous one.
The problem is : i could make it case insensitive but not accent insensitive and was wondering if anyone could help me with that.
Here is my code :
public static List<T> SearchEntityList<T>(this IQueryable<T> entityList, string searchBy, List<string> fieldsToCheck)
{
if (searchBy == null)
return entityList.ToList();
searchBy = searchBy.ToLower().RemoveDiacriticsUtil();
// Dynamic LINQ Library
string query = "";
foreach (string str in fieldsToCheck)
{
query += str + ".ToString().ToLower().Contains(#0) ||";
}
if (query != null)
{
// Removes the last "OR" inserted on the foreach here on top
query = query.Substring(0,query.Length - 3);
try
{
entityList = entityList.Where(query, searchBy);
}
catch (Exception e)
{
// query is wrong, list wont be filtered.
return entityList.ToList();
}
}
List<T> filteredList = entityList.ToList(); ;
return filteredList;
}
the method receives a list of string representing the fields to check, for example : "Username"
then a string query is built and checked with the database.
This code works as expected and is case insensitive, now i want to add accent insensitive to it.
i modify this line
query += str + ".ToString().ToLower().Contains(#0) ||";
with this one
query += str + "Collate(" + str + ".toString(), \"SQL_Latin1_General_CP1_CI_AI\").Contains(#0) ||";
and now i cannot make it work.
Got this error :
"No applicable method 'Collate' exists in type '...'"
I tested a lot of other stuff such as RemoveDiacritics, etc.. but they dont work with dynamic string linq queries...
Was wondering if anyone already had the same problem. Thanks !
You're on the right track.
You need to register your custom extension methods for dynamic linq before us use it:
// Registration for the default custom type handler
[DynamicLinqType]
public static class MyExtensions
{
// taken from: https://stackoverflow.com/questions/359827/ignoring-accented-letters-in-string-comparison/368850
public static string RemoveDiacritics(this string text)
{
return string.Concat(
text.Normalize(NormalizationForm.FormD)
.Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch) !=
UnicodeCategory.NonSpacingMark)
).Normalize(NormalizationForm.FormC);
}
}
public static class Entry
{
public static void Main(string[] args)
{
// your input
var query = "éè";
// clean it using your extension method
var cleanQuery = query.RemoveDiacritics();
// a test set for this demo
IQueryable<string> testSet = new EnumerableQuery<string>(new List<string>()
{
"soméè", "tèèst", "séét", "BBeeBB", "NoMatchHere"
});
var results = testSet.Where("it.RemoveDiacritics().Contains(#0)", cleanQuery);
Debug.Assert(results.Count() == 4);
}
}
The following code sample writes a simple object to a couchbase lite (version 2) database and reads all objects afterwards. This is what you can find in the official documentation here
This is quite a lot of manual typing since every property of every object must be transferred to the MutableObject.
class Program
{
static void Main(string[] args)
{
Couchbase.Lite.Support.NetDesktop.Activate();
const string DbName = "MyDb";
var db = new Database(DbName);
var item = new Item { Name = "test", Value = 5 };
// Serialization HERE
var doc = new MutableDocument();
doc.SetString("Name", item.Name);
doc.SetInt("Value", item.Value);
db.Save(doc);
using (var qry = QueryBuilder.Select(SelectResult.All())
.From(DataSource.Database(db)))
{
foreach (var result in qry.Execute())
{
var resultItem = new Item
{
// Deserialization HERE
Name = result[DbName].Dictionary.GetString("Name"),
Value = result[DbName].Dictionary.GetInt("Value")
};
Console.WriteLine(resultItem.Name);
}
}
Console.ReadKey();
}
class Item
{
public string Name { get; set; }
public int Value { get; set; }
}
}
From my research Couchbase lite uses JsonConvert internally, so there might be a way to simplify all that with the help of JsonConvert.
Anything like:
var json = JsonConvert.SerializeObject(item);
var doc = new MutableDocument(json); // No overload to provide raw JSON
or maybe
var data = JsonConvert.SerializeToDict(item); // JsonConvert does not provide this
var doc = new MutableDocument(data);
Is there or is this some kind of optimization and the preferred approach is by intend?
People ask about this quite often, but Couchbase Lite does not actually store JSON strings in the database. They are stored in a different format so this would not give the benefit that you think (the JSON would need to be reparsed and then broken down into the other format). I'd been pushing for a way to serialize classes directly instead of going through dictionary objects (which seems like the ultimate goal here) but our priority is on things that enterprise clients want and this doesn't seem to be one of them. Note that for it to make it in, it needs to be implemented in C# Java and Objective-C / Swift.
I don't know about JsonConvert but there seems to be a constructor that takes IDictionary<string, object> as argument. So I would try something like this (brain-compiled):
MutableDocument CreateDocument(object data)
{
if (data == null) return null;
var propertyValues = new Dictionary<string, object>();
foreach (var property in data.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
propertyValues[property.Name] = property.GetValue(data);
}
return new MutableDocument(propertyValues);
}
See if this works.
In my WPF program, I have an area of C# code that is quite repetitive, and it looks like this:
labelFirst = EnglishPicker.SelectedColorText.Substring(0, 1);
labelLast = EnglishPicker.SelectedColorText.Substring(3, 6);
label = labelFirst + labelLast;
UpdateSetting("English", label);
labelFirst = PhotographyPicker.SelectedColorText.Substring(0, 1);
labelLast = PhotographyPicker.SelectedColorText.Substring(3, 6);
label = labelFirst + labelLast;
UpdateSetting("Photography", label);
So I had a thought, is it possible to put this in a for loop something like this:
string[] names = {"English","Photography"};
foreach (string name in names)
{
labelFirst = (name +"Picker").SelectedColorText.Substring(0, 1);
}
Solution for those seeking help
Using a SharedMethod, I was able to shorten the code down as EnglishPicker and PhotographyPicker are the same class. Code:
private void GetPickerLabel(PickerClass picker){
labelFirst = picker.SelectedColorText.Substring(0,1);
labelLast = picker.SelectedColorText.Substring(3,6);
return labelFirst + labelLast;
}
UpdateSetting("English",GetPickerLabel(EnglishPicker));
UpdateSetting("Photography",GetPickerLabel(PhotographyPicker));
Thanks for those who helped me. :)
I think you almost nailed the simplest approach to this in your question, but using strings to refer to the pickers can break at run-time.
Try this instead:
Picker[] pickers = { EnglishPicker, PhotographyPicker};
foreach (Picker picker in pickers)
{
labelFirst = picker.SelectedColorText.Substring(0, 1);
}
The advantage here is that you have compile-time correctness and you can explicitly include or exclude any pickers you like.
It would be easy to turn the control back into text via the .Name property to get the setting updated.
Providing EnglishPicker and PhotographyPicker are properties:
You can use Reflection:
IEnumerable<string> names = new [] { "English", "Photography" };
foreach (string name in names)
{
// Build the property name
string propertyName = String.Concat(name, "Picker");
// Get Picker instance by locating the correct property
object picker = this.GetType().GetProperty(propertyName).GetValue(this);
// Get the SelectedColorText value from that instance
string colorText = (string) picker.GetType().GetProperty("SelectedColorText").GetValue(picker);
string first = colorText.Substring(0, 1);
string last = colorText.Substring(3, 6);
string label = String.Concat(first, last);
// Call UpdateSetting
UpdateSetting(name, label);
}
Flexibility comes with a price.
Everything, from the property lookup to the method invocation will be done in runtime, so watch out! It'll compile fine but it might break when you run it.
For instance, if you were to mistype "Enlgish" instead of "English", the code would compile perfectly. But, at runtime, the code will try to locate EnlgishPicker property and it'll fail.
Also, the above code is not defensive at all.
You should update it to include null checks to avoid potential exceptions.
How about an interface? No reflection needed at all. That's what they're for.
public interface ISelectedColorContainer {
string SelectedColorText {get;}
}
An Implementation
(I have no idea what your classes look like, but that's why we use interfaces!)
public class EnglishPickerImplementation:ISelectedColorContainer {
public string SelectedColorText {get {return "sunflower";}}
}
public class PhotographyPickerImplementation:ISelectedColorContainer {
public string SelectedColorText {get {return "chartreuse";}}
}
So you'd have a function that looks like this:
void SomeMethod(){
ISelectedColorContainer items = new ISelectedColorContainer[] {
new EnglishPickerImplementation(), new PhotographyPickerImplementation()};
foreach(var item in items){
var labelFirst = UpdateSetting(item);
var labelLast = item.SelectedColorText.Substring(3, 6);
var label = labelFirst + labelLast;
}
}
string UpdateSetting(ISelectedColorContainer input){
return input.SelectedColorText.Substring(0, 1);
}
Sorry if there are slight syntax errors, I didn't put this in an IDE.
Instead of a reflection-based solution, I would simply refactor the code to move the shared code in a separate method.
Example:
SharedMethod(EnglishPicker, "English");
SharedMethod(PhotographyPicker, "Photography");
// ...
private void SharedMethod(PickerClass picker, string settingName)
{
var labelFirst = picker.SelectedColorText.Substring(0, 1);
var labelLast = picker.SelectedColorText.Substring(3, 6);
var label = labelFirst + labelLast;
UpdateSetting(settingName, label);
}
All of my EF classes have a Projection() method that helps me choose what I want to project from the class to the SQL queries:
Example:
public static Expression<Func<Something, dynamic>> Projection()
{
return e => new
{
something = e.SomethingId,
name = e.Name,
requiredThingId = e.RequiredThingId,
requiredThing = new
{
requiredThingId = e.RequiredThing.RequiredThingId,
name = e.RequiredThing.Name,
...
},
optionalThingId = e.OptionalThingId,
optionalThing = e.OptionalThingId == null ? null : new
{
optionalThingId = e.OptionalThing.OptionalThingId,
name = e.OptionalThing.Name
}
...
};
}
I use these projection methods like this:
List<dynamic> projected = dbContext.Something.Select(Something.Projection()).ToList();
This way lets me reuse my projections all around my project.
I want to use ServiceStack.Text to serialize these lists to CSV.
I'm doing this:
string csvString = CsvSerializer.SerializeToCsv<dynamic>(Something.Select(Something.Projection()).ToList());
byte[] csvBytes = System.Text.Encoding.Unicode.GetBytes(csvString);
return File(csvBytes, "text/csv", "foo.csv");
But the result is an empty csv (csvString is full of "\r\n"'s and nothing more)
Questions:
Am I using correctly the SerializeToCsv() method?
Can I use <dynamic> in this library?
Suggestions for achieving my purpose?
For the example above the desired CSV would flatten all the properties, something like:
"somethingId";"name";"requiredThingId";"requiredThing.requiredThingId";"requiredThing.name";"optionalThingId";"optionalThing.optionalThingId";"optionalThing.name"
I will answer my own questions, but will not mark as accepted in hope of a new greater answer..
Am I using correctly the SerializeToCsv() method? Can I use dynamic
in this library?
Answer: Yes to both but ServiceStack.Text v4.0.36 is needed (which is available at MyGet, not Nuget)
Suggestions for achieving my purpose?
Answer: With ServiceStack.Text it is possible to serialize to CSV a List<dynamic>, but all the nested properties will be rendered as JSON and they will not be flattened out, eg:
List<dynamic> list = new List<dynamic>();
list.Add(new
{
name = "john",
pet = new
{
name = "doggy"
}
});
string csv = CsvSerializer.SerializeToCsv(list);
This list will be serialized to this csv:
name, pet
"john", { name = "doggy" }
And not to this csv, as I was expecting:
name, pet_name
"john", "doggy"
So... I finally ended up writing this code:
public class CsvHelper
{
public static string GetCSVString(List<dynamic> inputList)
{
var outputList = new List<Dictionary<string, object>>();
foreach (var item in inputList)
{
Dictionary<string, object> outputItem = new Dictionary<string, object>();
flatten(item, outputItem, "");
outputList.Add(outputItem);
}
List<string> headers = outputList.SelectMany(t => t.Keys).Distinct().ToList();
string csvString = ";" + string.Join(";", headers.ToArray()) + "\r\n";
foreach (var item in outputList)
{
foreach (string header in headers)
{
if (item.ContainsKey(header) && item[header] != null)
csvString = csvString + ";" + item[header].ToString();
else
csvString = csvString + ";";
}
csvString = csvString + "\r\n";
}
return csvString;
}
private static void flatten(dynamic item, Dictionary<string, object> outputItem, string prefix)
{
if (item == null)
return;
foreach (PropertyInfo propertyInfo in item.GetType().GetProperties())
{
if (!propertyInfo.PropertyType.Name.Contains("AnonymousType"))
outputItem.Add(prefix + "__" + propertyInfo.Name, propertyInfo.GetValue(item));
else
flatten(propertyInfo.GetValue(item), outputItem, (prefix.Equals("") ? propertyInfo.Name : prefix + "__" + propertyInfo.Name));
}
}
}
What this does is:
It flattens the List, so that all the properties of the objects in the list are primitives (eg: no nested properties)
It creates a CSV from that flattened list.
This algorithm is O(n*m), being
n: number of items in the list
m: number of properties inside each item (including nested properties)
Whilst the recommendation is to use clean POCO's for Serialization, you can serialize in-line anonymous types, e.g:
var somethings = dbContext.Something.Select(e => new {
something = e.SomethingId,
name = e.Name
});
somethings.ToCsv().Print();
Otherwise I've just added support for serializing IEnumerable<object> and IEnumerable<dynamic> in this commit.
This change is available from v4.0.36+ that's now available on MyGet.
I believe that part of what is going on is that the library is using properties in the object to determine what to serialize. I believe that a dynamic object does not construct properties like it's a POCO. If you don't setup a getter and setter on your object, it certainly won't work. Just for reference, I using version 4.5.6.0 of this library.
I'm new to using Dynamic Objects in C#. I am reading a CSV file very similarly to the code found here: http://my.safaribooksonline.com/book/programming/csharp/9780321637208/csharp-4dot0-features/ch08lev1sec3
I can reference the data I need with a static name, however I can not find the correct syntax to reference using a dynamic name at run time.
For example I have:
var records = from r in myDynamicClass.Records select r;
foreach(dynamic rec in records)
{
Console.WriteLine(rec.SomeColumn);
}
And this works fine if you know the "SomeColumn" name. I would prefer to have a column name a a string and be able to make the same type refrence at run time.
Since one has to create the class which inherits from DynamicObject, simply add an indexer to the class to achieve one's result via strings.
The following example uses the same properties found in the book example, the properties which holds the individual line data that has the column names. Below is the indexer on that class to achieve the result:
public class myDynamicClassDataLine : System.Dynamic.DynamicObject
{
string[] _lineContent; // Actual line data
List<string> _headers; // Associated headers (properties)
public string this[string indexer]
{
get
{
string result = string.Empty;
int index = _headers.IndexOf(indexer);
if (index >= 0 && index < _lineContent.Length)
result = _lineContent[index];
return result;
}
}
}
Then access the data such as
var csv =
#",,SomeColumn,,,
ab,cd,ef,,,"; // Ef is the "SomeColumn"
var data = new myDynamicClass(csv); // This holds multiple myDynamicClassDataLine items
Console.WriteLine (data.OfType<dynamic>().First()["SomeColumn"]); // "ef" is the output.
You will need to use reflection. To get the names you would use:
List<string> columnNames = new List<string>(records.GetType().GetProperties().Select(i => i.Name));
You can then loop through your results and output the values for each column like so:
foreach(dynamic rec in records)
{
foreach (string prop in columnNames)
Console.Write(rec.GetType().GetProperty (prop).GetValue (rec, null));
}
Try this
string column = "SomeColumn";
var result = rec.GetType().GetProperty (column).GetValue (rec, null);