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 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 dependency graph class built in c# with several private data properties that are supposed to only be accessed, but more importantly modified, through the standard 'getter / setter' methods. I came across a test which exposes the public interface through casting an IEnumerable as an ICollection. I am unsure how to prevent this obviously unwanted action.
Here is the failing unit test that confirms I am exposing private data:
[TestMethod]
public void PrivateDataTest()
{
try
{
DependencyGraph dg = new DependencyGraph();
dg.AddDependency("a", "b");
dg.AddDependency("a", "c");
ICollection<string> temp = (ICollection<string>)dg.GetDependents("a");
temp.Add("d");
Assert.IsTrue(new HashSet<string> { "b", "c", "d" }.SetEquals(temp));
Assert.IsTrue(new HashSet<string> { "b", "c" }.SetEquals(dg.GetDependents("a")));
}
catch (Exception e)
{
if (!(e is NotSupportedException || e is InvalidCastException))
Assert.Fail();
}
}
And here is my method 'GetDependents' which is called simultaneously to the ICollection cast.
/// <summary>
/// Enumerates dependents(s).
/// </summary>
public IEnumerable<string> GetDependents(string s)
{
try
{
return this.data[s].getDependencies();
}
catch (Exception e)
{
return new List<string>();
}
}
Additionally this is the 'data' property being accessed:
// key = node name, value = node object and all node data e.g. dependencies, dependees, name
private SortedDictionary<String, Node> data;
The 'data' property contains a string which represents the node's name e.g. "a", and a custom Node object. The name is the key to the actual Node object, which is a nested class that is used to store a node's name, and list of dependencies / dependees. Here is my my Node class (Note. I stripped away a lot of methods that are irrelevent for the purpose of this question):
public class Node
{
private String name;
private List<String> dependencies;
private List<String> dependees;
/// <summary>
/// construct a new Node by passing in a name and lists for both the dependencies and the dependees
/// </summary>
/// <param name="_name"></param>
/// <param name="_dependencies"></param>
/// <param name="_dependees"></param>
public Node(String _name, List<String> _dependencies, List<String> _dependees)
{
name = _name;
dependencies = _dependencies;
dependees = _dependees;
}
/// <summary>
/// construct a Node with just a name and blank dependent lists
/// </summary>
/// <param name="_name"></param>
public Node(String _name)
{
name = _name;
dependencies = new List<string>();
dependees = new List<string>();
}
/// <summary>
/// getter for the dependencies property
/// </summary>
public List<String> getDependencies()
{
return this.dependencies;
}
}
I hope this is enough information for someone to understand the scope of my question as it relates to my graph. I will reiterate; how would one go about preventing this breach of interface via casting? This is my first question on StackOverflow, and I'm also fairly new to the C# language, so I apologize if my formatting / lack of understanding is incorrigible.
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.
I have added a new control to my .NET form and I want to save its value in a table.I have added a new column in my table.How do you use MyGeneration Doodads to create a data access object for this table?I have looked at http://www.mygenerationsoftware.com/portal/doodads/cusage/tabid/53/default.aspx
but I can't understand what it means by "template".What is the procedure to regenerate doodads for a table?
You wont get much of a response on this... dOOdads have not been supported for many years. Regardless, we use dOOdads too and I just roll my own repositories for my WPF projects (I know it's not ASP, but I don't think you can just "plug-and-play"). Here is an example of my base lookup class:
public abstract class BaseLookup<TEntity>
{
// Constructor
protected BaseLookup()
{
this.SubsetIdentifier = null;
}
// Properties
public virtual object SubsetIdentifier { get; set; }
// Public Methods
public abstract IEnumerable<TEntity> Read();
public virtual TEntity ReadSingle()
{
return default(TEntity);
}
// Protected Methods
/// <summary>
/// Retrieve translated entities from the database. The methods used to do this
/// are specified from the child class as parameters (i.e. Action or Func delegates).
/// </summary>
/// <param name="loadSubsetFunc">Specify how to load a set of database records.
/// Return boolean confirmation that records were found.</param>
/// <param name="orderByAction">Specify what should happen to sort the results.</param>
/// <param name="translateRowFunc">Specify how a database record should translate to
/// a model entity. Return the new entity.</param>
/// <param name="moveNextFunc">Specify how the database row pointer should move on.
/// Return FALSE on a call to the final row.</param>
/// <returns>A set of translated entities from the database.</returns>
/// <example><code>
///
/// return base.ReloadRecords(
/// _dOOdad.LoadAll,
/// () =>
/// {
/// _dOOdad.Sort = _dOOdad.GetAutoKeyColumn();
/// },
/// () =>
/// {
/// var entity = new LookupEntity();
/// return entity.PopulateLookupEntity(_dOOdad.CurrentRow.ItemArray);
/// },
/// _dOOdad.MoveNext);
///
/// </code></example>
protected virtual IEnumerable<TEntity> ReloadRecords(Func<bool> loadSubsetFunc,
Action orderByAction, Func<TEntity> translateRowFunc, Func<bool> moveNextFunc)
{
// If records are found, sort them and return set of entities
if (loadSubsetFunc())
{
orderByAction();
do
{
var entity = translateRowFunc();
yield return entity;
}
while (moveNextFunc());
}
else
{
Debug.WriteLine(
string.Format(
"# ZERO records found: Returning empty set of {0}.",
typeof(TEntity).Name));
}
}
}
And here is a concrete implementation of the base lookup:
public class CyclesLookup : BaseLookup<BaseLookupEntity>
{
// Fields & Constructor
private readonly CYCLES _dOOdad;
public CyclesLookup(IDbConnection connection, string library)
{
_dOOdad = OpenConnection(connection, library);
}
// Base Override Methods
public override IEnumerable<BaseLookupEntity> Read()
{
// Clear old result set and settings
_dOOdad.FlushData();
// Reload the records and return them sorted and translated
return base.ReloadRecords(
_dOOdad.LoadAll,
() =>
{
_dOOdad.Sort = _dOOdad.GetAutoKeyColumn();
},
() =>
{
var entity = new LookupEntity();
entity.Description = string.Format("Cycles for {0}", _dOOdad.YEAR);
return entity.PopulateLookupEntity(_dOOdad.CurrentRow.ItemArray, true);
},
_dOOdad.MoveNext);
}
// Private Helper Methods
private static CYCLES OpenConnection(IDbConnection connection, string library)
{
var dOOdad = new CYCLES(connection);
dOOdad.SchemaGlobal = library + ".";
return dOOdad;
}
}
That PopulateLookupEntity method is just an extension method:
public static T PopulateLookupEntity<T>(this T entity, object[] rowItems,
bool noDescription = false) where T : BaseLookupEntity
{
int id = 0;
int.TryParse(rowItems[0].ToString(), out id);
entity.RecordID = id;
var attributesFirstIndex = 1;
if (!noDescription)
{
entity.Description = rowItems[1].ToString();
attributesFirstIndex = 2;
}
entity.Attributes = new object[rowItems.Length - attributesFirstIndex];
for (int index = attributesFirstIndex; index < rowItems.Length; index++)
{
entity.Attributes[index - attributesFirstIndex] = rowItems[index];
}
return (T)entity;
}
For non-lookups, I have a more complex BaseRepository class that inherits from BaseLookup. With that one, I don't use PopulateLookupEntity, but a private helper method like this:
private IPlanningGridHeader TranslateCurrentDoodadRow()
{
return new PlanningGridHeader()
{
PlanNumber = Convert.ToInt32(_dOOdad.PLANNUMBER),
Active = _dOOdad.ACTIVE == 1M,
Capturer = _dOOdad.CAPTURER,
Region = _dOOdad.REGION,
CorporateID = Convert.ToInt32(_dOOdad.CORPORATEID),
StartDate = _dOOdad.STARTDATE.ConvertDb2Date(),
EndDate = _dOOdad.ENDDATE.ConvertDb2Date(),
AdvertStartDate = _dOOdad.ADVERTSTARTDATE.ConvertDb2Date(),
AdvertEndDate = _dOOdad.ADVERTENDDATE.ConvertDb2Date(),
BpcsDealNumber = Convert.ToInt32(_dOOdad.BPCSDEALNUMBER),
Description = _dOOdad.DESCRIPTION,
DeactivationReason = _dOOdad.DEACTIVATIONREASON,
LastSavedUsername = _dOOdad.LASTUSER,
LastSavedDateTime = _dOOdad.LASTDATE.ConvertDb2Date().AddDb2Time(_dOOdad.LASTTIME)
};
}
Hope this helps :-)
Although it's old but I recommend staying away from this library. It has a serious bug that cause connection leaking, I bet the creator didn't check ADO.NET practices when coding it. Moreover, he didn't know how to deal with DBNull, thus he invented "string properties" along the side of normal properties to deal with NULL problem, turned generated code and programming model into a big mess, created more NullReferenceException when developer access normal properties which has value as null.
I collected all those problems from a 10+ years old legacy system using MyGeneration Doodads. Felt very happy that I could get rid of it at last.
If you're on the way of finding a DAC library for enterprise app, just don't pick this library.