I am trying to use CSVHelper to serialize a database that is constructed out of multiple classes like shown below. I would like to make the csv a bit more human readable by adding information on units (when appropriate) and by ordering the data so that the "Name" always appears first. The rest can come in whatever order.
I have a class like shown below.
[DataContract(IsReference = true)]
public class OpaqueMaterial : LibraryComponent
{
[DataMember]
[Units("W/m.K")]
public double Conductivity { get; set; } = 2.4;
[DataMember]
public string Roughness { get; set; } = "Rough";
}
[DataContract]
public abstract class LibraryComponent
{
[DataMember, DefaultValue("No name")]
public string Name { get; set; } = "No name";
}
To avoid writing seprarate read write functions for each class I am reading and writing with templated functions like given below:
public void writeLibCSV<T>(string fp, List<T> records)
{
using (var sw = new StreamWriter(fp))
{
var csv = new CsvWriter(sw);
csv.WriteRecords(records);
}
}
public List<T> readLibCSV<T>(string fp)
{
var records = new List<T>();
using (var sr = new StreamReader(fp))
{
var csv = new CsvReader(sr);
records = csv.GetRecords<T>().ToList();
}
return records;
}
That I then use in the code to read and write as such:
writeLibCSV<OpaqueMaterial>(folderPath + #"\OpaqueMaterial.csv", lib.OpaqueMaterial.ToList());
List<OpaqueMaterial> inOpaqueMaterial = readLibCSV<OpaqueMaterial>(folderPath + #"\OpaqueMaterial.csv");
The CSV output then looks like:
Conductivity, Roughnes, Name
2.4, Rough, No Name
I would like to come out as:
Name, Conductivity [W/m.K], Roughness
No Name, 2.4, Rough
I know that the reordering is possible using maps like:
public class MyClassMap : ClassMap<OpaqueMaterial>
{
public MyClassMap()
{
Map(m => m.Name).Index(0);
AutoMap();
}
}
I would like to make this abstract so that I dont have to apply a different mapping to every class. I was not able to find an example that could help with adding the custom headers. Any suggestions or help would be greatly appreciated.
You could create a generic version of ClassMap<T> that will automatically inspect the type T using reflection and then construct the mapping dynamically based on the properties it finds and based on the attributes that may or may not be attached to it.
Without knowing the CsvHelper library too well, something like this should work:
public class AutoMap<T> : ClassMap<T>
{
public AutoMap()
{
var properties = typeof(T).GetProperties();
// map the name property first
var nameProperty = properties.FirstOrDefault(p => p.Name == "Name");
if (nameProperty != null)
MapProperty(nameProperty).Index(0);
foreach (var prop in properties.Where(p => p != nameProperty))
MapProperty(prop);
}
private MemberMap MapProperty(PropertyInfo pi)
{
var map = Map(typeof(T), pi);
// set name
string name = pi.Name;
var unitsAttribute = pi.GetCustomAttribute<UnitsAttribute>();
if (unitsAttribute != null)
name = $"{name} {unitsAttribute.Unit}";
map.Name(name);
// set default
var defaultValueAttribute = pi.GetCustomAttribute<DefaultValueAttribute>();
if (defaultValueAttribute != null)
map.Default(defaultValueAttribute.Value);
return map;
}
}
Now, you just need to create a AutoMap<T> for every type T that you want to support.
I’ve added examples for a UnitsAttribute and the DefaultValueAttribute, that should give you an idea on how to proceed with more attributes if you need more.
Related
I am facing an issue, surely due to my lack of knowledge in the reflection process, while trying to set a "complex" class hierarchy based on Json files.
Here are my main model :
public class Names
{
public Weapons Weapons { get; set; }
public Armors Armors { get; set; }
public Utilities Utilities { get; set; }
public Names()
{
Weapons = new Weapons();
Armors = new Armors();
Utilities = new Utilities();
}
}
Each of them having a list of sub-model like this:
public class Weapons
{
public BattleAxe BattleAxe { get; set; } = new BattleAxe();
public Bomb_Missile Bomb_Missile { get; set; } = new Bomb_Missile();
// etc... Around 20 to 25
}
And finally the ended model which is the exact equivalent of each json files but may have very different properties :
public class BattleAxe
{
public string[] Normal { get; set; } = new string[0];
public string[] DescriptiveAdjective { get; set; } = new string[0];
public string[] Material { get; set; } = new string[0];
public string[] Type { get; set; } = new string[0];
public string[] Title { get; set; } = new string[0];
public string[] Of { get; set; } = new string[0];
public string[] NormalForTitle { get; set; } = new string[0];
}
Since the MS Json deserializer does not support the conversion to a $type as Newtonsoft before, I tried to populate the values using reflection too like this (I've removed all the null-check for code readability) :
public static void Load()
{
Names = new Names();
foreach (var category in Names.GetType().GetProperties())
{
if (category is not null && !(category.GetGetMethod()?.IsStatic ?? false))
{
var categoryType = category.PropertyType;
foreach (var item in category.PropertyType.GetProperties())
{
var itemType = item.PropertyType;
var subTypeData = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(File.ReadAllText($"./Assets/Names/{categoryType.Name}/{itemType.Name}.json"));
var concreteObj = Activator.CreateInstance(itemType);
foreach (var key in subTypeData.Keys)
{
if (itemType.GetProperty(key) is not null && concreteObj is not null)
{
var prop = concreteObj.GetType().GetProperty(key);
var convertedValue = ConvertJsonType(subTypeData[key], subTypeData[key].ValueKind, out var isReferenceType);
// It fails here
prop.SetValue(
isReferenceType ? convertedValue : null,
!isReferenceType ? convertedValue : null
);
}
}
item.SetValue(concreteObj, null);
}
}
}
}
So it fails at the prop.SetValue(...) of the deepest object in the hierarchy with a different error depending on the type of value to set.
If it is a reference, it throws a System.Reflection.TargetException : 'Object does not match target type' Exception
And if it is value, it throw a System.Reflection.TargetException : 'Non-static method requires a target.'
Knowing that I do not have problems around the deserialization as shown here, only the fact that I use a dynamic type (and my instinct tells me it is actually the problem...)
I do not add the ConvertJsonType(...) body as it is functional and really simple
I am more interested in the 'why' than the 'how' so if you can explain me the 'theory' behind the problem, that would help quite a lot :)
Thank you!
PS: I know I can simplify the things in a more readable/performant way but I must achieve it with reflection for personal learning :)
Same for the System.Text.Json namespace, I do not intend to switch back to Newtonsoft for that
When calling SetValue(instance, value) you should pass the object which property should be set.
It's a wild guess, but you could try this:
prop.SetValue(concreteObj,
!isReferenceType ? convertedValue : null);
Because you want to fill the properties of concreteObj, not the value it self.
If you look at the object prop it was a return value of concreteObj.GetType().GetProperty(key);. If you look at it close, The GetProperty is a method from Type which isn't bound to any instance. So that's why you need to pass the instance of the object as the first parameter.
I mean this in a positive way: The itemType.GetProperty(key) is called every iteration, it will be the same value each iteration, you could bring it before the loop.
As docs state TargetException is thrown when:
The type of obj does not match the target type, or a property is an instance property but obj is null.
Passing null for obj in SetValue is valid when you are trying to set value for static property, not an instance one. Property type being a reference one has nothing to do with property being instance or static one so your call should look something like:
prop.SetValue(concreteObj, convertedValue);
Also your item.SetValue(concreteObj, null); does not look right cause concreteObj should be second argument in this call. Something like this:
item.SetValue(Names, concreteObj);
Also if you want only instance properties you can provide BindingFlags to get only instance properties:
foreach (var category in Names.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
Also I would say that category is not null check is redundant so in pair with providing BindingFlags you should remove the if completely.
What Utility or Pattern can be used to solve this Issue? I don't know what can be used to assist with this. Can we use some type of pattern?
If you have the following abstract class:
abstract class Foo
{
function void Something()
{
// Get the media type
}
}
And the following classes that derive from that class:
class Foo1 : Foo
{
public string MyId {get;set}
public string MyFile {get;set}
public TxtFile MyTextFile {get;set}
function void myFooFunction()
{
// Save File to Txt
}
}
class Foo2 : Foo
{
public string MyId {get;set}
public string MyFile {get;set}
public XMLFile MyXMLFile {get;set}
function MyOtherFunction()
{
// Save to XML
}
}
Then in Linq (or Similar) within the repository you do something like this:
var a = (from e in db.myTable
where e.myFileType == "XML"
Select e);
Then we have to map this to the correct object. Like this:
Foo newFoo = FooFactory.CreateFooFor(a.myFileType.ToString())
newFoo.MyId = a.id;
newFoo.MyFile = a.myfile;
newFoo.MyXMLFile = a.xml;
The Factory certainly helps, but how do you do this for multiple "FileTypes" like txt for example? The Fields wouldn't match up!
Do I have to write more code that does the same thing??
I feel like there has to be something that can do this.
First, if myFooFunction and MyOtherFunction are both used to save, you can use the strategy pattern and just define and abstract Save() method to implement in derived classes. You might also look at the Template Method pattern.
Although this isn't exactly a pattern, you might also want to apply the "Pull Up Field" refactoring here and put the MyId and MyFile properties in the parent class.
For creation...
The Builder pattern is similar to the factory, but allows for more complex object creation. I don't know how well it fits this simplified example, but it might fit what you are actually trying to do in your real code. Probably not. I just mention it first because it is the closest to factory in my mind.
There are also the Mapper Pattern and the Data Mapper Pattern. You might encapsulate the mapping in an object and have the factory return a mapper:
FooMapper mapper = FooMapperFactory.CreateFooMapperFor(a.myFileType);
Foo newFoo = mapper.CreateFoo(a);
I believe you could solve your problem using generics. I took the liberty of changing around some code. Would this work?
public abstract class Foo
{
public abstract void Save();
public void Something()
{
// Get the media type
}
}
public class FooText : Foo
{
public string MyId { get; set; }
public string MyFile { get; set; }
public string MyTextFile { get; set; }
public override void Save()
{
// Save File to Txt
}
}
public class FooXml : Foo
{
public string MyId { get; set; }
public string MyFile { get; set; }
public string MyXMLFile { get; set; }
public override void Save()
{
// Save to XML
}
}
public class FooFactory<T> where T : Foo, new()
{
public static T CreateFoo()
{
return new T();
}
}
If you consider using reflection on the data that's returned from the database, or perhaps the Adapter pattern you can set up a dynamic way to map fields to each other. Using reflection (the following is pseudo logic as reflection is kind of messy code to provide):
Get a list of PropertyInfo objects for all public properties from the target type
Get a list of PropertyInfo objects for all public properties from the database type
Compare their names/types to create the mapping
Assign values from the database type, using reflection, to the target type
Something like this will do the trick:
public void AssignAndMapTypes<DatabaseType, TargetType>(DatabaseType db, ref TargetType target)
{
var dbType = db.GetType();
var dbTypeProperties = dbType.GetProperties(System.Reflection.BindingFlags.Public);
var targetType = target.GetType();
var targetTypeProperties = targetType.GetProperties(System.Reflection.BindingFlags.Public);
foreach (var prop in targetTypeProperties)
{
var matchingProp = dbTypeProperties.Where(e => { return (string.Compare(e.Name, prop.Name, true) == 0) && (e.PropertyType == prop.PropertyType) }).FirstOrDefault();
if(matchingProp != null)
{
prop.SetValue(target, matchingProp.GetValue(db, null), null);
}
}
}
I've read somewhere a comment by the author of ProtoBuf.NET that:
There are options to automatically infer the numbers, but that is brittle and not recommended. Only use this if you know you never need to add more members (it orders them alphabetically, so adding a new AardvarkCount will break everything).
This is exactly that sort of situation I am interested in :)
I have something that is akin to a map-reduce scenario where I want to serialize results generated on remote machines using protocol buffers (e.g. the "map" side of map-reduce) and later read them and combine those results for further processing (e.g. the "reduce" side).
I don't want to start an attribute decoration marathon over every possible class I have that might get serialized during this process, and I do find the protocol buffers to be very alluring as I can create result with Mono and consume them effortlessly on MS.NET and vice-versa...
The apparent downsides of not pre-tagging the members doesn't bother me as exactly the same software revision does generation/consumptionn, so I don't need do worry about new members popping up in the code and messing my whole scheme...
So in short, my question is:
How do I do it (Serialize with ProtoBuf.NET without tagging/building Meta classes on my own)?
Is there any hole in my scheme that I've glaringly missed?
If you can live with a single attribute, then the trick is:
[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
public class WithImplicitFields
{
public int X { get; set; }
public string Y { get; set; }
}
there are 2 options here; AllPublic works like XmlSerializer - public properties and fields are serialized (using the alphabetic order to choose tag numbers); AllFields works a bit like BinaryFormatter - the fields are serialized (again, alphabetic).
I can't remember if this is yet available on the v2 API; I know it is on my list of things to ensure work! But if you want it in v2 without any attributes, I'm sure I can add an Add(ImplicitFields) overload.
As long as the 2 ends are never out of step, this is fine. If you store the data, or don't version the two ends "in step", then there could be problems. See also the intellisense comments on the enum (which pretty much repeats the warning that you are already aware of).
I had the same problem and that how I've resolved it with TypeModel. It's based on properties ordered by their name (however it doesn't check on property setter/getter existence or serialize-ability of a given type):
[TestFixture]
public class InferredProtoPoc
{
[Test]
public void UsageTest()
{
var model = TypeModel.Create();
// Dynamically create the model for MyPoco
AddProperties(model, typeof(MyPoco));
// Display the Generated Schema of MyPoco
Console.WriteLine(model.GetSchema(typeof(MyPoco)));
var instance = new MyPoco
{
IntegerProperty = 42,
StringProperty = "Foobar",
Containers = new List<EmbeddedPoco>
{
new EmbeddedPoco { Id = 12, Name = "MyFirstOne" },
new EmbeddedPoco { Id = 13, Name = "EmbeddedAgain" }
}
};
var ms = new MemoryStream();
model.Serialize(ms, instance);
ms.Seek(0, SeekOrigin.Begin);
var res = (MyPoco) model.Deserialize(ms, null, typeof(MyPoco));
Assert.IsNotNull(res);
Assert.AreEqual(42, res.IntegerProperty);
Assert.AreEqual("Foobar", res.StringProperty);
var list = res.Containers;
Assert.IsNotNull(list);
Assert.AreEqual(2, list.Count);
Assert.IsTrue(list.Any(x => x.Id == 12));
Assert.IsTrue(list.Where(x => x.Id == 12).Any(x => x.Name == "MyFirstOne"));
Assert.IsTrue(list.Any(x => x.Id == 13));
Assert.IsTrue(list.Where(x => x.Id == 13).Any(x => x.Name == "EmbeddedAgain"));
}
private static void AddProperties(RuntimeTypeModel model, Type type)
{
var metaType = model.Add(type, true);
foreach (var property in type.GetProperties().OrderBy(x => x.Name))
{
metaType.Add(property.Name);
var propertyType = property.PropertyType;
if (!IsBuiltinType(propertyType) &&
!model.IsDefined(propertyType) &&
propertyType.GetProperties().Length > 0)
{
AddProperties(model, propertyType);
}
}
}
private static bool IsBuiltinType(Type type)
{
return type.IsValueType || type == typeof (string);
}
}
public class MyPoco
{
public int IntegerProperty { get; set; }
public string StringProperty { get; set; }
public List<EmbeddedPoco> Containers { get; set; }
}
public class EmbeddedPoco
{
public int Id { get; set; }
public String Name { get; set; }
}
And that's what you get from running it.
message EmbeddedPoco {
optional int32 Id = 1;
optional string Name = 2;
}
message MyPoco {
repeated EmbeddedPoco Containers = 1;
optional int32 IntegerProperty = 2;
optional string StringProperty = 3;
}
For performance, you could opt to compile the TypeModel, and/or store the generated proto for future uses. Beware however that hidden dependency on Protocol Buffer could be dangerous in the long run if the poco (Plain old container object) evolves.
I have a class MyDatabaseContext that has a series of DbSet collection properties:
public DbSet<EntityA> EntitiesA { get; set; }
public DbSet<EntityB> EntitiesB { get; set; }
public DbSet<EntityC> EntitiesC { get; set; }
I need to get the name of the collection given the type of the entity.
For example, I have "EntityB" and want to get as a result "EntitiesB".
I really wanted to avoid switch-case statements, since MyDatabaseContext is generated automatically (T4 templates).
if you just want the name of the property here you go. I would just refine the answer given by hunter. You can use the same method with string as return type.
public string GetEntitiName<T>() where T : class
{
PropertyInfo propInfo = typeof(MyDatabaseContext).GetProperties().Where(p => p.PropertyType == typeof(DbSet<T>)).FirstOrDefault();
string propertyName = propInfo.Name; //The string has the property name ..
return propertyName;
}
I tried a sample similar to your situation. Try replacing List with DbSet.
class Program
{
public static void GetEntities<T>() where T : class
{
var info = typeof(TestClass1).GetProperties().Where(p => p.PropertyType == typeof(List<T>));
Console.WriteLine(info.FirstOrDefault().Name);
}
static void Main(string[] args)
{
GetEntities<int>();
Console.ReadLine();
}
}
public class TestClass1
{
public List<int> IntTest { get; set; }
public List<double> DoubleTest { get; set; }
public List<string> IStringTest { get; set; }
}
This sample works.
I know this is old page, But my answer maybe useful for other guys referring here. (like me)
I think you want to accessing EntitiesB to run a query on it, like EntitiesB.Where(a=>a.bla=="blabla"). If I'm right or another visitor of this page needs something like this, just easily use the following code:
using System.Data.Entity.Infrastructure;
using System.Data.Objects;
((IObjectContextAdapter)_dbContext).ObjectContext.CreateObjectSet<EntityB>()
Description:
_dbContext is Context class inherting from DbContext.
EntitiesB is DbSet<EntityB> defined in Context class.
Example:
Ilist result = ((IObjectContextAdapter)_dbContext).ObjectContext.CreateObjectSet<EntityB>().Where(b=>b.bla=="blabla").ToList();
Your generated file is a partial class, you could create a new file and declare a class with same name using the keyword partial, then make a method which will return the desired Collection...
I haven't actually done this myself, but it sounds like what you want to do is to use reflection to locate the property of type "DbSet" that has the appropriate generic type parameter. The following pseudo-C# should get you started:
foreach ( FieldInfo field in this.GetType() )
{
if ( field.FieldType.IsGenericType )
{
foreach ( Type param in field.FieldType.GetGenericArguments() )
{
if ( param.Name == soughtType )
{
return field.Name;
}
}
}
}
I would like to automatically generate SQL statements from a class instance. The method should look like Update(object[] Properties, object PrimaryKeyProperty). The method is part of an instance (class, base method - generic for any child). Array of properties is an array of class properties, that will be used in update statement. Property names are equal to table field names.
The problem is that I can't get property names.
Is there any option to get a property name inside class instance?
sample:
public class MyClass {
public int iMyProperty { get; set; }
public string cMyProperty2 { get; set; }
{
main() {
MyClass _main = new MyClass();
_main.iMyProperty.*PropertyName* // should return string "iMyProperty"
{
I am aware of PropertyInfo, but I don't know hot to get the ID of a property from GetProperties() array.
Any suggestion?
Just wrote an implementation of this for a presentation on lambdas for our usergroup last Tuesday.
You can do
MembersOf<Animal>.GetName(x => x.Status)
Or
var a = new Animal()
a.MemberName(x => x.Status)
the code:
public static class MembersOf<T> {
public static string GetName<R>(Expression<Func<T,R>> expr) {
var node = expr.Body as MemberExpression;
if (object.ReferenceEquals(null, node))
throw new InvalidOperationException("Expression must be of member access");
return node.Member.Name;
}
}
Link to the presentation and code samples.
Also in SVN (more likely to be updated): http://gim-projects.googlecode.com/svn/presentations/CantDanceTheLambda
I found a perfect solution in This Post
public static string GetPropertyName<T>(Expression<Func<T>> propertyExpression)
{
return (propertyExpression.Body as MemberExpression).Member.Name;
}
And then for the usage :
var propertyName = GetPropertyName(
() => myObject.AProperty); // returns "AProperty"
Works like a charm
You can do something like this:
Type t = someInstance.getType();
foreach (MemberInfo mi in t.GetMembers())
{
if (mi.MemberType == MemberTypes.Property)
{
Console.WriteLine(mi.Name);
}
}
to get all the property names for instance's type.
You can get the name (I assume that's what you meant by ID) of a property using PropertyInfo.Name. Just loop through the PropertyInfo[] returned from typeof(className).GetProperties()
foreach (PropertyInfo info in typeof(MyClass).GetProperties())
{
string name = info.Name;
// use name here
}
Since you already have an explicit handle to the specific property you want, you know the name - can you just type it?
Not 100% sure if this will get you what you're looking for, this will fetch all properties with [Column] attribute inside your class:
In the datacontext I have:
public ReadOnlyCollection<MetaDataMember> ColumnNames<TEntity>( )
{
return this.Mapping.MappingSource.GetModel(typeof(DataContext)).GetMetaType(typeof(TEntity)).DataMembers;
}
Fetching the table column-names that are properties inside the class:
MyDataContext db = GetDataContext();
var allColumnPropertyNames = db.ColumnNames<Animal>().Where(n => n.Member.GetCustomAttributes(typeof(System.Data.Linq.Mapping.ColumnAttribute), false).FirstOrDefault() != null).Select(n => n.Name);
Let's say (from the first sample, method update of a class MyClass):
public class MyClass {
public int iMyStatusProperty { get; set; }
public int iMyKey { get; set; }
public int UpdateStatusProperty(int iValue){
this.iMyStatusProperty = iValue;
return _Update( new[iMyStatusProperty ], iMyKey); // this should generate SQL: "UPDATE MyClass set iMyStatusProperty = {iMyStatusProperty} where iMyKey = {iMyKey}"
}
{iMyStatusProperty} and {iMyKey} are property values of a class instance.
So, the problem is how to get property name (reflection) from a property without using names of properties as strings (to avoid field name typos).