Make names of named tuples appear in serialized JSON responses - c#

Situation: I have multiple Web service API calls that deliver object structures. Currently, I declare explicit types to bind those object structures together. For the sake of simplicity, here's an example:
[HttpGet]
[ProducesResponseType(typeof(MyType), 200)]
public MyType TestOriginal()
{
return new MyType { Speed: 5.0, Distance: 4 };
}
Improvement: I have loads of these custom classes like MyType and would love to use a generic container instead. I came across named tuples and can successfully use them in my controller methods like this:
[HttpGet]
[ProducesResponseType(typeof((double speed, int distance)), 200)]
public (double speed, int distance) Test()
{
return (speed: 5.0, distance: 4);
}
Problem I am facing is that the resolved type is based on the underlying Tuple which contains these meaningless properties Item1, Item2 etc. Example:
Question: Has anyone found a solution to get the names of the named tuples serialized into my JSON responses? Alternatively, has anyone found a generic solution that allows to have a single class/representation for random structures that can be used so that the JSON response explicitly names what it contains.

Problem with using named tuples in your case is that they are just syntactic sugar.
If you check named-and-unnamed-tuples documentation you will find part:
These synonyms are handled by the compiler and the language so that
you can use named tuples effectively. IDEs and editors can read these
semantic names using the Roslyn APIs. You can reference the elements
of a named tuple by those semantic names anywhere in the same
assembly. The compiler replaces the names you've defined with Item*
equivalents when generating the compiled output. The compiled
Microsoft Intermediate Language (MSIL) does not include the names
you've given these elements.
So you have problem as you do your serialization during runtime, not during compilation and you would like to use the information which was lost during compilation. One could design custom serializer which gets initialized with some code before compilation to remember named tuple names but I guess such complication is too much for this example.

For serializing response just use any custom attribute on action and custom contract resolver (this is only solution, unfortunately, but I'm still looking for any more elegance one).
Attribute:
public class ReturnValueTupleAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
var content = actionExecutedContext?.Response?.Content as ObjectContent;
if (!(content?.Formatter is JsonMediaTypeFormatter))
{
return;
}
var names = actionExecutedContext
.ActionContext
.ControllerContext
.ControllerDescriptor
.ControllerType
.GetMethod(actionExecutedContext.ActionContext.ActionDescriptor.ActionName)
?.ReturnParameter
?.GetCustomAttribute<TupleElementNamesAttribute>()
?.TransformNames;
var formatter = new JsonMediaTypeFormatter
{
SerializerSettings =
{
ContractResolver = new ValueTuplesContractResolver(names),
},
};
actionExecutedContext.Response.Content = new ObjectContent(content.ObjectType, content.Value, formatter);
}
}
ContractResolver:
public class ValueTuplesContractResolver : CamelCasePropertyNamesContractResolver
{
private IList<string> _names;
public ValueTuplesContractResolver(IList<string> names)
{
_names = names;
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var properties = base.CreateProperties(type, memberSerialization);
if (type.Name.Contains(nameof(ValueTuple)))
{
for (var i = 0; i < properties.Count; i++)
{
properties[i].PropertyName = _names[i];
}
_names = _names.Skip(properties.Count).ToList();
}
return properties;
}
}
Usage:
[ReturnValueTuple]
[HttpGet]
[Route("types")]
public IEnumerable<(int id, string name)> GetDocumentTypes()
{
return ServiceContainer.Db
.DocumentTypes
.AsEnumerable()
.Select(dt => (dt.Id, dt.Name));
}
This one returns next JSON:
[
{
"id":0,
"name":"Other"
},
{
"id":1,
"name":"Shipping Document"
}
]
Here the solution for Swagger UI:
public class SwaggerValueTupleFilter : IOperationFilter
{
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
var action = apiDescription.ActionDescriptor;
var controller = action.ControllerDescriptor.ControllerType;
var method = controller.GetMethod(action.ActionName);
var names = method?.ReturnParameter?.GetCustomAttribute<TupleElementNamesAttribute>()?.TransformNames;
if (names == null)
{
return;
}
var responseType = apiDescription.ResponseDescription.DeclaredType;
FieldInfo[] tupleFields;
var props = new Dictionary<string, string>();
var isEnumer = responseType.GetInterface(nameof(IEnumerable)) != null;
if (isEnumer)
{
tupleFields = responseType
.GetGenericArguments()[0]
.GetFields();
}
else
{
tupleFields = responseType.GetFields();
}
for (var i = 0; i < tupleFields.Length; i++)
{
props.Add(names[i], tupleFields[i].FieldType.GetFriendlyName());
}
object result;
if (isEnumer)
{
result = new List<Dictionary<string, string>>
{
props,
};
}
else
{
result = props;
}
operation.responses.Clear();
operation.responses.Add("200", new Response
{
description = "OK",
schema = new Schema
{
example = result,
},
});
}

