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
Related
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
Suppose I have a List of Person (which is a class). It contains about 20 field (Name, Surname, Age, DateOfBirthdate, and so on). So I got this list:
var listOfPersons= MyContext.Persons.Cast<Person>();
Now, I need to iterate through this List, and for each Person adding a new field (which it is not present in the class), called, let's say, CurrentDateTime.
I could create a new object, with the new field, and "copy & paste" values from Person to the new Class. Somethings like:
PersonNew newPerson = new PersonNew("Name", "Surname", "Age", "DateOfBirthdate", ... "CurrentDateTime");
But this is very bad if in the future I change the Person class. So, is there a strategy to "extending Person" with a new field? That takes the Person instance (whatever it is) and adds the new field?
You can create some static method that create PersonNew from Person using Automapper.
public class PersonNew : Person
{
public static PersonNew CreateFromPerson(Person person, DateTime currentDateTime)
{
var newPerson = Mapper.Map<PersonNew>(person);
newPerson.CurrentDateTime = currentDateTime;
}
}
I think that the solution you described works fine. If you want to keep track of each person's birthday without extending the Person class, you might use a Dictionary object
var listOfPersons = MyContext.Perons.Cast<Person>();
Dictionary<Person, DateTime> birthdays = new Dictionary<Person, DateTime>
foreach(Person person in listOfPersons)
{
birthdays.Add(person, getBirthday(person);
}
One solution is to make your class partial, and add your field in another partial definition of your class:
public partial class Person
{
public string Name { get; set; }
public string FirstName { get; set; }
...
}
...
public partial class Person
{
public DateTime CurrentDateTime { get; set; }
}
...
var listOfPersons = MyContext.Persons.Cast<Person>();
foreach (var person in listOfPersons)
{
person.CurrentDateTime = ....
}
Do note that you will use the same instance of your class.
First I would suggest using extension methods for projecting collections instead of iterating. Like that:
var newCollection = oldCollection.Select(entity => MakeNewType(entity))
Second, it's not completely clear what you mean by "extending Person" with a new field. Here are the couple of ways you can accomplish that.
1) Make another class with the new field and map it to the old one. This is a common scenario for asp.net mvc application where you map models to the appropriate viewmodels. Automapper is useful for these types of scenario (see SÅ‚awomir Rosiek anwser)
2) Take advantage of dlr in c# 4+. Yuo will lose the intellisense for dynamic objects, but they canned be passed around functions
var newPeople = people.Select(p =>
{
dynamic expando = new ExpandoObject();
expando.Id = p.Id;
expando.FirtName = p.FirtName;
/* ... */
expando.CurrentDateTime = DateTime.Now;
return expando;
});
3) Use Anonymous types. Anonymous types cannot be passed to another functions, so this approach is useful when you need to quickly project data inside a single method and calculate some result
var newPeople = people.Select(p => new
{
Id = p.Id,
FirtName = p.FirtName,
/* ... */
CurrentDateTime = DateTime.Now
});
in both cases you can now access newly "created" property:
foreach(var p in newPeople)
{
Console.WriteLine("CurrentDateTime: {0}", p.CurrentDateTime);
}
4) If you really need to create a fully featured .net class at runtime you can use Reflection.Emit. This scenario is typically used to create dynamic proxies - subclasses which implement some functionality only known at runtime. Entity framework does this.
I am adapting the Windows Phone Database Example using MVVM for my own app. Here it suggests wrapping the EntitySet properties on the model objects with a getter/setter combination that allows assignment using an IEnumerable (using the Assign method) e.g.
// Example method from a model class 'SomeModelObject'
[Association(Storage = "_todos", OtherKey = "_categoryId", ThisKey = "Id")]
public EntitySet<ToDoItem> ToDos
{
get { return this._todos; }
set { this._todos.Assign(value); }
}
However when I try and instantiate an object that has an EntitySet property it will not allow it, e.g.
SomeModelObject myModelObject = new SomeModelObject() {
Property1 = "foo",
Property2 = true,
// Following raises an error, even though setter should allow assignment
// from an IEnumerable (because of the use of 'Assign' in the setter)
ToDos = new List<ToDoItem>() {
new ToDoItem(),
},
};
The error is as follows,
Error 1 Cannot implicitly convert type
'System.Collections.Generic.List<SomeApp.ToDoItem>' to
'System.Data.Linq.EntitySet<SomeApp.ToDoItem>'
How do I instantiate objects referenced from an EntitySet?
Well, there are two problems with the code you've posted.
I suspect the main problem is that you're trying to set the value of the property to a List<ToDoItem> when the type of the property is EntitySet<ToDoItem>. (The error message you've posted doesn't match the code you've posted, but never mind.) You say it "should allow assignment from an IEnumerable, but I don't know why you'd expect that to be the case, given the declaration of the property:
public EntitySet<ToDoItem> ToDos { ... }
The property is not of type IEnumerable<ToDoItem>.
You're also using new SomeModelObject twice for some reason:
SomeModelObject myModelObject = new SomeModelObject() {
new SomeModelObject() {
Property1 = "foo",
...
}
};
I'll assume your real code looks more like this:
SomeModelObject myModelObject = new SomeModelObject() {
Property1 = "foo",
...
};
Please be more careful in future - it's very hard to diagnose code that isn't posted.
There are three options around the EntitySet.
If you want to add new items to an existing EntitySet<ToDoItem> you can do that within the object initializer:
SomeModelObject myModelObject = new SomeModelObject {
Property1 = "foo",
Property2 = true,
ToDos = {
new ToDoItem(),
}
};
Alternatively, if you want to set the value of the property itself, you need to create a new EntitySet<ToDoItem>:
SomeModelObject myModelObject = new SomeModelObject {
Property1 = "foo",
Property2 = true,
ToDos = new EntitySet<ToDoItem> {
new ToDoItem(),
}
};
This seems unlikely to be a good idea though, and I'd actually suggest making the setter private.
Alternatively, if you really want to be able to assign any IEnumerable<ToDoItem>, you can just change the type of the property:
[Association(Storage = "_todos", OtherKey = "_categoryId", ThisKey = "Id")]
public IEnumerable<ToDoItem> ToDos
{
get { return this._todos; }
set { this._todos.Assign(value); }
}
I am trying to serialize a List<T> where T: EntityObject and would love to leave out all the EntityKey and other EntityReference properties from the items in the list. Can this be done dynamically possibly using XmlAttributeOverrides?
As far as I can see, the XmlAttributeOverrides options only really point to the top level object ie the List<T> and not the T themselves, which is not very helpful to me.
Could anyone point me to a way to dynamically ignore properties of ArrayItems?
Here is a simple example I have been using that does not use EntityObjects but it should illustrate what I would like to do:
public class Car
{
public String Make { get; set; }
public String Model { get; set; }
public Double EngineSize { get; set; }
}
[Test]
public void WouldLoveToDynamicallyLeaveOutMembersOfArrayItems()
{
var cars = new List<Car>
{
new Car
{
Make = "Ferrari",
Model = "F1",
EngineSize = 6000
},
new Car
{
Make = "Williams",
Model = "F1",
EngineSize = 5500
}
};
var attributeOverrides = new XmlAttributeOverrides();
attributeOverrides.Add(typeof(Double), "EngineSize", new XmlAttributes {XmlIgnore = true});
var xs = new XmlSerializer(cars.GetType(), attributeOverrides, new []{ typeof(Car) }, new XmlRootAttribute("cars"), "");
var ms = new MemoryStream();
xs.Serialize(ms, cars);
ms.Position = 0;
var sr = new StreamReader(ms);
var result = sr.ReadToEnd();
Assert.IsFalse(result.Contains("EngineSize"));
}
Yes you can do that - the main error is you need typeof(Car) instead of typeof(double). With this, note that XmlAttributeOverrides does not just apply to the top-level onject.
However, I'm not sure that is the easiest route. Firstly, note that you must store and re-use the serialiser when using XmlAttributeOverrides otherwise you will leak assemblies.
I expect the main reason you want to do this is because you don't want to edit he generated file. However, there is another way; in a separate file, in the right namespace, you can use:
partial class Car {
public bool ShouldSerializeEngineSize() { return false; }
}
Where "ShouldSerialize*" is a pattern recognised and used by XmlSerializer to control conditional serialization. The "partial" class is simply a way of combining two separate code files into a single type (and was designed for this generated-content scenario).
This way, you dont need to mess with XmlAttributeOverrides, and you can use the simpler "new XmlSeralizer(Type)" (which has automatic caching per-type).
I am in need of creating a dynamically extend-able class in C#.
The goal is to create a class what can contain all info from a given contact from an Android SQLite Contacts table. The table's structure is kinda weird, as it does not have set field names, but uses colums of 'field name' and 'field content'.
That's what I want to turn into a usable format where the code reads the database, and for each entry creates the matching sub-variable. Such I want to know the best method to do so (I guess a simple
{
this.(variableNames[i].ToString()) = variableContent[i];
}
will not do it), what is the least resource-eating, but fastest (and easiest) way.
And also if we are here, is there ANY method to call a type's (let's say, I create a new Contact with e-mail, workplace, name, and image tags, but these variables names' are unknown) ALL sub-variables (Contact.image, Contact.FirstName, Contact.Email, etc) dynamically?
Of course there will be standardized fields what should be in ALL contact (one of the three names, phone number, e-mail #work and #home, and such), but these should be called dynamically too.
Use a Dictionary<string,string> instead.
Dictionary<string,string> contactInfo = new Dictionary<string,string>();
public void ImportContact()
{
...
// for each fieldName and fieldValue from your table
contactInfo.Add(fieldName, fieldValue);
...
// check that all standard fields are present, if desired
}
public string FirstName
{
get { return contactInfo["FirstName"]; }
}
If you are willing to go with dynamic typing, you can use the dynamic type in C# 4. You can use ExpandoObject or DynamicObject as a base for your Contact types.
Here is an example of a Contact class that can work both statically typed with some pre-defined properties; and can have properties attached to it at run-time. When treating it statically, you can still get the values by using the indexer:
class Contact : DynamicObject
{
private readonly Dictionary<string, object> bag = new Dictionary<string, object>();
public string FirstName { get; set; }
public string LastName { get; set; }
public object this[string key]
{
get { return bag[key]; }
set { bag[key] = value; }
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (bag.ContainsKey(binder.Name))
{
result = bag[binder.Name];
return true;
}
return base.TryGetMember(binder, out result);
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
bag[binder.Name] = value;
return true;
}
}
Which you can then use like this:
// Contact is statically typed.
Contact c = new Contact();
c.FirstName = "test";
// Treat as dynamic and attach some extra properties:
dynamic dynContact = c;
dynContact.AddressOne = "Somewhere";
dynContact.AddressTwo = "Someplace else";
Console.WriteLine(dynContact.AddressOne);
Console.WriteLine(dynContact.AddressTwo);
Other than using dynamic, you cannot create a new class with dynamically typed properties. After all, how would you consume those properties ? You might be better off creating a class containing the properties that you must have; and put the rest in a Dictionary<string,object>.
If you're using .NET 4.0, there's dynamic support. You can create objects something like this:
var newContact = new object { FirstName = "name", LastName = "name", etc... };
Alternatively, you might want to try using a Dictionary.