I've been searching and playing around with GetType() for a while, but I keep getting the name of the column (which I already have) and not the value.
I'm trying to build a csv file from the results of a method (stored procedure call) in a datacontext.
I'm fine up to figuring out how to dynamically grab result.some_column_name from the result set.
using (SomeDataContext ctx = new SomeDataContext())
{
List<Some_MethodResult> results = ctx.Get_Some_Method(parameter1, parameter2, parameter3).ToList();
var colnames = ctx.Mapping.MappingSource.GetModel(typeof(SomeDataContext)).GetMetaType(typeof(Get_Some_MethodResult)).DataMembers;
foreach (Get_Some_MethodResult r in results)
{
foreach (var colname in colnames)
{
string line = "\"" + r.GetType().GetField(colname.Name).ToString() + "\",";
sb.Append(line);
}
}
The above gets me the name of the field, and I'm looking for the value. GetMember() doesn't seem to get me any better results.
I'm still looking to see if I can find out the right way to dynamically refer to a column by column name, but if this is simple and just a non-often-asked question, I'm hoping someone can point me in the right direction.
Save the result of GetField and call GetValue on it, passing r as the parameter.
But you don't want to be calling GetField inside a nested loop. Loop through the columns once, saving all the GetField() results into a FieldInfo[] array. Then inside your nested loops, fetch from the array.
If that isn't performant enough, you can use an expression tree to build a Func<Get_Some_MethodResult, object> from each field that accesses that field on any arbitrary object, and save an array of those instead of FieldInfo. (For properties, this was possible using GetGetMethod and CreateDelegate before expression trees). Then you wouldn't need any reflection at all inside the nested loops.
Of course, all of this complexity might be avoided if you skipped over your ORM mapping and just processed the IDataReader that came back from the stored procedure. A data reader provides easy indexed access to both column values and values.
It's really quite trivial to write a function that turns any IDataReader into CSV.
So, the answer has a couple of parts to it. Kudos to my co-worker Tony Colich for helping me learn on this one.
First, it's GetProperties() and not GetFields() that I should have been looking at for the column values returned by the sproc.
Knowing that helped immensely. The new loop looks like (please ignore the colval/boolean bit):
using (SomeDataContext ctx = new SomeDataContext())
{
List<Some_MethodResult> results = ctx.Get_Some_Method(parameter1, parameter2, parameter3).ToList();
var colnames = ctx.Mapping.MappingSource.GetModel(typeof(SomeDataContext)).GetMetaType(typeof(Get_Some_MethodResult)).DataMembers;
var props = typeof(Get_Some_MethodResult).GetProperties().Where(p => colnames.Any(n => p.Name == n.Name));
foreach (Get_Some_MethodResult r in results)
{
foreach (var colname in colnames)
{
bool firstcol = true;
foreach (var colname in colnames)
{
var prop = props.FirstOrDefault(p => p.Name == colname.Name);
if (prop != null)
{
string colval = "";
if (!firstcol)
{
colval = ",\"" + prop.GetValue(r, null).ToString() + "\"";
}
else
{
colval = "\"" + prop.GetValue(r, null).ToString() + "\"";
}
//var field =
sb.Append(colval);
firstcol = false;
}
}
sb.AppendLine();
}
}
Now, as far as whether or not I should have done this at all, there are only so many hours to spend on a project sometimes, and this was the path of least resistance. Thanks to all who responded!
Related
This is the model I am receiving from an API.
var query = FindBy(item => item.TaskEnabledStatus == trueOrFalse).Select(items => new MainGridDto()
{
PK_Task = items.PK_Task,
TaskName = items.TaskName,
TaskDescription = items.TaskDescription,
TaskEnabledStatus = items.TaskEnabledStatus,
BusinessName = items.tBusinessGroups.BusinessName,
ExecutionTime = items.ExecutionTime.Value,
BusinessGroupDto = new BusinessGroupDto()
{
PK_BusinessGroup = items.tBusinessGroups.PK_BusinessGroup,
BusinessName = items.tBusinessGroups.BusinessName,
BusinessGroupEnabledStatus = items.tBusinessGroups.BusinessGroupEnabledStatus
},
EmailBody = items.EmailBody,
EmailSubject = items.EmailSubject,
UserOwner = items.tUsers.tRecipients.FirstName + " " + items.tUsers.tRecipients.LastName,
UsersDto = new UsersDto()
{
PK_User = items.tUsers.PK_User,
UserName = items.tUsers.UserName,
FirstAndLast = items.tUsers.tRecipients.FirstName + " " + items.tUsers.tRecipients.LastName
},
UserLastModified = items.tUsers.UserName,
Frequency = items.Frequency,
LastModifiedOn = items.LastModifiedOn
});
What I am trying to do is Deserialize the Object into a dictionary, but I keep getting an error because my keys are null. How can I assigned the PK to the dictionary and ExecutionTime as the value. Or even assigning an index to the keys and the object as the value? I had it working with a List, but I wanted to change it to a dictionary to have more efficient searching and performance.
var model = JsonConvert.DeserializeObject<Dictionary<int, ReportInfoDto>>(dtoResponse.Result);
An additional note, I am searching the dictionary with a linq query. Is that good practice or is there a more effiecent way?
var itemsToSendOut = from reports in model
where reports.Value.ExecutionTimeFormatted == DateTime.Now.ToString("hh:mm tt ")
select reports;
This is how I am calling the API and getting a json reponse with a list of objects.
private HttpClient client;
client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:57595/");
var getAllReports = client.GetAsync("api/AllReports").Result;
if (getAllReports.IsSuccessStatusCode)
{
var dtoResponse = getAllReports.Content.ReadAsStringAsync();
dtoResponse.Wait();
var model = JsonConvert.DeserializeObject<Dictionary<int, ReportInfoDto>>(dtoResponse.Result, new KeyValuePairConverter());
var itemsToSendOut = from reports in model
where reports.Value.ExecutionTimeFormatted == DateTime.Now.ToString("hh:mm tt ")
select reports;
This is an example data and exact format that the result is giving me when I debug.
Result = "[{\"PK_Task\":3,\"TaskName\":\"ExampleReportName\",\"BusinessName\":\"ExampleBusinessName\",\"UserOwner\":\"Stack Leet\",\"UsersDto\":{\"PK_User\":123,UserName\":\"Stack.L ...
So in your example, if I am hearing you right when you say in your comment: I am trying to check each report and look at their ExecutionTime, it sounds like what you want is a:
Dictonary<DateTime, MyObject>
Where YourObject is a single instance from the List<> you are getting from the API. And what you would like to do is extract ExecutionTime from the object and use it as the Key in the Dictionary.
Have I got that right? I'm going to answer under that assumption. Let me know if I'm wrong.
As far as I know, I don't think there is a way to deserialize a JSON string to a Dictionary and to specify which property becomes the dictionary's key. I could be wrong! And if I am, I hope someone will tell me and provide a better answer. But so far, I've not seen a way to make this happen.
That being said, how I would do it is something like this:
// Convert the json string from the API to a List:
List<MyObject> myObjects = JsonConvert.DeserializeObject<List<MyObject>>(dtoResponse.Result);
// Loop over list and make a Dictionary
Dictonary<DateTime, MyObject> model = new Dictonary<DateTime, MyObject>()
foreach (MyObject myObject in myObjects)
{
if (myObject.ExecutionTime != null)
model.Add(myObject.ExecutionTime, myObject);
}
Again, IF there is a way to do all of that in a single DeserializeObject<Dictionary...> line, I'd love to know about it. I looked and didn't see it documented anywhere. But this will get you your Dictionary that you can then quickly and easily search by ExecutionTime.
(As a side note: this assumes that each ExecutionTime is unique. If they are not, it won't work.)
Hope this helps.
EDIT:
You asked:
An additional note, I am searching the dictionary with a linq query. Is that good practice or is there a more effiecent way?
var itemsToSendOut = from reports in model
where reports.Value.ExecutionTimeFormatted == DateTime.Now.ToString("hh:mm tt ")
select reports;
Searching a dictionary by something in the value really negates the speed that a dictionary otherwise provides. If this is what you want, then there is no need for the dictionary at all. The speed of a dictionary comes because you can search for the value by the key. So you can do an O(1) search. Whereas if you are searching a list (or a Dictionary by it's value) then you are doing an O(n) search where n is the count of items in the list or dictionary.
I have an object called reportData, which is holding the data for a report.
This object is of type
List<Dictionary<string, string>>
I need to add logic to manipulate reportData that when the value of the key "Type" is Withdrawal the value of the key "TransAmount" should have a minus sign before it.
I was thinking I can accomplish this with some linq but I am not having any success.
This is what I tried so far...
foreach (var kvp in reportData.SelectMany(m => m).Where(x => x.Value != null))
{
if (kvp.Value.Equals("Deposit"))
(
//Over here I need to set the value of the key "TransAmount" to have a minus sign before it, but I'm not sure how to go about it
)
}
Over here is a snapshot of the data that is being held in reportData. The showcased item in the list is of Type "Withdrawal", My code needs to deal with items in the list that are of Type "Deposit"
https://gyazo.com/a5183aa404e51672712d680dcd8ad6af
How about something like this ?
foreach (var dict in reportData)
{
var keys = new List<string>(dict.Keys);
foreach (string key in keys)
{
if (key == "Type")
{
if (dict[key] == "Deposit")
{
dict["TransAmount"] = "-" +dict["TransAmount"] ;
}
}
}
}
Try this https://dotnetfiddle.net/Ii0MR7
We only need one loop and one operation.
We take dictionaries one by one, in each dictionary we are looking for the specific key "Type" while at the same time trying to get its value to variable called type (that's precisely what TryGetValue does). It also returns true in case when element exists. At this point we only need to make sure that the value of it is the one we're looking for.
If yes - we get in to the code block and modify another value. If you're not familiar with $ string interpolation please check this article.
foreach (var dict in reportData)
{
if (dict.TryGetValue("Type", out var type)
&& type == "Withdrawal"
&& dict.TryGetValue("TransAmount", out var amt)
&& !string.IsNullOrEmpty(amt))
{
dict["TransAmount"] = $"-{dict["TransAmount"]}";
}
}
And yes you can do it with LINQ but it is not recommended, a good use-case for LINQ is data querying and for manipulating data it is better to use good old loops, nevertheless here is the code:
reportData = reportData.Select(d =>
{
if (d.TryGetValue("Type", out var type) && type == "Withdrawal")
{
d["TransAmount"] = $"-{d["TransAmount"]}";
}
return d;
}.ToList(); // this will create a new instance of List<Dictionary>
I have a List of dynamic objects that I am trying to use dynamic Linq on. I am using dynamic objects because I do not know the properties that will be coming into the object. Linq works on my dynamic object, but, to avoid giant hard coding if statements, I would like to use dynamic Linq to search my list. The top half of the code snippet works but I need it to work dynamically so I can create a query string from my properties and filter that way.
public List<dynamic> GetFilteredLocationData(List<dynamic> locationData, string searchTerm){
//Does work
List<dynamic> totalResults = locationData.Where(x => x.Street.ToLower().Contains(searchTerm.ToLower()) ||
x.Street.ToLower().Contains(searchTerm.ToLower()) ||
x.Zip.ToLower().Contains(searchTerm.ToLower()));
//Does not work
var testQueryString = "(Street == \"king\")";
var testResult = locationData.Where(testQueryString);
return totalResults;
}
The runtime error I receive: No property or field 'Street' exists in type 'Object'
That error makes sense as object by default doesn't contain 'Street' but I'd expect the dynamic Linq to behave like the code above it. Is there something I am doing wrong here, or should I take a different approach? I can provide more detail if needed.
Thanks in advance!
Finally I got a working solution! It may not be the most efficient but it works for my needs and allows me to keep the dynamic nature I was hoping to retain. The solution was to drop Linq entirely and use a good old for-each loop. The Important part was the IDictionary which allowed me to search each row for the key value pair. This is the same functionality I was going for, just ditched linq.
public List<dynamic> GetFilteredLocationData(List<dynamic> locationData, string searchTerm){
List<dynamic> totalResults = new List<dynamic>();
List<string> locationProperties = new List<string> {"dynamic properties here, this was filled by call to DB for info pertaining to certain location combined with unique data"}
foreach (var locData in locationData)
{
var currentLoc = locData;
var currentLocDict = (IDictionary<string, object>)currentLoc;
bool containsSearchTerm = CheckIfLocationContainsSearch(currentLocDict, allLocationProperties, searchTerm);
if (containsSearchTerm)
{
totalResults.Add(locData);
}
}
}
public bool CheckIfLocationContainsSearch(IDictionary<string,object> location, List<string> locationProperties, string searchTerm){
foreach (var locProp in locationProperties)
{
if (location[locProp].ToString().ToLower().Contains(searchTerm))
{
return true;
}
}
return false;
}
This is non-language-specific, but I'll use examples in C#. Often I face the problem in which I need to add a parameter to an object inside any given iteration of at least one of its parameters, and I have always to come up with a lame temporary list or array of some kind concomitant with the problem of keeping it properly correlated.
So, please bear with me on the examples below:
Is there an easier and better way to do this in C sharp?
List<String> storeStr;
void AssignStringListWithNewUniqueStr (List<String> aList) {
foreach (String str in aList) {
storeStr.add(str);
str = AProcedureToGenerateNewUniqueStr();
}
}
void PrintStringListWithNewUniqueStr (List<String> aList) {
int i = 0;
foreach (String str in aList) {
print(str + storeStr[i]);
i++;
}
}
Notice the correlation above is guaranteed only because I'm iterating through an unchanged aList. When asking about a "easier and better way" I mean it should also make sure the storeStr would always be correlated with its equivalent on aList while keeping it as short and simple as possible. The List could also have been any kind of array or object.
Is there any language in which something like this is possible? It must give same results than above.
IterationBound<String> storeStr;
void AssignStringListWithNewUniqueStr (List<String> aList) {
foreach (String str in aList) {
storeStr = str;
str = AProcedureToGenerateNewUniqueStr();
}
}
void PrintStringListWithNewUniqueStr (List<String> aList) {
foreach (String str in aList) {
print(str + storeStr);
}
}
In this case, the fictitious "IterationBound" kind would guarantee the correlation between the list and the new parameter (in a way, just like Garbage Collectors guarantee allocs). It would somehow notice it was created inside an iteration and associate itself with that specific index (no matter if the syntax there would be uglier, of course). Then, when its called back again in another iteration and it was already created or stored in that specific index, it would retrieve this specific value of that iteration.
Why not simply project your enumerable into a new form?
var combination = aList
.Select(x => new { Initial = x, Addition = AProcedureToGenerateNewUniqueStr() })
.ToList()
.ForEach(x =>
{
print(x.Initial + x.Addition);
});
This way you keep each element associated with the new data.
aList.ForEach(x => print(x + AProcedureToGeneratorNewUniqueString()));
just messing around, trying to expand my bag o' tricks: I was just experimenting and want to do something like a Dictionary object with another inner Dictionary as the outside Dictionary's .Value
var dictionary = new Dictionary<ObjectType, Dictionary<string, string>>();
ObjectType is an enum
so...what then...either you're not suppose to do this or I just don't know how 'cause I started running into a wall when I was trying to figure out how to populate and retrieve data from it.
Purpose might help: I'm being passed an ObjectGUID and need to flip through a bunch of database tables to determine which table the object exists in. The method I've already written just queries each table and returns count (here are a couple examples)
// Domain Check
sql = string.Format(#"select count(domainguid) from domains where domainguid = ?ObjectGUID");
count = (int)MySQLHelper.ExecuteScalar(ConnectionStrings.ConnectionStrings.V4DB_READ, sql, pObjectGUID).ToString().Parse<int>();
if (count > 0)
return ObjectType.Domain;
// Group Check
sql = string.Format(#"select count(domaingroupguid) from domaingroups where domaingroupguid = ?ObjectGUID");
count = (int)MySQLHelper.ExecuteScalar(ConnectionStrings.ConnectionStrings.V4DB_READ, sql, pObjectGUID).ToString().Parse<int>();
if (count > 0)
return ObjectType.Group;
So, that's all done and works fine...but because the fieldname and table name are the only things that change for each check I started thinking about where I could re-use the repetitive code, I created a dictionary and a foreach loop that flips through and changes the sql line (shown below)...but, as you can see below, I need that ObjectType as kind of the key for each table/fieldname pair so I can return it without any further calculations
Dictionary<string, string> objects = new Dictionary<string,string>();
objects.Add("domains", "domainguid");
objects.Add("domaingroups", "domaingroupguid");
objects.Add("users", "userguid");
objects.Add("channels", "channelguid");
objects.Add("categorylists", "categorylistguid");
objects.Add("inboundschemas", "inboundschemaguid");
objects.Add("catalogs", "catalogguid");
foreach (var item in objects)
{
sql = string.Format(#"select count({0}) from {1} where {0} = ?ObjectGUID", item.Value, item.Key);
count = (int)MySQLHelper.ExecuteScalar(ConnectionStrings.ConnectionStrings.V4DB_READ, sql, pObjectGUID).ToString().Parse<int>();
if (count > 0)
return ?????
}
This isn't all that important since my original method works just fine but I thought you StackOverflow geeks might turn me on to some new clever ideas to research...I'm guessing someone is going to smack me in the head and tell me to use arrays... :)
EDIT # Jon Skeet ------------------------------------------
Heh, sweet, think I might have come upon the right way to do it...haven't run it yet but here's an example I wrote for you
var objectTypes = new Dictionary<string, string>();
objectTypes.Add("domainguid", "domains");
var dictionary = new Dictionary<ObjectType, Dictionary<string, string>>();
dictionary.Add(ObjectType.Domain, objectTypes);
foreach(var objectType in dictionary)
{
foreach(var item in objectType.Value)
{
sql = string.Format(#"select count({0}) from {1} where {0} = ?ObjectGUID", item.Key, item.Value);
count = (int)MySQLHelper.ExecuteScalar(ConnectionStrings.ConnectionStrings.V4DB_READ, sql, pObjectGUID).ToString().Parse<int>();
if (count > 0)
return objectType.Key;
}
}
This chunk should hit the domains table looking for domainguid and if count > 0 return ObjectType.Domain...look right? Only problem is, while it might seem somewhat clever, it's like 2 dictionary objects, a couple strings, some nested loops, harder to read and debug than my first version, and about 10 more lines per check hehe...fun to experiment though and if this looks like to you then I guess it's one more thing I can add to my brain :)
also found this how to fetch data from nested Dictionary in c#
You can definitely do it, although you're currently missing a closing angle bracket and parentheses. It should be:
var dictionary = new Dictionary<ObjectType, Dictionary<string, string>>().
To add a given value you probably want something like:
private void AddEntry(ObjectType type, string key, string value)
{
Dictionary<string, string> tmp;
// Assume "dictionary" is the field
if (!dictionary.TryGetValue(type, out tmp))
{
tmp = new Dictionary<string, string>();
dictionary[type] = tmp;
}
tmp.Add(key, value);
}
If that doesn't help, please show the code that you've tried and failed with - the database code in your question isn't really relevant as far as I can tell, as it doesn't try to use a nested dictionary.