Make use of anonymous object instead.
(double speed, int distance) = (5.0, 4);
return new { speed, distance };

You have a little bid conflicting requirements
Question:
I have loads of these custom classes like MyType and would love to use
a generic container instead
Comment:
However, what type would I have to declare in my ProducesResponseType
attribute to explicitly expose what I am returning
Based on above - you should stay with types you already have. Those types provide valuable documentation in your code for other developers/reader or for yourself after few months.
From point of readability
[ProducesResponseType(typeof(Trip), 200)]
will be better then
[ProducesResponseType(typeof((double speed, int distance)), 200)]
From point of maintainability
Adding/removing property need to be done only in one place. Where with generic approach you will need to remember update attributes too.

The simplest solution is using dynamic code, i.e. C#'s ExpandoObject to wrap your response in the format you expect the API to have
public JsonResult<ExpandoObject> GetSomething(int param)
{
var (speed, distance) = DataLayer.GetData(param);
dynamic resultVM = new ExpandoObject();
resultVM.speed= speed;
resultVM.distance= distance;
return Json(resultVM);
}
The return type of "GetData" is
(decimal speed, int distance)
This gives a Json response in the way you expect it to

Related

Ensuring anonymous objects in C# all have certain common properties

I have a problem regarding anonymous objects in C#. The situation is as follows:
I have a C# web app which does NOT use the traditional ASP.NET Razor engine, but instead uses the RazorEngine open source project (https://github.com/Antaris/RazorEngine). I'm not sure if this is relevant, but it could be.
I'm passing a model object to each page I'm displaying. Each model object is different, there are many pages and therefore many different model objects, but I would rather not have to declare separate classes for each model, which is why I've been using anonymous classes:
// In method which displays page A:
var model = new {
Lang = _lang,
PropX = "foo",
PropY = "bar"
};
RazorEngine.Run("templateA", model);
// In a different method which displays page B:
var model = new {
Lang = _lang,
PropZ = "smu"
};
RazorEngine.Run("templateB", model);
You may notice that botn (and in fact, all) those models have a common property (the "Lang" property), a few common properties actually (Lang is the only one displayed in the example above to simplify matters).
My main problem is that I'm trying to ensure that those properties are added to all the models in a way which guarantees that they are included to all pages, and if I later decide to add a new common property, then I can do that in a single place.
One way would of course be to drop the anonymous classes, and use typed classes which all inherit from a single base class, which would declare the common properties. But this would be a lot of boilerplate code, and if there is another solution then I would prefer that.
Another solution would be to either declare the common properties in a sub-property of the model object, or declare the individual page properties in a sub object:
// Either like this:
var model = new {
Common = GetCommonModelProperties(),
PropX = "foo",
PropY = "bar"
};
public object GetCommonModelProperties()
{
return new {
Lang = _lang
};
}
// etc.
// or like this:
var pageModel = new {
PropX = "foo",
PropY = "bar
};
var model = CreateModel(pageModel);
RazorEngine.Run("templateA", model);
// where CreateModel could be implemented like this:
public object CreateModel(object pageModel)
{
return new
{
Lang = _lang,
// etc., whatever common properties there exist
Data = pageModel
};
}
The problem with this approach is that I would have to modify all my templates, either all instances where those pages refer to the common property (I would have to rename all Model.Lang instances to Model.Common.Lang), or to the individual page data (modify Model.AnyProperty to Model.Data.AnyProperty). Of course there is a great risk of errors when such a rewrite takes place.
So: is there a way to create an anonymous object, where a number of its properties are always the same, but the rest can be specified dynamically?
I've tried to create two separate objects, and then combine them into one, using code from this question: Merging anonymous types
var commonModel = new {
Lang = _lang
};
var pageModel = new {
PropX = "foo",
PropY = "bar"
};
var model = Merge(commonModel, pageModel);
and yes, this works. Until I have to use the Lang object, which is of a class type (which I have full control over), and this class overloads operator[]. If I use this workaround, the overload stops working, and I get the error:
Cannot apply indexing with [] to an expression of type 'object'
N.b. the indexing works perfectly fine if I just include the Lang property in a regular anonymous object.
I've also tried to create a separate base class for all the models, declare all the common properties in that class, but also derive it from System.Dynamic.DynamicObject, and override the TryGetMember method which would dynamically look up the page properties from a dictionary (which would work, since those properties are usually simple objects, i.e. they don't override the indexing operator, so I can add those properties dynamically at runtime:
var pageModel = new {
PropX = "foo",
PropY = "bar"
};
var model = CreateMainModel(pageModel);
public object CreateMainModel(object pageModel)
{
var mainModel = new BaseModel()
mainModel.Lang = _lang;
foreach (System.Reflection.PropertyInfo fi in pageModel.GetType().GetProperties())
{
mainModel.PageProperties[fi.Name] = fi.GetValue(pageModel, null);
}
return mainModel;
}
class BaseModel : DynamicObject
{
public LanguageMap Lang { get; set; }
public Dictionary<string, object> PageProperties { get; set; }
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (PageProperties.ContainsKey(binder.Name))
{
result = PageProperties[binder.Name];
return true;
}
return false;
}
}
The problem with this is that properties declared inside a class which derives from DynamicObject will NOT be visible inside the page templates, it seems that only properties returned from TryGetMember are. And if I make the members visible the explicity checking for their names inside TryGetMember, then the indexing stops working just like in the case above.
Now if this was C++, I could create a preprocessor macro:
#define COMMON_MODEL_PROPERTIES \
Lang = _lang
More = _otherProperty
// Where models are declared:
var model = new
{
COMMON_MODEL_PROPERTIES,
PropX = "foo",
PropY = "bar"
}
but this isn't C++... It's C#.
Any ideas?
Ok, so as per my initial comment... just wow. I would highly recommend that you bite the bullet and refactor to use static typing. To anyone else reading this, please do not do this.
However if you really really really really really really really really want to achieve this with minimal changes, I think this will work for you.
Add an extension method to your codebase:
internal static class DynamicCommonPropertyExtensions
{
public static object AddCommonProperties(this object obj)
{
dynamic expando = new ExpandoObject();
foreach (var prop in obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
if (expando is IDictionary<string, object?> dict)
{
dict[prop.Name] = prop.GetValue(obj, null);
}
}
//Add common properties here
expando.CommonProp1 = "CommonPropValue1";
expando.CommonProp2 = "CommonPropValue2";
return expando;
}
}
This can then be used as follows (with your example)
var model = new
{
Lang = _lang,
PropX = "foo",
PropY = "bar"
};
var addedCommonProperties = model.AddCommonProperties();
RazorEngine.Run("templateA", addedCommonProperties);
Or even "better" without using another variable
var model = new
{
Lang = "Test",
PropX = "foo",
PropY = "bar"
}.AddCommonProperties();
RazorEngine.Run("templateA", model);
If you need to access the properties before passing through you can declare it as dynamic
dynamic model = new
{
Lang = "Test",
PropX = "foo",
PropY = "bar"
}.AddCommonProperties();
var a = model.CommonProp1;
I now feel really dirty having written this

