I have a class DocumentObject that extends DynamicObject to allow dynamic membership attributes.
public class DocumentObject : DynamicObject
{
/// <summary>
/// Inner dictionary that holds the dynamic members of the object
/// </summary>
Dictionary<string, object> dictionary = new Dictionary<string, object>();
/// <summary>
/// Try to get the member that is not defined in the class (additional dynamic members) from inner dictionary
/// </summary>
/// <param name="binder"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
// Converting the property name to lowercase
// so that property names become case-insensitive.
string name = binder.Name.ToLower();
// If the property name is found in a dictionary,
// set the result parameter to the property value and return true.
// Otherwise, return false.
return dictionary.TryGetValue(name, out result);
}
/// <summary>
/// Try to set the member that is not defined in the class (additional dynamic members) to inner dictionary
/// </summary>
/// <param name="binder"></param>
/// <param name="value"></param>
/// <returns></returns>
public override bool TrySetMember(SetMemberBinder binder, object value)
{
// Converting the property name to lowercase
// so that property names become case-insensitive.
dictionary[binder.Name.ToLower()] = value;
// You can always add a value to a dictionary,
// so this method always returns true.
return true;
}
/// <summary>
/// Get the names of all the dynamic members
/// </summary>
/// <returns></returns>
public override IEnumerable<string> GetDynamicMemberNames()
{
return dictionary.Keys;
}
}
I have a base Person class that inherits DocumentObject
public class PersonDto : DocumentObject
{
[JsonProperty("id")]
public string Id { get; set; }
}
Another child OfficePersonDto class that inherits PersonDto
public class OfficePersonDto : PersonDto
{
[JsonProperty("name")]
public string Name { get; set; }
}
In my function, I am receiving JSON object that has to be at least PersonDto object, but if its an OfficePersonDto type, I wish to be able to cast PersonDto into OfficePersonDto.
I.e. JSON = {"Id":1, "Name": "Orchard"}, in PersonDto, name attribute will be saved using DocumentObject's dictionary, while casting to OfficePersonDto, both Id and name are attributes of the class.
How can I cast from PersonDto to a child class e.g. OfficePersonDto?
PersonDto personDto = ...
OfficePersonDto off = personDto as OfficePersonDto // results in null or Name is null
Automapper is very helpful when the issue is about these kind of conversions.
I'll show you a quick working example. But it will be still open for more refactoring of course.
PersonDto personDto = ...
// Do not use the following conversion, instead get help from automapper
// OfficePersonDto off = personDto as OfficePersonDto
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<PersonDto, OfficePersonDto>()
.ForMember(d => d.Name, opt => opt.MapFrom(new OfficePersonNameResolver()));
});
var mapper = config.CreateMapper();
var off = mapper.Map<OfficePersonDto>(personDto); // off is created with correct values
OfficePersonNameResolver is like this:
public class OfficePersonNameResolver : IValueResolver<PersonDto, OfficePersonDto, string>
{
public string Resolve(PersonDto source, OfficePersonDto destination, string destMember, ResolutionContext context)
{
return (source as dynamic).Name;
}
}
You are welcome to ask if you have questions about this.
Edit: Generalizing the resolver to IValueResolver<TSource, TDestination, TDestinationMember>
Change the creation of configuration like this:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<PersonDto, OfficePersonDto>()
.ForMember(d => d.Name, opt => opt.MapFrom(new DynamicObjectValueResolver<PersonDto, OfficePersonDto, string>("name")));
});
And The value resolver should be like this now:
public class DynamicObjectValueResolver<TSource, TDestination, TDestinationMember> : IValueResolver<TSource, TDestination, TDestinationMember>
where TDestinationMember : class
{
private readonly string _propertyName;
public DynamicObjectValueResolver(string propertyName)
{
_propertyName = propertyName;
}
public TDestinationMember Resolve(TSource source, TDestination destination, TDestinationMember destMember, ResolutionContext context)
{
dynamic eo = JsonConvert.DeserializeObject<ExpandoObject>(JsonConvert.SerializeObject(source));
IDictionary<string, object> dictionary = eo;
return dictionary[_propertyName] as TDestinationMember;
}
}
Working example: https://dotnetfiddle.net/KS91To
Related
I have a UI from where we are adding following values in a table Fields
ProductName
ProductId
ProductCode
I have a existing class Product with some existing properties
public class Product
{
public string ProductID { get; set; }
//product in a product search listing
public string StoreName { get; set; }
public string SearchToken { get; set; }
}
I am looking for a method which will add properties in existing class Product at run time (dynamically) when user adds values in a table Fields
I don't know a way of defining properties during runtime, but another possibiity for you to achieve what you need is to use a dynamic object in C# known as ExpandoObject.
You first need to declare your dynamic object, it uses a kind of Dictionary internally so you can then add your properties to it.
using System.Dynamic;
dynamic newobj = new ExpandoObject();
//I can add properties during compile time such as this
newobj.Test = "Yes";
newobj.JValue = 123;
//Or during runtime such as this (populated from two text boxes)
AddProperty(newobj, tbName.Text, tbValue.Text);
public void AddProperty(ExpandoObject expando, string propertyName, object propertyValue)
{
var exDict = expando as IDictionary<string, object>;
if (exDict.ContainsKey(propertyName))
exDict[propertyName] = propertyValue;
else
exDict.Add(propertyName, propertyValue);
}
I have used it once in a solution here: Flattern child/parent data with unknown number of columns
But these sources can probably explain it better;
https://www.oreilly.com/learning/building-c-objects-dynamically
https://weblog.west-wind.com/posts/2012/feb/08/creating-a-dynamic-extensible-c-expando-object
https://learn.microsoft.com/en-us/dotnet/articles/csharp/programming-guide/types/using-type-dynamic
But, I'm not sure that this would really offer you any real advantages over using a simple Dictionary<string, object>
As others have pointed out, it doesn't sound like your scenario calls for this behavior. However, what you're asking for is possible. Using System.Reflection.Emit, you can dynamically extend your base class at runtime and even save it at runtime as a compiled .dll. The trickier part would be to figure out how to deploy your new class to production from your production code and overwrite your previous class, if that's needed. I'll leave that part up to you to figure out. As per the rest of your question, I had a similar need when creating automated testing for a framework that requires implementers to extend base classes, so I built a mini framework to do this. You can use it like this:
public void TestTypeCreator()
{
//create and save new type
object _newProduct = DynamicTypeCreator
.Create("NewProduct", typeof(Product), #"C:\PROD")
.AddPassThroughCtors()
.AddProperty("ProductName", typeof(string))
.FinishBuildingAndSaveType("NewProduct.dll")
.GetConstructor(new Type[0] { })
.Invoke(new object[0] { });
//set ProductName value
_newProduct.GetType().GetProperty("ProductName").SetValue(_newProduct, "Cool Item");
//get ProductName value
string _prodName = _newProduct.GetType().GetProperty("ProductName").GetValue(_newProduct).ToString();
//get StoreName value
string _storeName = _newProduct.GetType().GetProperty("StoreName").GetValue(_newProduct).ToString();
}
Since I haven't published this code anywhere, I'll paste the whole thing below. This "framework" is very limited and you might have to add to it/modify it for your specific purpose. Here ya go:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.IO;
namespace ConsoleCommon
{
public interface IBaseObject
{
IEmptyObject AddPassThroughCtors();
}
public interface IEmptyObject
{
IAfterProperty AddProperty(string name, Type type);
}
public interface IAfterProperty : IEmptyObject, IFinishBuild
{
IAfterAttribute AddPropertyAttribute(Type attrType, Type[] ctorArgTypes, params object[] ctorArgs);
}
public interface IAfterAttribute : IEmptyObject, IFinishBuild
{
}
public interface IFinishBuild
{
Type FinishBuildingType();
Type FinishBuildingAndSaveType(string assemblyFileName);
}
public static class DynamicTypeCreator
{
public static IBaseObject Create(string className, Type parentType)
{
return new DynamicTypeCreatorBase().Create(className, parentType);
}
public static IBaseObject Create(string className, Type parentType, string dir)
{
return new DynamicTypeCreatorBase().Create(className, parentType, dir);
}
}
public class PropertyBuilding
{
public PropertyBuilding(PropertyBuilder propertyBuild, MethodBuilder getBuild, MethodBuilder setBuild)
{
propertyBuilder = propertyBuild;
getBuilder = getBuild;
setBuilder = setBuild;
}
public PropertyBuilder propertyBuilder { get; }
public MethodBuilder getBuilder { get; }
public MethodBuilder setBuilder { get; }
}
public class DynamicTypeCreatorBase : IBaseObject, IEmptyObject, IAfterProperty, IAfterAttribute
{
TypeBuilder _tBuilder;
List<PropertyBuilding> _propBuilds = new List<PropertyBuilding>();
AssemblyBuilder _aBuilder;
/// <summary>
/// Begins creating type using the specified name.
/// </summary>
/// <param name="className">Class name for new type</param>
/// <param name="parentType">Name of base class. Use object if none</param>
/// <returns></returns>
public IBaseObject Create(string className, Type parentType)
{
return Create(className, parentType, "");
}
/// <summary>
/// Begins creating type using the specified name and saved in the specified directory.
/// Use this overload to save the resulting .dll in a specified directory.
/// </summary>
/// <param name="className">Class name for new type</param>
/// <param name="parentType">Name of base class. Use object if none</param>
/// <param name="dir">Directory path to save .dll in</param>
/// <returns></returns>
public IBaseObject Create (string className, Type parentType, string dir)
{
_parentType = parentType;
//Define type
AssemblyName _name = new AssemblyName(className);
if (string.IsNullOrWhiteSpace(dir))
{
_aBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(_name, AssemblyBuilderAccess.RunAndSave);
}
else _aBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(_name, AssemblyBuilderAccess.RunAndSave, dir);
ModuleBuilder _mBuilder = _aBuilder.DefineDynamicModule(_name.Name, _name.Name + ".dll");
_tBuilder = _mBuilder.DefineType(className, TypeAttributes.Public | TypeAttributes.Class, parentType);
return this;
}
/// <summary>
/// Adds constructors to new type that match all constructors on base type.
/// Parameters are passed to base type.
/// </summary>
/// <returns></returns>
public IEmptyObject AddPassThroughCtors()
{
foreach(ConstructorInfo _ctor in _parentType.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
{
ParameterInfo[] _params = _ctor.GetParameters();
Type[] _paramTypes = _params.Select(p => p.ParameterType).ToArray();
Type[][] _reqModifiers = _params.Select(p => p.GetRequiredCustomModifiers()).ToArray();
Type[][] _optModifiers = _params.Select(p => p.GetOptionalCustomModifiers()).ToArray();
ConstructorBuilder _ctorBuild = _tBuilder.DefineConstructor(MethodAttributes.Public, _ctor.CallingConvention, _paramTypes, _reqModifiers, _optModifiers);
for (int i = 0; i < _params.Length; i++)
{
ParameterInfo _param = _params[i];
ParameterBuilder _prmBuild = _ctorBuild.DefineParameter(i + 1, _param.Attributes, _param.Name);
if (((int)_param.Attributes & (int)ParameterAttributes.HasDefault) != 0) _prmBuild.SetConstant(_param.RawDefaultValue);
foreach(CustomAttributeBuilder _attr in GetCustomAttrBuilders(_param.CustomAttributes))
{
_prmBuild.SetCustomAttribute(_attr);
}
}
//ConstructorBuilder _cBuilder = _tBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Any, argTypes);
ILGenerator _ctorGen = _ctorBuild.GetILGenerator();
_ctorGen.Emit(OpCodes.Nop);
//arg0=new obj, arg1-arg3=passed params. Push onto stack for call to base class
_ctorGen.Emit(OpCodes.Ldarg_0);
for (int i = 1; i <= _params.Length; i++) _ctorGen.Emit(OpCodes.Ldarg, i);
_ctorGen.Emit(OpCodes.Call, _ctor);
_ctorGen.Emit(OpCodes.Ret);
}
return this;
}
/// <summary>
/// Adds a new property to type with specified name and type.
/// </summary>
/// <param name="name">Name of property</param>
/// <param name="type">Type of property</param>
/// <returns></returns>
public IAfterProperty AddProperty(string name, Type type)
{
//base property
PropertyBuilder _pBuilder = _tBuilder.DefineProperty(name, PropertyAttributes.None, type, new Type[0] { });
//backing field
FieldBuilder _fBuilder = _tBuilder.DefineField($"m_{name}", type, FieldAttributes.Private);
//get method
MethodAttributes _propAttrs = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
MethodBuilder _getBuilder = _tBuilder.DefineMethod($"get_{name}", _propAttrs, type, Type.EmptyTypes);
ILGenerator _getGen = _getBuilder.GetILGenerator();
_getGen.Emit(OpCodes.Ldarg_0);
_getGen.Emit(OpCodes.Ldfld, _fBuilder);
_getGen.Emit(OpCodes.Ret);
//set method
MethodBuilder _setBuilder = _tBuilder.DefineMethod($"set_{name}", _propAttrs, null, new Type[] { type });
ILGenerator _setGen = _setBuilder.GetILGenerator();
_setGen.Emit(OpCodes.Ldarg_0);
_setGen.Emit(OpCodes.Ldarg_1);
_setGen.Emit(OpCodes.Stfld, _fBuilder);
_setGen.Emit(OpCodes.Ret);
_propBuilds.Add(new PropertyBuilding(_pBuilder, _getBuilder, _setBuilder));
return this;
}
/// <summary>
/// Adds an attribute to a property just added.
/// </summary>
/// <param name="attrType">Type of attribute</param>
/// <param name="ctorArgTypes">Types of attribute's cstor parameters</param>
/// <param name="ctorArgs">Values to pass in to attribute's cstor. Must match in type and order of cstorArgTypes parameter</param>
/// <returns></returns>
public IAfterAttribute AddPropertyAttribute(Type attrType, Type[] ctorArgTypes, params object[] ctorArgs)
{
if (ctorArgTypes.Length != ctorArgs.Length) throw new Exception("Type count must match arg count for attribute specification");
ConstructorInfo _attrCtor = attrType.GetConstructor(ctorArgTypes);
for (int i = 0; i < ctorArgTypes.Length; i++)
{
CustomAttributeBuilder _attrBuild = new CustomAttributeBuilder(_attrCtor, ctorArgs);
_propBuilds.Last().propertyBuilder.SetCustomAttribute(_attrBuild);
}
return this;
}
/// <summary>
/// Completes building type, compiles it, and returns the resulting type
/// </summary>
/// <returns></returns>
public Type FinishBuildingType()
{
foreach(var _pBuilder in _propBuilds)
{
_pBuilder.propertyBuilder.SetGetMethod(_pBuilder.getBuilder);
_pBuilder.propertyBuilder.SetSetMethod(_pBuilder.setBuilder);
}
Type _paramsType = _tBuilder.CreateType();
return _paramsType;
}
/// <summary>
/// Completes building type, compiles it, saves it, and returns the resultying type.
/// Assembly is saved in the calling assembly's directory or in the dir specified in the Create method.
/// </summary>
/// <param name="assemblyFileName">Filename of the assembly</param>
/// <returns></returns>
public Type FinishBuildingAndSaveType(string assemblyFileName)
{
Type _newType = FinishBuildingType();
Save(assemblyFileName);
return _newType;
}
#region Helpers
private CustomAttributeBuilder[] GetCustomAttrBuilders(IEnumerable<CustomAttributeData> customAttributes)
{
return customAttributes.Select(attribute => {
object[] attributeArgs = attribute.ConstructorArguments.Select(a => a.Value).ToArray();
PropertyInfo[] namedPropertyInfos = attribute.NamedArguments.Select(a => a.MemberInfo).OfType<PropertyInfo>().ToArray();
object[] namedPropertyValues = attribute.NamedArguments.Where(a => a.MemberInfo is PropertyInfo).Select(a => a.TypedValue.Value).ToArray();
FieldInfo[] namedFieldInfos = attribute.NamedArguments.Select(a => a.MemberInfo).OfType<FieldInfo>().ToArray();
object[] namedFieldValues = attribute.NamedArguments.Where(a => a.MemberInfo is FieldInfo).Select(a => a.TypedValue.Value).ToArray();
return new CustomAttributeBuilder(attribute.Constructor, attributeArgs, namedPropertyInfos, namedPropertyValues, namedFieldInfos, namedFieldValues);
}).ToArray();
}
/// <summary>
/// Requires admin privileges.
/// To save in a specified dir, use the Create overload that requires a 'dir' parameter.
/// </summary>
/// <param name="assemblyFileName"></param>
private void Save(string assemblyFileName)
{
string _assemblyName = assemblyFileName;
if (!Path.HasExtension(assemblyFileName) || Path.GetExtension(assemblyFileName).ToLower() != ".dll")
_assemblyName += ".dll";
_aBuilder.Save(_assemblyName);
}
#endregion
}
}
Definition:
public class Product
{
public string ProductID { get; set; }
//product in a product search listing
public string StoreName { get; set; }
public string SearchToken { get; set; }
public Dictionary<string, object> Fields { get; set; }
}
Usage:
var Prod = new Product();
Prod.Fields.Add("MyCustomFieldString", "my value here");
Prod.Fields.Add("MCustomFieldInt", 123);
MessageBox.Show(Prod.Fields[MyCustomFieldString].ToString());
MessageBox.Show(Prod.Fields[MCustomFieldInt].ToString());
You need to clarify (maybe to yourself) what exactly do you need, or I'll say, do you want.
After you compile your code, metadata is generated based on your types\methods\fields. There are many tables (45 to be more specific) to describe your data.
So, If you want to add new properties to some type, you need to add new rows to metadata tables. To add new rows, you need to compile code.
So what is Refelection.Emit? Is a way to dynamically create new data in your app. With this you create a dynamic assembly, dynamic type, dynamic method, add some IL and there you go. But, all of this is in memory. and that not what you want.
So, or dynamically data using Emit \ ExpandoObject, or statically data by adding the properties to code and compile it.
You have also a way of between the run time way to compile way, it's call Mono.Cecil by using it, you can, by code, add the new properties to type, then compile it and load the new dll to your AppDomain or just shutdown the app and re lunch it. It can be automatically and just by code. There is no magic, it will add data to the metadata tables in your assembly, but it can short the way of doing that manually.
But there is a more option that I'm sure you know about it. I you are using Visual Studio, you probably heard about Edit and Continue (EnC). This feature is actually write new code to your running assembly, on the fly add the new data (and only the new) to the metadata.
So, I can't tell you what you should do because I don't know exactly what is your case, and there is cases that people want extreme solution when they not need them, but if you really must the option to add new data to your already running assembly, EnC can be a solution. To use EnC take a look on the feature in Roslyn code (you might want to check their test to understand how to work with). After that, take a look on #Josh Varty example here.
After all I wrote, I really (x3) don't think you need to make something crazy for what you need. I'm sure there is a better, simple way to do it.
I found using DynamicObject Class useful for adding properties dynamically during runtime.
Add a property to your Existing Class like below :
class MyClass
{
public int Id { get; set; }// Existing property
public List<dynamic> Information { get; set; }
// Added above property to handle new properties which will come dynamically
}
//-------- While Implementing ----
MyClass obj = new MyClass();
obj.Id = 1111; // Existing Property
obj.Information = new List<dynamic>();
obj.Information.Add(new ExpandoObject());
obj.Information[0].Name= "Gyan"; // New Property
obj.Information[0].Age = 22; // New Property
I have stored immutable types in a temporary CQRS read store (query/read side, in fact implemented by a simple List with abstraction access layer, I don't want to use a full blown document database at this point). These read stores contains items like the following:
public class SomeItem
{
private readonly string name;
private readonly string description;
public SomeItem(string name, string description)
{
this.name = name;
this.description = description;
}
public string Name
{
get { return this.name; }
}
public string Description
{
get { return this.description; }
}
}
Now I want to change the Name and in a 2nd Command the Description.
These changes should keep the current state, which means for the example above:
// initial state
var someItem = new SomeItem("name", "description");
// update name -> newName
someItem = new SomeItem("newName", someItem.Description);
// update description -> newDescription
someItem = new SomeItem(someItem.Name, "newDescription");
This does look error prone to me if you have several properties... you have to manage keeping the current state. I could add something like Clone() to every type but I think/hope there is something better out there that performs well and is easy to use, I don't want to write much repetive code (lazy programmer). Any suggestions how to improve the code above? The SomeItem class needs to stay immutable (transported through several different threads).
Sadly, there's no simple way in C#. F# has the with keyword, and you could have a look at lenses, but it's all somewhat tedious in C#. The best I can give you is something like this:
class SomeItem
{
private readonly string name;
private readonly string description;
public SomeItem(string name, string description)
{
this.name = name;
this.description = description;
}
public SomeItem With
(
Option<string> name = null,
Option<string> description = null
)
{
return new SomeItem
(
name.GetValueOrDefault(this.name),
description.GetValueOrDefault(this.description)
);
}
}
This allows you to do the updates like
var newItem = oldItem.With(name: "My name!");
I've used this approach with extension methods and T4s to great effect, but even when you write the code manually, it's reasonably reliable - if you add a new field, you must add it to the With as well, so it works quite well.
There's a few more approaches if you are willing to tolerate runtime code generation and reducing type safety, but that's kind of going against the grain IMO.
With C#9 we got the with operator for this purpose.
public record Car
{
public string Brand { get; init; }
public string Color { get; init; }
}
var car = new Car{ Brand = "BMW", Color = "Red" };
var anotherCar = car with { Brand = "Tesla"};
With-expressions When working with immutable data, a common pattern is
to create new values from existing ones to represent a new state. For
instance, if our person were to change their last name we would
represent it as a new object that’s a copy of the old one, except with
a different last name. This technique is often referred to as
non-destructive mutation. Instead of representing the person over
time, the record represents the person’s state at a given time. To
help with this style of programming, records allow for a new kind of
expression; the with-expression:
News in C#9
NOTE
With operator is only supported by records.
Records At the core of classic object-oriented programming is the idea that an object has strong identity and encapsulates mutable state
that evolves over time. C# has always worked great for that, But
sometimes you want pretty much the exact opposite, and here C#’s
defaults have tended to get in the way, making things very laborious.
Recods in C#9
What you are looking for is commonly called the with operator:
// returns a new immutable object with just the single property changed
someItem = { someItem with Name = "newName" };
Unfortunately, unlike F#, C# does not have such an operator (yet?).
Other C# developers are missing this feature as well, which is why someone wrote a Fody extension to do exactly that:
https://github.com/mikhailshilkov/With.Fody
Here's another approach, which implements an UpdateWith method manually but requires an Option<T> helper class. Luaan's answer describes this approach in more detail:
Implementing F#-inspired "with" updates for immutable classes in C#
Simple solution
I also thought about this question. Record's are not suitable for my purposes, since it is necessary to interact with EF Core.
I suggest a simple and low-cost way:
add a copy constructor to the class;
Make properties that change during cloning available for initialization;
clone an object with a change through the copy constructor with an initialization list:
var a = new SomeItem("name", "abracadabra");
var b = new SomeItem(a) {Description="descr"};
Simple code
var a = new SomeItem("name", "abracadabra");
var b = new SomeItem(a) {Description="descr"};
public class SomeItem
{
private string name;
private string description;
public SomeItem(string name, string description)
{
Name = name;
Description = description;
}
public SomeItem(SomeItem another): this(another.Name, another.Description)
{
}
public string Name
{
get => name;
init => name = value;
}
public string Description
{
get => description;
init => description = value;
}
}
Extended solution
If the final type is not known at compile time, then this approach is easy to extend. Let's say there is a class "ValueObject" whose derived types we need to clone.
Note: I apologize for the incorrect translation in some places. English version obtained using google.translate
Additional code
using System.Linq.Expressions;
using Light.GuardClauses;
using JetBrains.Annotations;
using static DotNext.Linq.Expressions.ExpressionBuilder;
using ValueObject = Company.Domain....;
/// <summary>
/// The plagiarizer creates a copy of the object with a change in its individual properties using an initializer
/// </summary>
/// <remarks> The foreign object must define a copy constructor, and mutable members must support initialization </remarks>
public struct Plagiarist {
/// <summary>
/// Object to be copied
/// </summary>
private readonly object _alienObject;
/// <summary>
/// Type <see cref="_alienObject" />
/// </summary>
private Type _type => _alienObject.GetType();
/// <summary>
/// Object parsing Expression
/// </summary>
private readonly ParsingInitializationExpression _parser = new();
public Plagiarist(object alienObject) {
_alienObject = alienObject.MustNotBeNullReference();
if (!CopyConstructorIs())
throw new ArgumentException($"Type {_type.FullName} must implement a copy constructor");
}
/// <summary>
/// Does the object we want to plagiarize have a copy constructor?
/// </summary>
/// <returns>True - there is a copy constructor, otherwise - false</returns>
[Pure]
private bool CopyConstructorIs() {
return _type.GetConstructor(new[] { _type }) is not null;
}
/// <summary>
/// Returns a copy of a foreign object with a change in its individual properties using an initializer
/// </summary>
/// <param name="initializer">
/// <see cref="Expression" /> create an object with initialization of those fields,
/// which need to be changed:
/// <code>() => new T() {Member1 = "Changed value1", Member2 = "Changed value2"}</code>
/// or <see cref="Expression" /> create an anonymous type with initialization of those fields
/// that need to be changed:
/// <code>() => new {Member1 = "Changed value1", Member2 = "Changed value2"}</code>
/// </param>
/// <returns></returns>
[Pure]
public object Plagiarize(Expression<Func<object>> initializer) {
var (newValues, constructParam) = _parser.ParseInitialization(initializer);
var constrCopies = _type.New(_alienObject.Const().Convert(_type));
Expression plagiarist = (newValues.Count, constructParam.Count) switch {
(> 0, _) => Expression.MemberInit(constrCopies, newValues.Values),
(0, > 0) => Expression.MemberInit(constrCopies, ConstructorInInitializationList(constructParam).Values),
_ => constrCopies
};
var plagiarize = Expression.Lambda<Func<object>>(plagiarist).Compile();
return plagiarize();
}
[Pure]
public Dictionary<string, MemberAssignment> ConstructorInInitializationList(
Dictionary<string, Expression> constructorParameters) {
Dictionary<string, MemberAssignment> initializer = new();
const BindingFlags flagReflections = BindingFlags.Default | BindingFlags.Instance | BindingFlags.Public;
var allProperties = _type.GetProperties(flagReflections);
var allFields = _type.GetFields(flagReflections);
foreach (var memberName in constructorParameters.Keys) {
var property = allProperties.FirstOrDefault(s => s.Name ==memberName);
var field = allFields.FirstOrDefault(s => s.Name == memberName);
(MemberInfo member, Type memberType) = (property, field) switch {
({ }, _) => (property, property.PropertyType),
(null, { }) => ((MemberInfo)field, field.FieldType),
_ => throw new ArgumentException($"{_type.FullName} does not contain member {memberName}")
};
initializer[memberName] = Expression.Bind(member, constructorParameters[memberName].Convert(memberType));
}
return initializer;
}
/// <summary>
/// Template "Visitor" for traversing the expression tree in order to highlight
/// initialization expression and constructor
/// </summary>
private class ParsingInitializationExpression : ExpressionVisitor {
private Dictionary<string, MemberAssignment>? _initializer;
private Dictionary<string, Expression>? _initializerAnonym;
/// <summary>
/// Parses the expression tree and returns the initializer and constructor parameters
/// </summary>
/// <param name="initializer"><see cref="Expression" /> to parse</param>
/// <returns> tuple of initializer and constructor</returns>
public ParsedInitialization ParseInitialization(Expression initializer) {
_initializer = new Dictionary<string, MemberAssignment>();
_initializerAnonym = new Dictionary<string, Expression>();
Visit(initializer);
return new ParsedInitialization(_initializer, _initializerAnonym);
}
protected override MemberAssignment VisitMemberAssignment(MemberAssignment node) {
_initializer![node.Member.Name] = node;
return base.VisitMemberAssignment(node);
}
protected override Expression VisitNew(NewExpression node) {
foreach (var (member, value) in node.Members?.Zip(node.Arguments) ??
Array.Empty<(MemberInfo First, Expression Second)>())
_initializerAnonym![member.Name] = value;
return base.VisitNew(node);
}
/// <summary>
/// Type to return values from method <see cref="ParseInitialization" />
/// </summary>
/// <param name="Initializer"></param>
/// <param name="ConstructorParameters"></param>
public record struct ParsedInitialization(Dictionary<string, MemberAssignment> Initializer,
Dictionary<string, Expression> ConstructorParameters);
}
}
public static class ValueObjectPlagiarizer{
/// <summary>
/// Creates a copy of the object with a change in its individual properties using an initializer
/// </summary>
/// <param name="alien">Object to be plagiarized</param>
/// <param name="initializer">
/// <see cref="Expression" /> creating an object of type <typeparamref name="T" />
/// with initialization of those fields that need to be changed:
/// <code>ob.Plagiarize(() => new T() {Member1 = "Changed value1", Member2 = "Changed value2"})</code>
/// or <see cref="Expression" /> create an anonymous type with initialization of those fields that need to be changed:
/// <code>ob.Plagiarize(() => new {Member1 = "Changed value1", Member2 = "Changed value2"})</code>
/// </param>
/// <returns>plagiarism of the object</returns>
public static object Plagiarize<T>(this ValueObject alien, Expression<Func<T>> initializer)
where T : class {
var bodyReduced = initializer.Convert<object>();
var initializerReduced = Expression.Lambda<Func<object>>(bodyReduced, initializer.Parameters);
return new Plagiarist(alien).Plagiarize(initializerReduced);
}
}
Usages
If SomeItem is a descendant of ValueObject
ValueObject a = new SomeItem("name", "abracadabra");
// via type constructor
var b = (SomeItem)a.Plagiarize(()=>new SomeItem(null){Description="descr"});
// anonymous type
var c = (SomeItem)a.Plagiarize(()=>new{Description="descr"});
b.Description.Should().Be("descr"); //true
c.Description.Should().Be("descr"); //true
If what you want to do is (as you commented) update the name of an existing object, a readonly property might be bad design.
Otherwise if its really a new object you want to create, you might want your class to implement some interface with a 'Dispose' method.
I am not sure how to seek help and this is kind of unusual I agree, so please pardon me. I'll try to explain as below:
• I am consuming JSON using POST and getting a dynamic object. I need to convert all incoming properties in dynamic object to uppercase.
• I am using what I think is a bodged solution. I'm converting JSON to string Dictionary, then putting values into a new dictionary after converting the Key to Key.ToUpper() and then deserializing it back into a dynamic object.
Current working solution is as follows:
string _StrJSON = #"{""FIELDA"" : ""1234"",""fieldb"" : ""OtherValue""}";
var d = JsonConvert.DeserializeObject<Dictionary<string, string>>(_StrJSON);
d.ContainsKey("FIELDB").Dump(); // returns false, obviously.
Dictionary<string, string> dr = new Dictionary<string, string>();
d.ToList().ForEach(r => dr.Add(r.Key.ToUpper(), r.Value));
dr.Dump("dr"); // FIELDA 1234 - FIELDB OtherValue
var a = JsonConvert.SerializeObject(dr);
a.Dump(); // {"FIELDA":"1234","FIELDB":"OtherValue"}
This works.
[EDIT]
Some confusion about my "var" above and other stuff. I am very clear on what is dynamic and what isn't.
[/EDIT]
What I have tried so far which DOES NOT WORK is as below:
namespace Newtonsoft.Json
{
/// <summary>
/// Converts JSON keys to uppercase.
/// </summary>
public class UppercaseContractResolver : Serialization.DefaultContractResolver
{
#region Methods.
#region Public Methods.
/// <summary>
/// Resolves property name for this JSON object.
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
protected override string ResolvePropertyName(string key)
{
return key.ToUpper();
}
#endregion
#endregion
#region Constructors.
/// <summary>
/// Converts JSON keys to uppercase.
/// </summary>
public UppercaseContractResolver()
{
}
#endregion
}
/// <summary>
/// Wrapper for Newtonsoft.Json.JsonConvert for JSON keys as uppercase.
/// </summary>
public static class JsonConvertUpper
{
#region Members.
#endregion
#region Methods.
#region Public Methods.
/// <summary>
/// Tries to serialize specified object with JSON keys in upper case.
/// <para>e.g. "key":value should become "KEY":value by using this method.</para>
/// </summary>
/// <param name="value">Object.</param>
/// <returns></returns>
public static string SerializeObjectUpper(object value)
{
return JsonConvert.SerializeObject(value, new JsonSerializerSettings
{
ContractResolver = new UppercaseContractResolver()
});
}
/// <summary>
/// Tries to Deserialize specified object with JSON keys in upper case.
/// <para>e.g. "key":value should become "KEY":value by using this method.</para>
/// </summary>
/// <param name="strValue">String.</param>
/// <returns></returns>
public static object DeserializeObjectUpper(string strValue)
{
// DeserializeObject does not allow uppercase properties. So use SerializeObjectUpper and then deserialize.
var value = JsonConvert.DeserializeObject(strValue);
string strJSON = SerializeObjectUpper(value);
return JsonConvert.DeserializeObject(strJSON, new JsonSerializerSettings()
{
ContractResolver = new UppercaseContractResolver()
});
}
#endregion
#endregion
}
}
The method call for above would be:
dynamic json = JsonConvertUpper.DeserializeObjectUpper(_StrJSON);
if (json.CTN== null)
{...}
[EDIT]
Please note I don't have classes as JSON keys have to be treated as UPPERCASE and classes and other code etc are all in ProperCase.
[/EDIT]
Any way of doing it inside JsonConvert at all? To avoid my manual patch? Any help is appreciated. Thanks.
The reason that your contract resolver does not work is that Json.NET's contract resolver defines how to map JSON from and to the properties of a POCO when serializing -- but when you deserialize to dynamic, Json.NET isn't actually mapping from and to a POCO, it's building up a JToken hierarchy directly from the JSON, the members of which implement IDynamicMetaObjectProvider. I.e.
dynamic o = JsonConvert.DeserializeObject(strJSON);
and
dynamic o = JToken.Parse(strJSON);
are the same.
Given that this is the case, the easiest way to do what you want would be to explicitly convert each property name to uppercase as the hierarchy is being built, like so:
public static class JsonExtensions
{
public static JToken ParseUppercase(string json)
{
using (var textReader = new StringReader(json))
using (var jsonReader = new JsonTextReader(textReader))
{
return jsonReader.ParseUppercase();
}
}
public static JToken ParseUppercase(this JsonReader reader)
{
return reader.Parse(n => n.ToUpperInvariant());
}
public static JToken Parse(this JsonReader reader, Func<string, string> nameMap)
{
JToken token;
using (var writer = new RenamingJTokenWriter(nameMap))
{
writer.WriteToken(reader);
token = writer.Token;
}
return token;
}
}
class RenamingJTokenWriter : JTokenWriter
{
readonly Func<string, string> nameMap;
public RenamingJTokenWriter(Func<string, string> nameMap)
: base()
{
if (nameMap == null)
throw new ArgumentNullException();
this.nameMap = nameMap;
}
public override void WritePropertyName(string name)
{
base.WritePropertyName(nameMap(name));
}
// No need to override WritePropertyName(string name, bool escape) since it calls WritePropertyName(string name)
}
And then use it like:
dynamic json = JsonExtensions.ParseUppercase(_StrJSON);
Sample fiddle.
I have a fairly generic 'rule' class that I am using to drive the behavior of an analysis engine I'm writing:
public class Rule
{
/// <summary>
/// The general rule type.
/// </summary>
public RuleType RuleType { get; set; }
/// <summary>
/// The human-readable description of the rule.
/// </summary>
public string RuleDescription { get; set; }
/// <summary>
/// The integer magnitude of the rule, if applicable.
/// </summary>
public int? RuleInt { get; set; }
/// <summary>
/// The boolean sign associated with the rule, if applicable.
/// </summary>
public bool? RuleBool { get; set; }
/// <summary>
/// The enum flag associated with the rule, if applicable. CAN be null.
/// </summary>
public System.Enum RuleFlagEnum { get; set; }
/// <summary>
/// A dumping ground for any other random crap I've failed to account for at this point in time.
/// </summary>
public object RuleObject { get; set; }
}
RuleType is a specific enum, like so:
public enum RuleType
{
Invalid,
ModifyDifficulty,
StrengthChange,
ColorChange,
SignChange
}
Using Json.NET, that both serializes and deserializes just fine.
RuleEnum, however, is giving me problems. Whether using the default enum serialization or the string enum serialization, the specific type of enum is not provided. As such, during deserialization, I am left with System.Enum and a string value, which is wholly unhelpful.
This is an example of the serialization, to show what I'm talking about:
{
"RuleType": "SignChange",
"RuleDescription": "Strength 1 Inversion Gate",
"RuleInt": 1,
"RuleFlagEnum": "Negative"
}
RuleFlagEnum, in this case, is referring to the enum:
public enum SignChange
{
Zero,
Positive,
Negative
}
I have tried using all of the TypeNameHandling options inside Json.NET. They only put type hinting on the objects, which doesn't help with RuleFlagEnum since it is technically a primitive.
I would really, really like to keep the enum at System.Enum so we can load any arbitrary enum in for later interpretation by the rule type, so the entire thing is more expandable. Is this possible?
The difficulty here is that System.Enum is an abstract class, so it is impossible to deserialize a value of unknown concrete type as such a type. Rather, one needs to have the specific type information in the JSON somewhere, however Json.NET will serialize an enum as a string or an integer (depending upon whether a StringEnumConverter is applied) -- but not an as an object, thus leaving no opportunity for a polymorphic "$type" property to be added.
The solution is, when serializing, to serialize a generic wrapper class that can convey the concrete type information:
public abstract class TypeWrapper
{
protected TypeWrapper() { }
[JsonIgnore]
public abstract object ObjectValue { get; }
public static TypeWrapper CreateWrapper<T>(T value)
{
if (value == null)
return new TypeWrapper<T>();
var type = value.GetType();
if (type == typeof(T))
return new TypeWrapper<T>(value);
// Return actual type of subclass
return (TypeWrapper)Activator.CreateInstance(typeof(TypeWrapper<>).MakeGenericType(type), value);
}
}
public sealed class TypeWrapper<T> : TypeWrapper
{
public TypeWrapper() : base() { }
public TypeWrapper(T value)
: base()
{
this.Value = value;
}
public override object ObjectValue { get { return Value; } }
public T Value { get; set; }
}
Then use serialize the wrapper when serializing your class:
/// <summary>
/// The enum flag associated with the rule, if applicable. CAN be null.
/// </summary>
[JsonIgnore]
public System.Enum RuleFlagEnum { get; set; }
[JsonProperty("RuleFlagEnum", TypeNameHandling = TypeNameHandling.All)]
TypeWrapper RuleFlagEnumValue
{
get
{
return RuleFlagEnum == null ? null : TypeWrapper.CreateWrapper(RuleFlagEnum);
}
set
{
if (value == null || value.ObjectValue == null)
RuleFlagEnum = null;
else
RuleFlagEnum = (Enum)value.ObjectValue;
}
}
This produces JSON like the following:
{
"RuleType": "ModifyDifficulty",
"RuleFlagEnum": {
"$type": "Question31351262.TypeWrapper`1[[Question31351262.MyEnum, MyApp]], MyApp",
"Value": "Two, Three"
},
}
I am receiving XML from the database and trying to process it into my system. I have generated a class from an XSD that I received from the other team. The problem I'm having is that, right now the property names in the XSD do not match up to the property names within my application (typical). Meaning in order for me to build an object that I can use I have to create an object and piece it together with the XML. Like this:
DynamicEntity auth = new DynamicEntity(EntityAuthorization);
auth.Properties["DealerId] = authprm.new_dealerid[0].Value;
auth.Properties["CarId"] = authprm.new_carid[0].Value;
etc...
I could potentially have 40 to 100 properties to set. I've thought about applying XmlElement to the class attributes but every time I have to regenerate the class (from the XML) I will lose the XmlElements that I applied earlier. What are some suggestions?
Thanks everyone!
You could use a metadata "buddy class", by putting the MetadataTypeAttribute on your class. I would make the class that gets generated partial if it isn't already and then create another partial class and add the MetadataType Attribute to that class. Then if you regenerate the class from the XSD and it's not partial the compiler will complain and you will remember to make it partial.
I don't really like "buddy classes" because the properties get duplicated, not really DRY, but it might be the solution you are looking for. For a simple overview of Metadata Class, which is usually about validation attributes - but it works for any attributes, you can find on MS here
You can see answer https://stackoverflow.com/a/26724847/1798889 for how to make the XmlSerializer know about a buddy class.
Update
Now that I think about this if you don't want to use the Buddy class you can build a fluent api to configure the XML Serializer.
We will keep the CustomAttributeProvider class from the XMLSerializer with buddy class question but instead make an XmlSerializerConfigurator class.
public class XmlSerializerConfigurator<T>
{
private readonly XmlAttributeOverrides _xmlAttributeOverrides;
public XmlSerializerConfigurator(XmlAttributeOverrides xmlAttributeOverrides)
{
_xmlAttributeOverrides = xmlAttributeOverrides;
}
public XmlSerializerConfigurator() : this(new XmlAttributeOverrides())
{
}
/// <summary>
/// Adds attributes to properties or fields and strongly typed
/// </summary>
/// <typeparam name="TData"></typeparam>
/// <param name="property"></param>
/// <param name="xmlAttributes"></param>
/// <returns></returns>
public XmlSerializerConfigurator<T> AddPropertyOrFieldAttributes<TData>(Expression<Func<T, TData>> property,
params Attribute[] xmlAttributes)
{
var memberName = property.GetName();
_xmlAttributeOverrides.Add(typeof (T), memberName,
new XmlAttributes(new CustomAttributeProvider(xmlAttributes)));
return this;
}
/// <summary>
/// Adds class level attributes
/// </summary>
/// <param name="xmlAttributes"></param>
/// <returns></returns>
public XmlSerializerConfigurator<T> AddClassLevelAttributes(params Attribute[] xmlAttributes)
{
_xmlAttributeOverrides.Add(typeof(T), new XmlAttributes(new CustomAttributeProvider(xmlAttributes)));
return this;
}
/// <summary>
/// Creates an XmlSerializerConfigurator that is tied to the main one
/// </summary>
/// <typeparam name="K"></typeparam>
/// <returns></returns>
public XmlSerializerConfigurator<K> ChildClassConfigurator<K>()
{
// passes in own XmlAttributeOverrides and since object reference it will fill the same object
return new XmlSerializerConfigurator<K>(_xmlAttributeOverrides);
}
/// <summary>
/// Returns back an XmlSerializer with this configuration
/// </summary>
/// <returns></returns>
public XmlSerializer GetSerializer()
{
return new XmlSerializer(typeof(T), _xmlAttributeOverrides);
}
}
This class will allow you to configure what attributes are on what class/property for the Serializer and not rely on the buddy class. We need the ChildClassAttributes for when the class has another class as it's property to allow configuring that class.
Also I use an extension method that returns back a property or field from an expression called GetName.
public static string GetName<TEntity, TData>(this Expression<Func<TEntity, TData>> field)
{
var name = "";
if (field.Body is MemberExpression)
{
var body = field.Body as MemberExpression;
var ebody = body.Expression as MemberExpression;
if (ebody != null)
{
name = ebody.Member.Name + ".";
}
name = name + body.Member.Name;
}
else if (field.Body is UnaryExpression)
{
var ubody = field.Body as UnaryExpression;
var body = ubody.Operand as MemberExpression;
var ebody = body.Expression as MemberExpression;
if (ebody != null)
{
name = ebody.Member.Name + ".";
}
name = name + body.Member.Name;
}
else if (field.Body is ConstantExpression)
{
var cbody = field.Body as ConstantExpression;
name = cbody.Value.ToString();
}
else
{
throw new InvalidOperationException(String.Format("{0} not supported.", field));
}
return name;
}
Now you can use/configure it this way
var xmlConfiguration = new XmlSerializerConfigurator<Group>();
xmlConfiguration.AddPropertyOrFieldAttributes(x => x.Employees, new XmlArrayItemAttribute(typeof (Employee)),
new XmlArrayItemAttribute(typeof (Manager)));
xmlConfiguration.AddClassLevelAttributes(new XmlRootAttribute("GroupName"));
var childConfiguration = xmlConfiguration.ChildClassConfigurator<Employee>();
childConfiguration.AddPropertyOrFieldAttributes(x => x.FullName, new XmlElementAttribute("Name"));
var xmlSerializer = xmlConfiguration.GetSerializer();
Now all the properties and fields are strongly typed and not duplicated in a buddy class.