With the help of everyone in a very short time. The problem got solved by overriding the toString Method.
I have a problem with the following: (solved)
public class CryptoApiResponse
{
[DeserializeAs(Name = "ticker")]
public List<CryptoAttributes> CryptoCurrency { get; set; }
public override string ToString()
{
return $"Currency:{CryptoCurrency[0].Currency} " +
$"PriceFiat:{CryptoCurrency[0].PriceFiat} " +
$"Fiat:{CryptoCurrency[0].TargetFiat}";
}
}
public class CryptoAttributes
{
[DeserializeAs(Name = "base")]
public string Currency { get; set; }
[DeserializeAs(Name = "target")]
public string TargetFiat { get; set; }
[DeserializeAs(Name = "price")]
public string PriceFiat { get; set; }
}
And I want to access the following:
public void Display<CryptoApiResponse>(List<CryptoApiResponse> apiList)
{
if (apiList != null)
{
foreach (CryptoApiResponse cryptoCurrency in apiList)
{
Console.WriteLine(cryptoCurrency.ToString());
}
}
Console.ReadLine();
}
Console.WriteLine(obj);
// this means more or less the following
Console.WriteLine(obj.ToString());
// this means you should override the ToString() method
// or to make a custom string
You're iterating through a List, and in each crypto there exist a sub-list List. In short you get List>.
When you foreach this List, you may need to use a second foreach to iterate the values in the Sub list to reach your property.
foreach (var crypt in crypto)
{
foreach (var basedata in crypt.Ticker)
{
Console.WriteLine($"Currency:{basedata.Currency} Price: {basedata.Price} Target: {basedata.Target}");
}
}
If you keep the naming of the API you linked and differentiate between lists ans single objects names it will be easier to understand what is the problem. The classes should look something like this (pay attention to the difference between Ticker and Tickers
public class Crypto
{
public List<Ticker> Tickers { get; set; }
}
public class Ticker
{
public string Currency { get; set; }
public string Target { get; set; }
public string Price { get; set; }
}
The parameter crypto (should be cryptos) in Display is a list and Tickers is a list, so you need nested loop. You should also remove the Crypto parameter from the methos signature as it hides the Crypto class
public void Display(List<Crypto> cryptos)
{
foreach (Crypto crypto in cryptos)
{
foreach (Ticker ticker in crypto.Tickers)
{
Console.WriteLine(ticker);
}
}
}
Or if you would like to use partial Linq
public void Display(List<Crypto> cryptos)
{
foreach (Ticker ticker in cryptos.SelectMany(crypto => crypto.Tickers))
{
Console.WriteLine(ticker);
}
}
Could you try to use "Crypto" instead of "var" when looping? I mean do it like this. I remeber the version before VS2015 (may be VS2010), the type of variable will be treated as object if we use "var".
public void Display<Crypto>(List<Crypto> crypto)
{
if (crypto != null)
{
// Currency, Target and Price
foreach (***Crypto*** ticker in crypto)
{
Console.WriteLine(ticker); // ticker Type Crypo
// ticker.Ticker
}
}
Console.ReadLine();
}
Related
I have three classes:
public class M2ArticleMain
{
public int Id { get; set; }
public List<M2ArticleAttributeWeb> Attribut_Web { get; set; }
}
public class M2ArticleAttributeWeb
{
public int Web_Id { get; set; }
public M2ArticleTmpMainSkus Variants { get; set; }
}
public class M2ArticleTmpMainSkus
{
public DateTime TimeAdded { get; set; }
public List<string> Skus { get; set; }
}
And I have two Lists in my code like this:
List<M2ArticleMain> data = new List<M2ArticleMain>();
List<M2ArticleAttributeWeb> attb = new List<M2ArticleAttributeWeb>();
In some part of my code firstly I (from foreach loop) add data to attb list where I add only only some data (because I don't have all data at this point), like this:
...
attb.Add(new M2ArticleAttributeWeb
{
Web_id = item.Id, //(item is from foreach loop)
Variants = null //this is **importat**, I left null for later to add it
});
Next, after I fill attb, I add all this to data list:
...
data.Add(new M2ArticleMain
{
Id = item.Id_Pk, //this is also from foreach loop,
Attribut_Web = attb //now in this part I have only data for Web_id and not Variants
}
Now my question is How to Add items later to data list to object Variants?
Something like this:
data.AddRange( "how to point to Variants" = some data);
The M2ArticleAttributeWeb type holding your Variants property is the member of a collection. That is, there are potentially many of them. You can reference an individual Variants property like this:
data[0].Attribut_Web[0].Variants
But you need to know which items you want to add map to which data and Attribut_Web indexes/objects in order to assign them properly. That probably means another loop, or even a nested loop. That is, you can see all of your Variants properties in a loop like this:
foreach(var main in data)
{
foreach(var attrw in main)
{
var v = attrw.Variants;
// do something with v
Console.WriteLine(v);
// **OR**
attrw.Variants = // assign some object
}
}
It's also much better practice to create your collection properties with the object, and then give them private set attributes:
public class M2ArticleMain
{
public int Id { get; set; }
public List<M2ArticleAttributeWeb> Attribut_Web { get; private set; } = new List<M2ArticleAttributeWeb>();
}
public class M2ArticleAttributeWeb
{
public int Web_Id { get; set; }
public M2ArticleTmpMainSkus Variants { get; set; }
}
public class M2ArticleTmpMainSkus
{
public DateTime TimeAdded { get; set; }
public List<string> Skus { get; private set; } = new List<string>();
}
Now instead of assigning Attribut_Web = attb, you would need to .Add() to the existing List.
I recently came across a piece of code at work that has a repeating if-else condition that checks on an enum called OperationType :
public enum OperationType
{ A, B }
Right now the class's job is to run an operation either on device A or on device B, while reading from a SharedDevice and store some values basically for an X,Y plot. We record the characteristics of the SharedDevice in the function of DeviceA or DeviceB. The problem is that we need to iterate over a list of different parameters and send them to the SharedDevice. This list is different for device A and for device B.
Device class:
public class Device
{
public double CurrentValue { get; }
public DeviceParameters Parameters { get; set; }
}
And here is the class responsible for executing this operation:
public class MyOperationExecuter
{
public Device SharedDevice { get; }
public Device DeviceA { get; }
public Device DeviceB { get; }
public List<DeviceParameters> ParametersA { get; }
public List<DeviceParameters> ParametersB { get; }
public List<double> XValuesOfA { get; }
public List<double> YValuesOfA { get; }
public List<double> XValuesOfB { get; }
public List<double> YValuesOfB { get; }
public void DoMyOperation(OperationType operationType)
{
List<DeviceParameters> changingDeviceParameters;
if (operationType == OperationType.A)
{
changingDeviceParameters = ParametersA;
}
else
{
changingDeviceParameters = ParametersB;
}
if (operationType == OperationType.A)
{
XValuesOfA.Clear();
YValuesOfA.Clear();
}
else
{
XValuesOfB.Clear();
YValuesOfB.Clear();
}
foreach (var parameters in changingDeviceParameters)
{
// set the device parameters
SharedDevice.Parameters = parameters;
// retrieve the device readings and store the values in the correct dataprovider
if (operationType == OperationType.A)
{
XValuesOfA.Add(DeviceA.CurrentValue);
YValuesOfA.Add(SharedDevice.CurrentValue));
}
else
{
XValuesOfB.Add(DeviceB.CurrentValue);
YValuesOfB.Add(SharedDevice.CurrentValue);
}
}
// save updated x,y data
Save();
}
}
As you can see there is a repeating if statement which is not very future proof, since we have to check for the enum in every single step. Also we might need to add an C-type device which would result in an ever growing switch statement. We might also need to execute operations on both A and B. How should I refactor this operation so I can keep extending it without this always repeating if-else logic?
A fairly simple way would be to declare a variable representing A or B:
var XValues = operationType == OperationType.A ? XValuesOfA : XValuesOfB;
then you can just use XValues. Do the same for DeviceA. If you have more operations you could use a switch expression.
A neater solution would be to make separate objects containing everything needed for A or B, so your class could simply check the operation type and then delegate all the work to respective object. I.e.
public class MyDevice
{
public Device SharedDevice { get; }
public Device Device { get; }
public List<DeviceParameters> Parameters { get; }
public List<double> XValuesOf { get; }
public List<double> YValuesOf { get; }
public void DoMyOperation()
{
...
}
}
I would also recommend using a single list containing both X and Y values, something like a Vector2. I find this easier to use, and helps avoid repeating code.
Without changing class fields/properties I'd go with new method:
private void SetParameters(List<DeviceParameters> parameters, List<double> xValues, List<double> yValues, Device device)
{
xValues.Clear();
yValues.Clear();
foreach(var parameter in parameters)
{
SharedDevice.Parameters = parameter;
xValues.Add(device.CurrentValue);
yValues.Add(SharedDevice.CurrentValue);
}
}
And then in DoMyOperation it's enough to:
if (operationType == OperationType.A)
{
SetParameter(ParametersA, XValuesOfA, YValuesOfA, DeviceA);
}
else
{
SetParameter(ParametersB, XValuesOfB, YValuesOfB, DeviceB);
}
You should add new class. Which will be used to define device type specific properties.
A class like this;
public class MyDeviceValues
{
public MyDeviceValues(List<DeviceParameters> parameters, List<double> xValuesOf, List<double> yValuesOf)
{
Parameters = parameters;
XValues = xValuesOf;
YValues = yValuesOf;
}
public List<DeviceParameters> Parameters { get; }
public List<double> XValues { get; }
public List<double> YValues { get; }
}
So, you can have a generic DoMyOperation function. It will be like this:
public void DoMyOperation(MyDeviceValues myDeviceValues)
{
var changingDeviceParameters = myDeviceValues.Parameters;
myDeviceValues.XValues.Clear();
myDeviceValues.YValues.Clear();
foreach (var parameters in changingDeviceParameters)
{
// set the device parameters
SharedDevice.Parameters = parameters;
// retrieve the device readings and store the values in the correct dataprovider
myDeviceValues.XValues.Add(DeviceA.CurrentValue);
myDeviceValues.YValues.Add(SharedDevice.CurrentValue);
}
// save updated x,y data
Save();
}
Here is the refactored version of the whole code you pasted:
https://dotnetfiddle.net/dLyJl9
I'm basically trying to use reflection to flatten any class into a dictionary so that I can generically use and bind them in Blazor. I then need to be able to create an instance of the class and populate it with the data from the dictionary (which will have been updated by a component).
e.g
public class Order
{
public Guid Id { get; set; }
public Customer Customer { get; set; }
public string Address { get; set; }
public string Postcode { get; set; }
public List<string> Test { get; set; }
public List<Test> Test2 { get; set; }
}
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName => $"{FirstName} {LastName}";
public Gender Gender { get; set; }
public List<string> Test { get; set; }
}
Should become:
{
"Id": "",
"Customer.FirstName": "",
"Customer.LastName": "",
"Customer.Gender": "",
"Customer.Test": "",
"Address": "",
"Postcode": "",
"Test": "",
"Test2": ""
}
For some reason when I iterate the properties of the Order class, Test2 is missed. The loop shows the property in the collection when I put a breakpoint, it just seems to skip it. I've never seen this happen before.
Code: https://dotnetfiddle.net/g1qyVQ
I also don't think the current code with handle further nested depth which I would like it to be able to work with any POCO object really.
Also if anyone knows a better way to do what I'm trying, I would love to find an easier way. Thanks
First of all, good job on linking the code sample. Without that, I would have passed by this question in about three seconds. :D
In GetAllProperties(), your entire loop is inside a giant try catch block, where the catch returns the dictionary as it is so far, without checking what the exception is. So if you don't get everything you expect, you've probably hit an error.
Amend the catch block:
catch (Exception ex) { Console.WriteLine(ex.ToString()); return result; }
Now, you can see the problem:
System.ArgumentException: An item with the same key has already been added. Key: Test
Your object has more than one property named "Test," but Keys in a Dictionary must be unique.
Summary: Errors aren't the enemy, they're your best friend. Don't use try / catch to bypass errors. If you do, you may get "mysterious, never seen that happen before!" results.
For anyone interested, here is where I'm at now:
https://dotnetfiddle.net/3ORKNs
using JsonFlatten;
using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.Json;
namespace RecursiveClassProperties
{
public static class Program
{
static void Main(string[] args)
{
var item = CreateDefaultItem(typeof(Order));
Console.WriteLine(JsonSerializer.Serialize(item, new JsonSerializerOptions { WriteIndented = true }));
var json = JsonSerializer.Serialize(item);
var properties = JObject.Parse(json).Flatten();
Console.WriteLine(JsonSerializer.Serialize(properties, new JsonSerializerOptions { WriteIndented = true }));
var formProperties = properties.ToDictionary(x => x.Key, x => new FormResponse(string.Empty));
Console.WriteLine(JsonSerializer.Serialize(formProperties, new JsonSerializerOptions { WriteIndented = true }));
}
private static object CreateFormItem(Type type, Dictionary<string, FormResponse> formProperties, object result = null)
{
result = CreateDefaultItem(type);
return result;
}
private static object CreateDefaultItem(Type type, object result = null, object nested = null, bool isBase = false)
{
void SetProperty(PropertyInfo property, object instance)
{
if (property.PropertyType == typeof(string)) property.SetValue(instance, string.Empty);
if (property.PropertyType.IsEnum) property.SetValue(instance, 0);
if (property.PropertyType == typeof(Guid)) property.SetValue(instance, Guid.Empty);
}
if (result is null)
{
result = Activator.CreateInstance(type);
isBase = true;
}
var properties = type.GetProperties();
foreach (var property in properties)
{
if (!Attribute.IsDefined(property, typeof(FormIgnoreAttribute)) && property.GetSetMethod() is not null)
{
if (property.PropertyType == typeof(string) || property.PropertyType.IsEnum || property.PropertyType == typeof(Guid))
{
if (isBase) SetProperty(property, result);
else if (nested is not null && nested.GetType() is not IList && !nested.GetType().IsGenericType) SetProperty(property, nested);
}
else
{
var _nested = default(object);
if (isBase)
{
property.SetValue(result, Activator.CreateInstance(property.PropertyType));
_nested = property.GetValue(result);
}
if (nested is not null)
{
property.SetValue(nested, Activator.CreateInstance(property.PropertyType));
_nested = property.GetValue(nested);
}
CreateDefaultItem(property.PropertyType, result, _nested);
}
}
}
return result;
}
}
[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
public class FormIgnoreAttribute : Attribute { }
public class FormResponse
{
public FormResponse(string value) => Value = value;
public string Value { get; set; }
}
public class Order
{
public Guid Id { get; set; }
public Customer Customer { get; set; }
public string Address { get; set; }
public string Postcode { get; set; }
public Test Test { get; set; }
public List<Gender> Genders { get; set; }
public List<string> Tests { get; set; }
}
public enum Gender
{
Male,
Female
}
public class Test
{
public string Value { get; set; }
public List<Gender> Genders { get; set; }
public List<string> Tests { get; set; }
}
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName => $"{FirstName} {LastName}";
public Gender Gender { get; set; }
public Test Test { get; set; }
public List<Gender> Genders { get; set; }
public List<string> Tests { get; set; }
}
}
The idea is that I can assign values to formProperties, pass it to CreateFormItem() and get a populated object back. The reason I'm doing this is because I have a Blazor component Table which has a typeparam TItem, basically think of it as Table<TItem> for those unfamiliar with Blazor. The table is then supplied a list of objects which it can then render.
Flattening the object in this way will both allow me to easily display all properties and subproperties of the class in the table, but most importantly bind the input of a "new item" form which will return the new object to a delegate outside of the component (back in normal .NET) to submit to a creation controller (to put it in the DB). The reason having a Dictionary<string, FormResponse> is important is that with a generic type, you aren't able to bind the input of the form to the "model". You are however able to bind the input to a string property of a class, even if it's not a string. Hence FormResponse.Value.
I will next need to have CreateFormItem() return the object with the actual data from the form. Sorry if this is a bit longwinded, couldn't think of a more concise way to explain it.
Thanks :)
I have an abstract class that looks like so:
public abstract class PageObjectsBase
{
public abstract string FriendlyName { get; }
public abstract string PageObjectKeyPrefix { get; }
public abstract string CollectionProperty { get; }
}
And a class that derives from PageObjectsBase:
public class PageRatingList : PageObjectsBase
{
public IList<PageRating> PageRatings { get; set; }
public PageRatingList()
{
this.PageRatings = new List<PageRating>();
}
public override string CollectionProperty
{
get
{
var collectionProperty = typeof(PageRatingList).GetProperties().FirstOrDefault(p => p.Name == "PageRatings");
return (collectionProperty != null) ? collectionProperty.Name : string.Empty;
}
}
public override string FriendlyName
{
get
{
return "Page feedback/rating";
}
}
public override string PageObjectKeyPrefix
{
get
{
return "pagerating-";
}
}
}
And a PageRating class which PageRatingList.PageRatings is holding a collection of:
public class PageRating : PageObjectBase
{
public int Score { get; set; }
public string Comment { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
The PageRatingList is being stored in a database (EPiServer's Dynamic Data Store, more specifically using the Page Object Manager). I need to create some reporting functionality and am essentially loading all reports that derive from PageObjectBase. When it comes to returning the data, the code will never know at compile time what type of data it is to load, so I am using Reflection. In my reporting class I have:
//this gives me the right type
var type = Type.GetType("MyNameSpace.PageRatingList", true);
var startPageData = this._contentRepository.Get<PageData>(startPage);
PageObjectManager pageObjectManager = new PageObjectManager(startPageData);
//this loads the instances from the DB
var props = pageObjectManager.LoadAllMetaObjects()
.FirstOrDefault(o => o.StoreName == "Sigma.CitizensAdvice.Web.Business.CustomEntity.PageRatingList");
//this gives me 4 PropertyInfo objects (IList: PageRatings, string : CollectionProperty, string :FriendlyName, string : PageObjectKeyPrefix)
var properties = props.Value.GetType().GetProperties();
I can then iterate through the PropertyInfo objects using:
foreach (var property in properties)
{
//extract property value here
}
The issue I am having is that I cannot figure out how to get the value of each of the propertyinfo objects. In addition, one of those properties is type List and again we wont know the type of T until runtime. So I also need some logic that checks if one of the PropertyInfo objects is of type List and then provides access to each of the properties in the List - the List being of type PageRating.
Can anyone help here? I've not really used reflection in the past so I am winging my way through it, rightly or wrongly!
Many thanks
Al
I may be missunderstanding the problem, but i think you may use something like this:
var props = new PageRatingList(); /*actual instanse of the object, in your case, i think "props.Value" */
var properties = typeof(PageRatingList).GetProperties();
foreach (var property in properties)
{
if (property.PropertyType == typeof(IList<PageRating>))
{
IList<PageRating> list = (IList<PageRating>)property.GetValue(props);
/* do */
}
else
{
object val = property.GetValue(props);
}
}
Hope this helps to find your solution.
I've been attempting to design a efficient interface that I'm using for some plugins. I thought I had found a decent interface but trying to implement it is not going well. So I was hoping to see if anyone out here has some better suggestions as to how this could be done. It errors out with "does not contain a public definition for 'GetEnumerator'"
Plugin interface:
namespace ALeRT.PluginFramework
{
public interface IQueryPlugin
{
string PluginCategory { get; }
string Name { get; }
string Version { get; }
string Author { get; }
System.Collections.Generic.List TypesAccepted { get; }
}
interface IQueryPluginRBool : IQueryPlugin
{
bool Result(string input, bool sensitive);
}
interface IQueryPluginRString : IQueryPlugin
{
string Result(string input, bool sensitive);
}
}
In essence I am attempting to take a list of types that should be used (types could be URL, Name, Email, IP, etc.) and compare them to the value in the query plugin. Each query plugin can possibly have multiple types it accepts. When they match, it performs the actions that are in the query plugin.
[ImportMany]
public IEnumerable<IQueryPlugin> QPlugins { get; set; }
private void QueryPlugins(List<string> val, bool sensitive)
{
foreach (string tType in val) //Cycle through a List<string>
{
foreach (var qPlugins in this.QPlugins) //Cycle through all query plugins
{
foreach (string qType in qPlugins) //Cycle though a List<string> within the IQueryPlugin interface AcceptedTypes
{
if (qType == tType) //Match the two List<strings>, one is the AcceptedTypes and the other is the one returned from ITypeQuery
{
//Do stuff here
}
}
}
}
}
Your code
foreach (string qType in qPlugins)
{
if (qType = tType)
{
//Do stuff here
}
}
Will not work. You must iterate through qPlugins.TypeAccepted
First of all. Do not expose a list (like the line below) since it violates Law Of Demeter. That means that the plugin do not how control over it's own list. Anyone that have a reference to the plugin can modify the list.
System.Collections.Generic.List TypesAccepted { get; }
This is better:
IEnumerable<TheType> TypesAccepted { get; }
But that still let's anyone modify the elements of the list (without the knowledge of the plugin). It's fine if the elements are immutable.
A better solution would be to create methods in the plugin interface. For instance have a visitor pattern method:
public interface IPluginTypeVisitor
{
void Visit(AcceptedType type);
}
public interface IQueryPlugin
{
string PluginCategory { get; }
string Name { get; }
string Version { get; }
string Author { get; }
void VisitTypes(IPluginTypeVisitor visitor);
}
But the best solution in the case of your loop example is simply:
public interface IQueryPlugin
{
string PluginCategory { get; }
string Name { get; }
string Version { get; }
string Author { get; }
bool IsTypeAcceptable(TheTypeType type); // get it, thetypetype? hahaha
}
private void QueryPlugins(List<string> val, bool sensitive)
{
foreach (string tType in val) //Cycle through a List<string>
{
foreach (var plugin in this.QPlugins) //Cycle through all query plugins
{
if (plugin.IsTypeAcceptable(tType))
//process it here
}
}
}