I am totally new to C# and I am having a little issue with understanding how I can make this happen.
I have this class that creates data binding for a UI dropdown node. I haven't written this. I am just trying to tap into it.
public override IEnumerable<AssociativeNode> BuildOutputAst(List<AssociativeNode> inputAstNodes)
{
if (Items.Count == 0 ||
Items[0].Name == NO_FAMILY_TYPES ||
SelectedIndex == -1)
{
return new[] { AstFactory.BuildAssignment(GetAstIdentifierForOutputIndex(0), AstFactory.BuildNullNode()) };
}
var args = new List<AssociativeNode>
{
AstFactory.BuildStringNode(((string) Items[SelectedIndex].Name).ToString())
};
var func = new Func<string, int>(Data.byName);
var functionCall = AstFactory.BuildFunctionCall(func, args);
return new[] { AstFactory.BuildAssignment(GetAstIdentifierForOutputIndex(0), functionCall) };
}
The problem is at the end when I am making a new func variable. Data.byName should return a piece of data by a key name. For that I have used a dictionary like so:
public class Data
{
public string sProcessName { get; set; }
public Dictionary<string, int> dOffsets { get; set; }
public Data()
{
sProcessName = "Client";
Dictionary<string, int> dOffsets = new Dictionary<string, int>()
{
{"Left", 11},
{"Center", 22},
{"Right", 33}
};
}
public static int byName(string name)
{
if (name == null)
{
throw new ArgumentException("name");
}
int intValue = new Data().dOffsets[name];
return intValue;
}
}
This Data() class sits in a separate DLL but the idea is that I call Data.byName("Left") and get back an int 11.
Is this byName definition even required? I am curious if I could just use Data().dOffsets[name] and not having to wrap this whole thing like this
Related
I am migrating from .netcore 2.1 to .net 6. I have an endpoint that has a return type of Task I have a class that has properties of type object.
And after some checks i am returning an ok result of my object i.e return Ok(myObj);
This is working without any problems in .netcore 2.1 but in .net6 the object properties are returning empty arrays for some reason.
I created a new app directly in .net 6 and tried to replicate the issue to see if it is some mistake in I did while migrating or not but it seems that it doesn't work either. The new project looks like this :
[HttpGet("test")]
public IActionResult Test()
{
var features = new Dictionary<string, bool>()
{
{ "Foo", true },
{ "Bar", false }
};
var settings = new Dictionary<string, string>()
{
{ "font-size", "16-px" },
{ "showOnStartUp", "no" }
};
var resp = new Result()
{
InnerDto = new InnerDto()
{
Foo = BuildObject(features.Select(f => new KeyValuePair<string, object>(f.Key, f.Value)).ToDictionary(k => k.Key, v => v.Value)),
Bar = BuildObject(settings.Select(f => new KeyValuePair<string, object>(f.Key, f.Value)).ToDictionary(k => k.Key, v => v.Value))
},
S = "Test"
};
return Ok(resp);
}
private static object BuildObject(Dictionary<string, object> foo)
{
var objects = foo.Select(f => new
{
Key = f.Key,
Value = SetSettingsValueBasedOnType(f)
}).ToList();
var sb = new StringBuilder();
sb.AppendLine("{");
for (int i = 0; i < objects.Count; i++)
{
string val;
var obj = objects[i];
if (bool.TryParse(obj.Value.ToString(), out var parsedVal))
{
val = obj.Value.ToString()!.ToLower();
}
else
{
val = JsonConvert.SerializeObject(obj.Value);
}
var line = $#"""{obj.Key}"":{val}";
sb.AppendLine(line);
if (i < objects.Count - 1)
{
sb.Append(',');
}
}
sb.AppendLine("}");
var featuresObj = JsonConvert.DeserializeObject(sb.ToString());
return featuresObj!;
}
private static object SetSettingsValueBasedOnType(KeyValuePair<string, object> bar)
{
object val;
if (bool.TryParse(bar.Value.ToString(), out var parseVal))
{
val = parseVal;
}
else
{
val = bar.Value;
}
return val;
}
}
public class Result
{
public string S { get; set; }
public InnerDto InnerDto { get; set; }
}
public class InnerDto
{
public object Foo { get; set; }
public object Bar { get; set; }
}
the logic is working to last line before the return ok as you can see here:
and the response I'm getting is like this:
What is making this work on .netcore 2.1 and not on .net 6 ?
if you are using Newtonsoft.JSON, it is fine, just add serialize object to your object.
Change this
return Ok(resp);
to
return Ok(JsonConvert.SerializeObject(resp, Formatting.Indented));
Now we know it works, we could add it globally to all controllers by doing this:
builder.Services.AddControllers()
.AddNewtonsoftJson();
In this case, you might need to install the following package:
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.13" />
Then this will also work return Ok(resp);
And here is your output:
Microsoft doc: https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.newtonsoftjsonmvcbuilderextensions.addnewtonsoftjson?view=aspnetcore-6.0
I have this serviceLayer Method that is used by my Web API proyect to return data to clients:
public IEnumerable<Contactos_view> ListarVistaNew(int activos, string filtro, int idSector, int idClient, string ordenar, int registroInic, int registros)
{
using (var myCon = new AdoNetContext(new AppConfigConnectionFactory(EmpresaId)))
{
using (var rep = base_getRep(myCon))
{
return rep.Listar(activos, filtro, idSector, idClient, ordenar, registroInic, registros);
}
}
}
Now the question is: How can I return only desired property of class Contactos_view? This class contains 20 properties, and my Idea is to add a parameter of type string[] Fields so client can select only the desired propeties.
Is it possible? what would be the returned type of ListarVistaNew in that case?
Thank you!
You can dynamically create and populate expando objects.
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
namespace ClientSelectsProperties
{
public class OriginalType
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
class Program
{
// this simulates your original query result - it has all properties
private static List<OriginalType> queryResult = new List<OriginalType> {
new OriginalType { Id = 1, Name = "one", Description = "one description" },
new OriginalType { Id = 2, Name = "two", Description = "two description" }
};
// "hardcoded" property value readers, go crazy here and construct them dynamically if you want (reflection, code generation...)
private static Dictionary<string, Func<OriginalType, object>> propertyReaders = new Dictionary<string, Func<OriginalType, object>> {
{ "Id", t => t.Id },
{ "Name", t => t.Name },
{ "Description", t => t.Description }
};
static void Main(string[] args)
{
// your client only wants Id and Name
var result = GetWhatClientWants(new List<string> { "Id", "Name" });
}
private static List<dynamic> GetWhatClientWants(List<string> propertyNames)
{
// make sure your queryResult is in-memory collection here. Body of this select cannot be executed in the database
return queryResult.Select(t =>
{
var expando = new ExpandoObject();
var expandoDict = expando as IDictionary<string, object>;
foreach (var propertyName in propertyNames)
{
expandoDict.Add(propertyName, propertyReaders[propertyName](t));
}
return (dynamic)expando;
}).ToList();
}
}
}
I am creating a Log Parsing tool, that is parsing a CSV File into individual classes that derive from a root class. However, it is taking a long time to define the individual classes and to set their individual properties in each class, since there is hundreds of different types of logs. The thing I did notice is that it is all pretty much exactly the same thing and wanted to see if there was a way to speed things up and do something along the lines of how LINQ to DB does things and add some logic in to automatically set properties based on information from Attributes.
Below is an example of what I am working with and an idea on how things should work.
class Program
{
static void Main(string[] args)
{
Dictionary<string, string> dictionary = new Dictionary<string, string>
{
{"key", "Stack Overflow"},
{"item1", "Test"},
{"item2", "Sample"},
{"item3", "3"}
};
Example example = new Example(dictionary);
Console.WriteLine(example.LogKey); //Stack Overflow
Console.WriteLine(example.Item1); //Test
Console.WriteLine(example.Item2); //
Console.WriteLine(example.Item3); //3
Console.ReadKey();
}
}
[AttributeUsage(AttributeTargets.Property)]
class LogItem : Attribute
{
public LogItem(string key)
{
Key = key;
}
public string Key { get; private set; }
public bool Ignore { get; set; }
}
class Log
{
public Log(Dictionary<string, string> items)
{
Dictionary = items;
}
public Dictionary<string, string> Dictionary { get; private set; }
[LogItem("key")]
public string LogKey { get; set; }
}
class Example : Log
{
public Example(Dictionary<string, string> items) : base(items)
{
}
[LogItem("item1")]
public string Item1 { get; set; }
[LogItem("item2", Ignore = true)]
public string Item2 { get; set; }
[LogItem("item3")]
public int Item3 { get; set; }
}
All of my data is comming through as a string unfortunately so it would be a good idea to get the type of the property and converting the string to that. Not important right now for this question since I can do that on my own.
Does anyone have an idea on how to make something like this work? If possible could something like this be done in the Parent Class to allow the Child Class to set the properties with the Attribute Ignore == true on it's own.
I was able to come up with the following after looking into how LINQ to CSV works.
static void ExtractData(Log log)
{
List<PropertyInfo> propertyInfos =
log.GetType()
.GetProperties()
.Where(
p => p.GetCustomAttributes(typeof (LogItem), true).Any(logItem => !((LogItem) logItem).Ignore))
.ToList();
foreach (var propertyInfo in propertyInfos)
{
LogItem logItem = (LogItem)propertyInfo.GetCustomAttributes(typeof(LogItem), true).First();
if(!log.Dictionary.ContainsKey(logItem.Key))
continue;
TypeConverter typeConverter = TypeDescriptor.GetConverter(propertyInfo.PropertyType);
MethodInfo parseNumberMethod = propertyInfo.PropertyType.GetMethod("Parse",
new[] { typeof(String), typeof(NumberStyles), typeof(IFormatProvider) });
MethodInfo parseExactMethod = propertyInfo.PropertyType.GetMethod("ParseExact",
new[] { typeof(string), typeof(string), typeof(IFormatProvider) });
Object objValue = null;
if (typeConverter.CanConvertFrom(typeof(string)))
{
objValue = typeConverter.ConvertFromString(null, CultureInfo.CurrentCulture, log.Dictionary[logItem.Key]);
Debug.WriteLine("TypeConverter - " + propertyInfo.Name);
}
else if (parseExactMethod != null)
{
objValue =
parseExactMethod.Invoke(
propertyInfo.PropertyType,
new Object[]
{
log.Dictionary[logItem.Key],
logItem.OutputFormat,
CultureInfo.CurrentCulture
});
}
else if (parseNumberMethod != null)
{
objValue =
parseNumberMethod.Invoke(
propertyInfo.PropertyType,
new Object[]
{
log.Dictionary[logItem.Key],
logItem.NumberStyles,
CultureInfo.CurrentCulture
});
}
else
{
objValue = log.Dictionary[logItem.Key];
}
PropertyInfo goodPropertyInfo = propertyInfo.DeclaringType.GetProperty(propertyInfo.Name);
goodPropertyInfo.SetValue(log, objValue, null);
}
}
Have you seen LinqToCSV? You create classes for each of the types of log, adding inheritance etc and describe the columns using attributes.
here's an example of how simple your code would become.
IEnumerable<ManualInputFormat> MapFileToRows(Stream input)
{
var csvDescriptor = new CsvFileDescription
{
SeparatorChar = ',',
FirstLineHasColumnNames = true
};
var context = new CsvContext();
return context.Read<InputFormat>(new StreamReader(input), csvDescriptor);
}
where InputFormat is your destination attribute decorated POCO
http://www.codeproject.com/Articles/25133/LINQ-to-CSV-library
I'm trying to implement the C# aspect of a LightWeight JSON Spec JsonR, but cannot get my head around any kind of recursion :-/ If anyone could help out here it would be more than greatly appreciated.
// Mockup class
public class User {
public string Name { get; set; }
public int Age { get; set; }
public List<string> Photos { get; set; }
public List<Friend> Friends { get; set; }
}
// Mockup class
public class Friend {
public string FirstName { get; set; }
public string LastName { get; set; }
}
// Initialize objects
var userList = new List<User>();
userList.Add(new User() {
Name = "Robert",
Age = 32,
Photos = new List<string> { "1.jpg", "2.jpg" },
Friends = new List<Friend>() {
new Friend() { FirstName = "Bob", LastName = "Hope"},
new Friend() { FirstName = "Mr" , LastName = "T"}
}
});
userList.Add(new User() {
Name = "Jane",
Age = 21,
Photos = new List<string> { "4.jpg", "5.jpg" },
Friends = new List<Friend>() {
new Friend() { FirstName = "Foo" , LastName = "Bar"},
new Friend() { FirstName = "Lady" , LastName = "Gaga"}
}
});
The idea behind it all is to now take the above object and split it into 2 separate collections, one containing the keys, and the other containing the values. Like this we can eventually only send the values over the wire thus saving lots of bandwidth, and then recombine it on the client (a js implementation for recombining exists already)
If all went well we should be able to get this out of the above object
var keys = new object[] {
"Name", "Age", "Photos",
new { Friends = new [] {"FirstName", "LastName"}}};
var values = new [] {
new object[] {"Robert", 32, new [] {"1.jpg", "2.jpg"},
new [] { new [] {"Bob", "Hope"},
new [] {"Mr", "T"}}},
new object[] {"Jane", 21, new [] {"4.jpg", "5.jpg"},
new [] { new [] {"Foo", "Bar"},
new [] {"Lady", "Gaga"}}}};
As a verification we can test the conformity of the result with
Newtonsoft.Json.JsonConvert.SerializeObject(keys).Dump("keys");
// Generates:
// ["Name","Age","Photos",{"Friends":["FirstName","LastName"]}]
Newtonsoft.Json.JsonConvert.SerializeObject(values).Dump("values");
// Generates:
// [["Robert",32,["1.jpg","2.jpg"],[["Bob","Hope"],["Mr","T"]]],["Jane",21,["4.jpg","5.jpg"],[["Foo","Bar"],["Lady","Gaga"]]]]
A shortcut i explored was to take advantage of Newton's JArray/JObject facilities like this
var JResult = Newtonsoft.Json.JsonConvert.DeserializeObject(
Newtonsoft.Json.JsonConvert.SerializeObject(userList));
Like this we end up with a sort of array object that we can already start iterating on
Anyone think they can crack this in a memory/speed efficient way ?
I have a solution that works with your example data. It is not a universal solution and may fail with other examples, but it shows how to use recursions. I did not include any error handling. A real-world solution would have to.
I use this helper method which gets the item type of the generic lists:
private static Type GetListItemType(Type listType)
{
Type itemType = null;
foreach (Type interfaceType in listType.GetInterfaces()) {
if (interfaceType.IsGenericType &&
interfaceType.GetGenericTypeDefinition() == typeof(IList<>)) {
itemType = interfaceType.GetGenericArguments()[0];
break;
}
}
return itemType;
}
Now, the recursion:
public void SplitKeyValues(IList source, List<object> keys, List<object> values)
{
Type itemType = GetListItemType(source.GetType());
PropertyInfo[] properties = itemType.GetProperties();
for (int i = 0; i < source.Count; i++) {
object item = source[i];
var itemValues = new List<object>();
values.Add(itemValues);
foreach (PropertyInfo prop in properties) {
if (typeof(IList).IsAssignableFrom(prop.PropertyType) &&
prop.PropertyType.IsGenericType) {
// We have a List<T> or array
Type genericArgType = GetListItemType(prop.PropertyType);
if (genericArgType.IsValueType || genericArgType == typeof(string)) {
// We have a list or array of a simple type
if (i == 0)
keys.Add(prop.Name);
List<object> subValues = new List<object>();
itemValues.Add(subValues);
subValues.AddRange(
Enumerable.Cast<object>(
(IEnumerable)prop.GetValue(item, null)));
} else {
// We have a list or array of a complex type
List<object> subKeys = new List<object>();
if (i == 0)
keys.Add(subKeys);
List<object> subValues = new List<object>();
itemValues.Add(subValues);
SplitKeyValues(
(IList)prop.GetValue(item, null), subKeys, subValues);
}
} else if (prop.PropertyType.IsValueType ||
prop.PropertyType == typeof(string)) {
// We have a simple type
if (i == 0)
keys.Add(prop.Name);
itemValues.Add(prop.GetValue(item, null));
} else {
// We have a complex type.
// Does not occur in your example
}
}
}
}
I call it like this:
List<User> userList = InitializeObjects();
List<object> keys = new List<object>();
List<object> values = new List<object>();
SplitKeyValues(userList, keys, values);
InitializeObjects initializes the user list as you did above.
UPDATE
The problem is that you are using an anonymous type new { Friends = ... }. You would have to create an anonymous type dynamically by using reflection. And that's pretty nasty. The article "Extend Anonymous Types using Reflection.Emit" seems to do it. (I didn't test it).
Maybe an easier approach would do the job. I suggest creating a helper class for the description of class types.
public class Class
{
public string Name { get; set; }
public List<object> Structure { get; set; }
}
Now let's replace an else case in the code above:
...
} else {
// We have a list or array of a complex type
List<object> subKeys = new List<object>();
var classDescr = new Class { Name = genericArgType.Name, Structure = subKeys };
if (i == 0)
keys.Add(classDescr);
List<object> subValues = new List<object>();
itemValues.Add(subValues);
SplitKeyValues(
(IList)prop.GetValue(item, null), subKeys, subValues);
}
...
The result is:
You may want to try an external tool, like AutoMapper, which is a convention based mapping library.
I suggest to try the flattening feature with your own conventions.
I'm sorry I cannot write out an example because of lack of time, but I think the ideas behind this open source library might help a lot.
Automapper easily handles mapping one list of object types to another list of different objects types, but is it possible to have it map to an existing list using an ID as a key?
I have not found better way than the following.
Here are source and destination.
public class Source
{
public int Id { get; set; }
public string Foo { get; set; }
}
public class Destination
{
public int Id { get; set; }
public string Foo { get; set; }
}
Define converter (You should change List<> to whatever type you are using).
public class CollectionConverter: ITypeConverter<List<Source>, List<Destination>>
{
public List<Destination> Convert(ResolutionContext context)
{
var destinationCollection = (List<Destination>)context.DestinationValue;
if(destinationCollection == null)
destinationCollection = new List<Destination>();
var sourceCollection = (List<Source>)context.SourceValue;
foreach(var source in sourceCollection)
{
Destination matchedDestination = null;
foreach(var destination in destinationCollection)
{
if(destination.Id == source.Id)
{
Mapper.Map(source, destination);
matchedDestination = destination;
break;
}
}
if(matchedDestination == null)
destinationCollection.Add(Mapper.Map<Destination>(source));
}
return destinationCollection;
}
}
And here is actual mapping configuration and example.
Mapper.CreateMap<Source,Destination>();
Mapper.CreateMap<List<Source>,List<Destination>>().ConvertUsing(new CollectionConverter());
var sourceCollection = new List<Source>
{
new Source{ Id = 1, Foo = "Match"},
new Source{ Id = 2, Foo = "DoesNotMatchWithDestination"}
};
var destinationCollection = new List<Destination>
{
new Destination{ Id = 1, Foo = "Match"},
new Destination{ Id = 3, Foo = "DoeNotMatchWithSource"}
};
var mergedCollection = Mapper.Map(sourceCollection, destinationCollection);
You should get the following result.
I found this article very useful and as such I thought I would feed back in my generic version of the type converter which you can use to select the property to match on from each object.
Using it all you need to do is:
// Example of usage
Mapper.CreateMap<UserModel, User>();
var converter = CollectionConverterWithIdentityMatching<UserModel, User>.Instance(model => model.Id, user => user.Id);
Mapper.CreateMap<List<UserModel>, List<User>>().ConvertUsing(converter);
//The actual converter
public class CollectionConverterWithIdentityMatching<TSource, TDestination> :
ITypeConverter<List<TSource>, List<TDestination>> where TDestination : class
{
private readonly Func<TSource, object> sourcePrimaryKeyExpression;
private readonly Func<TDestination, object> destinationPrimaryKeyExpression;
private CollectionConverterWithIdentityMatching(Expression<Func<TSource, object>> sourcePrimaryKey, Expression<Func<TDestination, object>> destinationPrimaryKey)
{
this.sourcePrimaryKeyExpression = sourcePrimaryKey.Compile();
this.destinationPrimaryKeyExpression = destinationPrimaryKey.Compile();
}
public static CollectionConverterWithIdentityMatching<TSource, TDestination>
Instance(Expression<Func<TSource, object>> sourcePrimaryKey, Expression<Func<TDestination, object>> destinationPrimaryKey)
{
return new CollectionConverterWithIdentityMatching<TSource, TDestination>(
sourcePrimaryKey, destinationPrimaryKey);
}
public List<TDestination> Convert(ResolutionContext context)
{
var destinationCollection = (List<TDestination>)context.DestinationValue ?? new List<TDestination>();
var sourceCollection = (List<TSource>)context.SourceValue;
foreach (var source in sourceCollection)
{
TDestination matchedDestination = default(TDestination);
foreach (var destination in destinationCollection)
{
var sourcePrimaryKey = GetPrimaryKey(source, this.sourcePrimaryKeyExpression);
var destinationPrimaryKey = GetPrimaryKey(destination, this.destinationPrimaryKeyExpression);
if (string.Equals(sourcePrimaryKey, destinationPrimaryKey, StringComparison.OrdinalIgnoreCase))
{
Mapper.Map(source, destination);
matchedDestination = destination;
break;
}
}
if (matchedDestination == null)
{
destinationCollection.Add(Mapper.Map<TDestination>(source));
}
}
return destinationCollection;
}
private string GetPrimaryKey<TObject>(object entity, Func<TObject, object> expression)
{
var tempId = expression.Invoke((TObject)entity);
var id = System.Convert.ToString(tempId);
return id;
}
}