converting entities to viewmodels for use with web api

I'm currently struggling with finding a better way to populate my ViewModel objects with my Entitiy objects. I have the following Web Api controller method:
[HttpGet]
public IEnumerable<ClientSearchViewModel> FindClients(string query)
{
var clients = _clientService.SearchClient(query).ToList();
var returnClients = new List<ClientSearchViewModel>();
foreach (var client in clients)
{
returnClients.Add(new ClientSearchViewModel(client));
}
return returnClients;
}
And I'm doing this in my ClientSearchViewModel constructor:
public ClientSearchViewModel(Client client)
{
this.Id = client.Id;
this.FirstName = client.PersonName.FirstName;
this.LastName = client.PersonName.LastName;
}
Is there another way other than going through the list of returned objects and creating a new ViewModel list?
I strongly suggest use of a mapping plugin for this, such as:
AutoMapper
or
ValueInjector
Plugins like this will allow you to map between the objects being used internally or in your data layer, with your external objects (DTOs/ViewModels). They handle a number of things out of the box such as automatic mapping of any like named properties with the same type, but also allow for a lot of control in the specific mapping of properties or types, for those times when you need something more custom.
For a brief comparison of the two, there isn't much better than hearing the authors themselves respond: AutoMapper vs ValueInjecter
Personally, I find ValueInjector to be quicker to use, while having more control overall, but I also find it to be much less readable/inuitive than AutoMapper, which can require a bit more code to accomplish similar goals. As such, I'd pick the one that you find you and/or your team will prefer the syntax of and how easily you can grasp the concepts vs how much power you really need.
So I had the same miff... I can't say that I've benchmarked my solution, but it does seem to run reasonably fast...
3 bits:
public static T Transform<T>(this object convertFrom) where T : class, new()
{
return (T) (new ServiceExtension().Transform(convertFrom, typeof (T)));
}
private class ServiceExtension
{
public object Transform(object convertFrom, Type convertTo)
{
object _t = Activator.CreateInstance(convertTo);
if (convertFrom == null) return _t;
var convertType = convertFrom.GetType();
foreach (
var property in _t.GetType().GetProperties().Where(f => f.CanWrite && f.GetSetMethod(true).IsPublic)
)
{
if (property.GetCustomAttributes(typeof (TransformAttribute), true).Any())
{
var transform =
(property.GetCustomAttributes(typeof (TransformAttribute), true).FirstOrDefault() as
TransformAttribute);
var transformname = transform.RelatedField ?? property.Name;
if (convertType.GetProperty(transformname) == null)
throw new ArgumentException(
string.Format(
"We were unable to find property:\"{0}\" on {1}. Please check the RelativeField value on the {2} for \"{0}\"",
transformname, convertFrom.GetType().Name, convertTo.Name));
var theValue = convertType.GetProperty(transformname).GetValue(convertFrom);
if (isCollection(theValue))
{
foreach (var item in (theValue as ICollection))
{
var someVal = new object();
var newToType = property.PropertyType.GetGenericArguments().FirstOrDefault();
if (!String.IsNullOrEmpty(transform.FullyQualifiedName))
someVal =
Transform(
item.GetType().GetProperty(transform.FullyQualifiedName).GetValue(item),
newToType);
else
someVal = Transform(item, newToType);
if (property.GetValue(_t) == null)
throw new NullReferenceException(
string.Format(
"The following property:{0} is null on {1}. Likely this needs to be initialized inside of {1}'s empty constructor",
property.Name, _t.GetType().Name));
property.PropertyType.GetMethod("Add")
.Invoke(property.GetValue(_t), new[] {someVal});
//property.SetValue(_t, theValue.Transform(theValue.GetType()));
}
}
else
property.SetValue(_t, theValue);
}
//property.SetValue(_t, property.GetValue(convertFrom, null), null);
}
return _t;
}
public bool isCollection(object o)
{
return o is ICollection
|| typeof (ICollection<>).IsInstanceOfType(o);
}
}
public class TransformAttribute : Attribute
{
public string RelatedField { get; private set; }
public string FullyQualifiedName { get; set; }
public TransformAttribute()
{
}
public TransformAttribute(string relatedField)
{
RelatedField = relatedField;
}
}
such that the end result is: myObject.Transform()
But the decorations let you account for differences between your POCO and your ViewModel

Using LINQ to create a List<T> where T : someClass<U>

This is related to a prior question of mine C# Generic List conversion to Class implementing List<T>
I have the following code:
public abstract class DataField
{
public string Name { get; set; }
}
public class DataField<T> : DataField
{
public T Value { get; set; }
}
public static List<DataField> ConvertXML(XMLDocument data) {
result = (from d in XDocument.Parse(data.OuterXML).Root.Decendendants()
select new DataField<string>
{
Name = d.Name.ToString(),
Value = d.Value
}).Cast<DataField>().ToList();
return result;
}
This works however I would like to be able to modify the select portion of the LINQ query to be something like this:
select new DataField<[type defined in attribute of XML Element]>
{
Name = d.Name.ToString(),
Value = d.Value
}
Is this just a poor approach? is it possible? Any suggestions?
Here is a working solution: (You must specify fully qualified type names for your Type attribute otherwise you have to configure a mapping somehow...)
I used the dynamic keyword, you can use reflection to set the value instead if you do not have C# 4...
public static void Test()
{
string xmlData = "<root><Name1 Type=\"System.String\">Value1</Name1><Name2 Type=\"System.Int32\">324</Name2></root>";
List<DataField> dataFieldList = DataField.ConvertXML(xmlData);
Debug.Assert(dataFieldList.Count == 2);
Debug.Assert(dataFieldList[0].GetType() == typeof(DataField<string>));
Debug.Assert(dataFieldList[1].GetType() == typeof(DataField<int>));
}
public abstract class DataField
{
public string Name { get; set; }
/// <summary>
/// Instanciate a generic DataField<T> given an XElement
/// </summary>
public static DataField CreateDataField(XElement element)
{
//Determine the type of element we deal with
string elementTypeName = element.Attribute("Type").Value;
Type elementType = Type.GetType(elementTypeName);
//Instanciate a new Generic element of type: DataField<T>
dynamic dataField = Activator.CreateInstance(typeof(DataField<>).MakeGenericType(elementType));
dataField.Name = element.Name.ToString();
//Convert the inner value to the target element type
dynamic value = Convert.ChangeType(element.Value, elementType);
//Set the value into DataField
dataField.Value = value;
return dataField;
}
/// <summary>
/// Take all the descendant of the root node and creates a DataField for each
/// </summary>
public static List<DataField> ConvertXML(string xmlData)
{
var result = (from d in XDocument.Parse(xmlData).Root.DescendantNodes().OfType<XElement>()
select CreateDataField(d)).ToList();
return result;
}
}
public class DataField<T> : DataField
{
public T Value { get; set; }
}
You cannot do this easily in C#. The generic type argument has to specified at compile time. You can use reflection to do otherwise
int X = 1;
Type listype = typeof(List<>);
Type constructed = listype.MakeGenericType( X.GetType() );
object runtimeList = Activator.CreateInstance(constructed);
Here we have just created a List<int>. You can do it with your type
Different instances of a generic class are actually different classes.
I.e. DataField<string> and DataField<int> are not the same class at all(!)
This means, that you can not define the generic parameter during run-time, as it has to be determined during compile-time.
I would say this is a poor approach. In reality, even after you parse your XML file, you're not going to know what types of "DataFields" you have. You might as well just parse them as objects.
However, if you know that you're only ever going to have x number of types, you can do like so:
var Dictionary<string, Func<string, string, DataField>> myFactoryMaps =
{
{"Type1", (name, value) => { return new DataField<Type1>(name, Type1.Parse(value); } },
{"Type2", (name, value) => { return new DataField<Type2>(name, Type2.Parse(value); } },
};
Termit's answer is certainly excellent. Here is a little variant.
public abstract class DataField
{
public string Name { get; set; }
}
public class DataField<T> : DataField
{
public T Value { get; set; }
public Type GenericType { get { return this.Value.GetType(); } }
}
static Func<XElement , DataField> dfSelector = new Func<XElement , DataField>( e =>
{
string strType = e.Attribute( "type" ).Value;
//if you dont have an attribute type, you could call an extension method to figure out the type (with regex patterns)
//that would only work for struct
Type type = Type.GetType( strType );
dynamic df = Activator.CreateInstance( typeof( DataField<>).MakeGenericType( type ) );
df.Name = e.Attribute( "name" ).Value;
dynamic value = Convert.ChangeType( e.Value , type );
df.Value = value;
return df;
} );
public static List<DataField> ConvertXML( string xmlstring )
{
var result = XDocument.Parse( xmlstring )
.Root.Descendants("object")
.Select( dfSelector )
.ToList();
return result;
}
static void Main( string[] args )
{
string xml = "<root><object name=\"im1\" type=\"System.String\">HelloWorld!</object><object name=\"im2\" type=\"System.Int32\">324</object></root>";
List<DataField> dfs = ConvertXML( xml );
}
you can create generic type by reflection
var instance = Activator.CreateInstance( typeof(DataField)
.MakeGenericType(Type.GetType(typeNameFromAttribute) );
// and here set properties also by reflection
#Termit and #Burnzy put forward good solutions involving factory methods.
The problem with that is that you're loading up your parsing routine with a bunch of extra logic (more testing, more errors) for dubious returns.
Another way to do it would be to use a simplified string-based DataField with typed read methods - the top answer for this question.
An implementation of a typed-value method that would be nice but only works for value types (which does not include strings but does include DateTimes):
public T? TypedValue<T>()
where T : struct
{
try { return (T?) Convert.ChangeType(this.Value, typeof(T)); }
catch { return null; }
}
I'm assuming that you're wanting to use the type information to do things like dynamically assigning user-controls to the field, validation rules, correct SQL types for persistence etc.
I've done a lot of this sort of thing with approaches that seem a bit like yours.
At the end of the day you should seperate your metadata from your code - #Burnzy's answer chooses the code based on the metadata (a "type" attribute of the DataField element) and is a very simple example of this.
If you're dealing with XML, XSDs are a very useful and extensible form of metadata.
As far as what you store each field's data in - use strings because:
they are nullable
they can store partial values
they can store invalid values (makes telling the user to sort their act out more transparent)
they can store lists
special cases won't invade unrelated code because there aren't any
learn regular expressions, validate, be happy
you can convert them to stronger types really easily
I found it very rewarding to develop little frameworks like this - it is a learning experience and you'll come out understanding a lot more about UX and the reality of modelling from it.
There are four groups of test cases that I would advise you to tackle first:
Dates, Times, Timestamps (what I call DateTime), Periods (Timespan)
in particular, make sure you test having a different server locality from the client's.
lists - multi-select foreign keys etc
null values
invalid input - this generally involves retaining the original value
Using strings simplifies all this greatly because it allows you to clearly demarcate responsibilities within your framework. Think about doing fields containing lists in your generic model - it gets hairy rather quickly and it is easy to end up with a special case for lists in pretty much every method. With strings, the buck stops there.
Finally, if you want a solid implementation of this sort of stuff without having to do anything much, consider DataSets - old school I know - they do all sorts of wonderful things you wouldn't expect but you do have to RTFM.
The main downfall of that idea would be that it isn't compatible with WPF data binding - though my experience has been that reality isn't compatible with WPF data binding.
I hope I interpreted your intentions correctly - good luck either way :)
Unfortunately, there no inheritance relation between C<T> and C<string> for instance.
However, you can inherit from a common non-generic class and in addition to this implement a generic interface.
Here I use explicit interface implementation in order to be able to declare a Value property typed as object, as well as a more specifically typed Value property.
The Values are read-only and can only be assigned through a typed constructor parameter. My construction is not perfect, but type safe and doesn't use reflection.
public interface IValue<T>
{
T Value { get; }
}
public abstract class DataField
{
public DataField(string name, object value)
{
Name = name;
Value = value;
}
public string Name { get; private set; }
public object Value { get; private set; }
}
public class StringDataField : DataField, IValue<string>
{
public StringDataField(string name, string value)
: base(name, value)
{
}
string IValue<string>.Value
{
get { return (string)Value; }
}
}
public class IntDataField : DataField, IValue<int>
{
public IntDataField(string name, int value)
: base(name, value)
{
}
int IValue<int>.Value
{
get { return (int)Value; }
}
}
The list can then be declared with the abstract base class DataField as generic parameter:
var list = new List<DataField>();
switch (fieldType) {
case "string":
list.Add(new StringDataField("Item", "Apple"));
break;
case "int":
list.Add(new IntDataField("Count", 12));
break;
}
Access the strongly typed field through the interface:
public void ProcessDataField(DataField field)
{
var stringField = field as IValue<string>;
if (stringField != null) {
string s = stringField.Value;
}
}
While the other questions mostly proposed an elegant solution to convert your XML elements to a generic class instance, I'm going to deal with the consequences of taking the approach to model the DataField class as a generic like DataField<[type defined in attribute of XML Element]>.
After selecting your DataField instance into the list you want to use these fields. Her polymorphism comes into play! You want to iterate your DataFields an treat them in a uniform way. Solutions that use generics often end up in a weird switch/if orgy since there is no easy way to associate behavior based on the generic type in c#.
You might have seen code like this (I'm trying to calculate the sum of all numeric DataField instances)
var list = new List<DataField>()
{
new DataField<int>() {Name = "int", Value = 2},
new DataField<string>() {Name = "string", Value = "stringValue"},
new DataField<float>() {Name = "string", Value = 2f},
};
var sum = 0.0;
foreach (var dataField in list)
{
if (dataField.GetType().IsGenericType)
{
if (dataField.GetType().GetGenericArguments()[0] == typeof(int))
{
sum += ((DataField<int>) dataField).Value;
}
else if (dataField.GetType().GetGenericArguments()[0] == typeof(float))
{
sum += ((DataField<float>)dataField).Value;
}
// ..
}
}
This code is a complete mess!
Let's go try the polymorphic implementation with your generic type DataField and add some method Sum to it that accepts the old some and returns the (possibly modified) new sum:
public class DataField<T> : DataField
{
public T Value { get; set; }
public override double Sum(double sum)
{
if (typeof(T) == typeof(int))
{
return sum + (int)Value; // Cannot really cast here!
}
else if (typeof(T) == typeof(float))
{
return sum + (float)Value; // Cannot really cast here!
}
// ...
return sum;
}
}
You can imagine that your iteration code gets a lot clearer now but you still have this weird switch/if statement in you code. And here comes the point: Generics do not help you here it's the wrong tool at the wrong place. Generics are designed in C# for giving you compile time type safety to avoid potential unsafe cast operations. They additionally add to code readability but that's not the case here :)
Let's take a look at the polymorphic solution:
public abstract class DataField
{
public string Name { get; set; }
public object Value { get; set; }
public abstract double Sum(double sum);
}
public class IntDataField : DataField
{
public override double Sum(double sum)
{
return (int)Value + sum;
}
}
public class FloatDataField : DataField
{
public override double Sum(double sum)
{
return (float)Value + sum;
}
}
I guess you will not need too much fantasy to imagine how much adds to your code's readability/quality.
The last point is how to create instances of these classes. Simply by using some convention TypeName + "DataField" and Activator:
Activator.CreateInstance("assemblyName", typeName);
Short Version:
Generics is not the appropriate approach for your problem because it does not add value to the handling of DataField instances. With the polymorphic approach you can work easily with the instances of DataField!
It's not impossible as you can do this with reflection. But this isn't what generics were designed for and isn't how it should be done. If you're going to use reflection to make the generic type, you may as well not use a generic type at all and just use the following class:
public class DataField
{
public string Name { get; set; }
public object Value { get; set; }
}
You'll need to insert the logic for determining the data type from your XML and add all the types you need to use but this should work:
result = (from d in XDocument.Parse(data.OuterXML).Root.Descendants()
let isString = true //Replace true with your logic to determine if it is a string.
let isInt = false //Replace false with your logic to determine if it is an integer.
let stringValue = isString ? (DataField)new DataField<string>
{
Name = d.Name.ToString(),
Value = d.Value
} : null
let intValue = isInt ? (DataField)new DataField<int>
{
Name = d.Name.ToString(),
Value = Int32.Parse(d.Value)
} : null
select stringValue ?? intValue).ToList();

C# Indexer on Named Property Wrapper

I have legacy code that I'm trying to obsolete that I cannot change. Its a column-based data storage class that can be indexed based on the name of the column. Something like this:
StorageClassName scn = new StorageClassName("Property_Name",new double[]{1,2,3});
double[] d = scn["Property_Name"];
I'm trying to create some sort of wrapper class that will allow me to implement interfaces/class inheritance.
BetterStorageClassName bscn = scn;
double[] d = bscn.PropertyName;
What is the best way to accomplish this wrapper class? It doesn't necessarily need to be implicit.
Edit: I left off the main part of this question. The answers I got for the first part are good though. How would I do the conversion the other way?
double[] d = bscn["PropertyName"];
I'm assuming this uses reflection in some capacity.
If you have a class with string indexer and you want to translate that into properties, you could do that using dynamic:
class StorageClassName : Dictionary<string, double[]>
{}
class DynamicStorageClassName : DynamicObject
{
private readonly StorageClassName m_storageClassName;
public DynamicStorageClassName(StorageClassName storageClassName)
{
m_storageClassName = storageClassName;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (m_storageClassName.ContainsKey(binder.Name))
{
result = m_storageClassName[binder.Name];
return true;
}
return base.TryGetMember(binder, out result);
}
}
Usage:
var scn = new StorageClassName { { "PropertyName", new double[] { 1, 2, 3 } } };
dynamic dscn = new DynamicStorageClassName(scn);
Console.WriteLine(dscn.PropertyName[1]);
Keep in mind that you pay some performance penalty for using dynamic and if the set of properties won't change, you should probably use ordinary properties as others suggested.
If, on the other hand, you had a class with ordinary properties and wanted to translate that to using indexer, you can use reflection:
class StaticStorageClassName
{
public double[] PropertyName { get; set; }
public StaticStorageClassName()
{
PropertyName = new double[] { 1, 2, 3 };
}
}
class ReverseStorageClassName
{
private readonly StaticStorageClassName m_staticStorageClassName;
public ReverseStorageClassName(StaticStorageClassName staticStorageClassName)
{
m_staticStorageClassName = staticStorageClassName;
}
public double[] this[string name]
{
get
{
var propertyInfo = typeof(StaticStorageClassName).GetProperty(name);
if (propertyInfo == null)
throw new ArgumentException();
return (double[])propertyInfo.GetValue(m_staticStorageClassName, null);
}
}
}
Usage:
var sscn = new StaticStorageClassName();
var rscn = new ReverseStorageClassName(sscn);
Console.WriteLine(rscn["PropertyName"][2]);
If the various properties have different types, you'd have to make the return type of the indexer either object or dynamic.
Note that using reflection wouldn't work if StaticStorageClassName was implemented using DynamicObject.
For the example given, I would imagine it implemented like this:
public double[] PropertyName
{
get { return wrapped["Property_Name"]; }
set { wrapped["Property_Name"] = value; }
}
Are you trying to avoid writing all these wrappers? Or do type conversions?
From your question I guess you want to have full named properties instead of string based keys. In that case you can just provide properties for the access:
class BetterStorageClassName : StorageClassName
{
public double[] PropertyName
{
get { return this["Property_Name"]; }
set { this["Property_Name"] = value; }
}
}
However that method has several caveats:
It implies that the properties are fixed and don't really change because otherwise you end up constantly modifying your wrapper.
The properties can't be dynamically defined at runtime.
You have to have a wrapper class for each set of properties.

Mapping switch statements to data classes

This seems to come up alot in my code, i'm wondering if there is some way of removing the switch statement, or if there is a more elegant way of doing it?
public class MetaData
{
public string AlbumArtist { get; set; }
public string AlbumTitle { get; set; }
public string Year { get; set; }
public string SongTitle { get; set; }
public static MetaData CreateMetaDataFrom(IEnumerable<TextFrame> textFrames)
{
var metaData = new MetaData();
foreach (var frame in textFrames)
{
switch (frame.Descriptor.ID)
{
case "TPE1":
metaData.AlbumArtist = frame.Content;
break;
case "TALB":
metaData.AlbumTitle = frame.Content;
break;
case "TIT2":
metaData.SongTitle = frame.Content;
break;
case "TYER":
metaData.Year = frame.Content;
break;
}
}
return metaData;
}
}
This is related to an object-oriented approach. The usual method to get rid of if's or case's is to use a lookup table of criteria and effects. There are other techniques that use this same idea, such as data-directed programming (http://en.wikipedia.org/wiki/Data-directed_programming) and dispatch tables (http://en.wikipedia.org/wiki/Dispatch_table). Many language implementations use type dispatch tables to implement virtual method calls.
The lookup table could be a hashtable populated with lambda functions as so:
Dictionary<string, Func<MetaData, string, string>> lookup = new Dictionary<string, Func<MetaData, string, string>>();
lookup["TPE1"] = (m, v) => m.AlbumArtist = v;
lookup["TALB"] = (m, v) => m.AlbumTitle = v;
lookup["TIT2"] = (m, v) => m.SongTitle = v;
lookup["TYER"] = (m, v) => m.Year = v;
then you assign fields of metaData inside your loop as:
lookup[frame.Descriptor.ID](metaData, frame.Content);
From your code I conclude that IEnumerable<TextFrame> has always 4 members
so you could just write (haven't tried it so check for the syntax):
public static MetaData CreateMetaDataFrom(IEnumerable<TextFrame> textFrames)
{
return new MetaData()
{
metaData.AlbumArtist = textFrames.Where(frame => frame.Descriptor.ID = "TPE1").SingleOrDefault().Content,
metaData.AlbumTitle = textFrames.Where(frame => frame.Descriptor.ID = "TALB").SingleOrDefault().Content,
metaData.SongTitle = textFrames.Where(frame => frame.Descriptor.ID = "TIT2").SingleOrDefault().Content;
metaData.Year = textFrames.Where(frame => frame.Descriptor.ID = "TYER").SingleOrDefault().Content;
};
}
You might want to look at implementing the Strategy Pattern. DimeCasts.Net have an excellent video tutorial that may help.
I was tempted to suggest the Strategy Pattern but you may need a slight variation. Consider writing a method in the TextFrame class, lets call it putContent(MetaData).
Then create subclasses of TextFrame each representing a different Type of Frame. Each subclass will override the putContent(Metadata) method and do its appropraite logic.
PseudoCode Example for TPE1:
Metadata putContent(MetaData md){
md.AlbumArtist = Content;
return md;
}
You MetaData code will then change to:
var metaData = new MetaData();
foreach (var frame in textFrames)
{
metaData = frame.putContent(metaData);
}
return metaData;
Of course, to create the TextFrames them selves will then require a Factory so this is not the end of the story.
It seems like you know what the types will be before hand (using a switch) so why not just retrieve the values as required, without a for switch.
Examples are when using a hashtable, and you know what fields will be available, just use the fields.
ig you are not sure if the field is available, a simple test will be sufficiant if the list contains the value.
You can then even write a helper function to check and return the value if the list has the value.
What you really have here is a four-way setter. The canonical refactoring here is "Replace Parameter with Explicit Methods" (p285 of Refactoring by Martin Fowler). The Java example he gives is changing:
void setValue(String name, int value) {
if (name.equals("height")) {
_height = value;
return;
}
if (name.equals("width")) {
_width = value;
return;
}
}
to:
void setHeight(int arg) {
_height = arg;
}
void setWidth(int arg) {
_width = arg;
}
Assuming that the caller of CreateMetaDataFrom() knows what it's passing in, you could skip the switch/case and use the actual setters for those properties.

Categories