Get properties in order of declaration using reflection - c#

I need to get all the properties using reflection in the order in which they are declared in the class. According to MSDN the order can not be guaranteed when using GetProperties()
The GetProperties method does not return properties in a particular
order, such as alphabetical or declaration order.
But I've read that there is a workaround by ordering the properties by the MetadataToken. So my question is, is that safe? I cant seem find any information on MSDN about it. Or is there any other way of solving this problem?
My current implementation looks as follows:
var props = typeof(T)
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.OrderBy(x => x.MetadataToken);

On .net 4.5 (and even .net 4.0 in vs2012) you can do much better with reflection using clever trick with [CallerLineNumber] attribute, letting compiler insert order into your properties for you:
[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
public sealed class OrderAttribute : Attribute
{
private readonly int order_;
public OrderAttribute([CallerLineNumber]int order = 0)
{
order_ = order;
}
public int Order { get { return order_; } }
}
public class Test
{
//This sets order_ field to current line number
[Order]
public int Property2 { get; set; }
//This sets order_ field to current line number
[Order]
public int Property1 { get; set; }
}
And then use reflection:
var properties = from property in typeof(Test).GetProperties()
where Attribute.IsDefined(property, typeof(OrderAttribute))
orderby ((OrderAttribute)property
.GetCustomAttributes(typeof(OrderAttribute), false)
.Single()).Order
select property;
foreach (var property in properties)
{
//
}
If you have to deal with partial classes, you can additionaly sort the properties using [CallerFilePath].

If you're going the attribute route, here's a method I've used in the past;
public static IOrderedEnumerable<PropertyInfo> GetSortedProperties<T>()
{
return typeof(T)
.GetProperties()
.OrderBy(p => ((Order)p.GetCustomAttributes(typeof(Order), false)[0]).Order);
}
Then use it like this;
var test = new TestRecord { A = 1, B = 2, C = 3 };
foreach (var prop in GetSortedProperties<TestRecord>())
{
Console.WriteLine(prop.GetValue(test, null));
}
Where;
class TestRecord
{
[Order(1)]
public int A { get; set; }
[Order(2)]
public int B { get; set; }
[Order(3)]
public int C { get; set; }
}
The method will barf if you run it on a type without comparable attributes on all of your properties obviously, so be careful how it's used and it should be sufficient for requirement.
I've left out the definition of Order : Attribute as there's a good sample in Yahia's link to Marc Gravell's post.

According to MSDN MetadataToken is unique inside one Module - there is nothing saying that it guarantees any order at all.
EVEN if it did behave the way you want it to that would be implementation-specific and could change anytime without notice.
See this old MSDN blog entry.
I would strongly recommend to stay away from any dependency on such implementation details - see this answer from Marc Gravell.
IF you need something at compile time you could take a look at Roslyn (although it is in a very early stage).

Another possibility is to use the System.ComponentModel.DataAnnotations.DisplayAttribute Order property.
Since it is builtin, there is no need to create a new specific attribute.
Then select ordered properties like this
const int defaultOrder = 10000;
var properties = type.GetProperties().OrderBy(p => p.FirstAttribute<DisplayAttribute>()?.GetOrder() ?? defaultOrder).ToArray();
And class can be presented like this
public class Toto {
[Display(Name = "Identifier", Order = 2)
public int Id { get; set; }
[Display(Name = "Description", Order = 1)
public string Label {get; set; }
}

What I have tested sorting by MetadataToken works.
Some of users here claims this is somehow not good approach / not reliable, but I haven't yet seen any evidence of that one - perhaps you can post some code snipet here when given approach does not work ?
About backwards compatibility - while you're now working on your .net 4 / .net 4.5 - Microsoft is making .net 5 or higher, so you pretty much can assume that this sorting method won't be broken in future.
Of course maybe by 2017 when you will be upgrading to .net9 you will hit compatibility break, but by that time Microsoft guys will probably figure out the "official sort mechanism". It does not makes sense to go back or break things.
Playing with extra attributes for property ordering also takes time and implementation - why to bother if MetadataToken sorting works ?

You may use DisplayAttribute in System.Component.DataAnnotations, instead of custom attribute. Your requirement has to do something with display anyway.

If you can enforce your type has a known memory layout, you can rely on StructLayout(LayoutKind.Sequential) then sort by the field offsets in memory.
This way you don't need any attribute on each field in the type.
Some serious drawbacks though:
All field types must have a memory representation (practically no other reference types other than fixed-length arrays or strings). This includes parent types, even if you just want to sort the child type's fields.
You can use this for classes including inheritance, but all parent classes need to also have sequential layout set.
Obviously, this doesn't sort properties but fields might be fine for POCOs.
[StructLayout(LayoutKind.Sequential)]
struct TestStruct
{
public int x;
public decimal y;
}
[StructLayout(LayoutKind.Sequential)]
class TestParent
{
public int Base;
public TestStruct TestStruct;
}
[StructLayout(LayoutKind.Sequential)]
class TestRecord : TestParent
{
public bool A;
public string B;
public DateTime C;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 42)] // size doesn't matter
public byte[] D;
}
class Program
{
static void Main(string[] args)
{
var fields = typeof(TestRecord).GetFields()
.OrderBy(field => Marshal.OffsetOf(field.DeclaringType, field.Name));
foreach (var field in fields) {
Console.WriteLine($"{field.Name}: {field.FieldType}");
}
}
}
Outputs:
Base: System.Int32
TestStruct: TestStruct
A: System.Boolean
B: System.String
C: System.DateTime
D: System.Byte[]
If you try to add any forbidden field types, you'll get System.ArgumentException: Type 'TestRecord' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed.

I did it this way:
internal static IEnumerable<Tuple<int,Type>> TypeHierarchy(this Type type)
{
var ct = type;
var cl = 0;
while (ct != null)
{
yield return new Tuple<int, Type>(cl,ct);
ct = ct.BaseType;
cl++;
}
}
internal class PropertyInfoComparer : EqualityComparer<PropertyInfo>
{
public override bool Equals(PropertyInfo x, PropertyInfo y)
{
var equals= x.Name.Equals(y.Name);
return equals;
}
public override int GetHashCode(PropertyInfo obj)
{
return obj.Name.GetHashCode();
}
}
internal static IEnumerable<PropertyInfo> GetRLPMembers(this Type type)
{
return type
.TypeHierarchy()
.SelectMany(t =>
t.Item2
.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(prop => Attribute.IsDefined(prop, typeof(RLPAttribute)))
.Select(
pi=>new Tuple<int,PropertyInfo>(t.Item1,pi)
)
)
.OrderByDescending(t => t.Item1)
.ThenBy(t => t.Item2.GetCustomAttribute<RLPAttribute>().Order)
.Select(p=>p.Item2)
.Distinct(new PropertyInfoComparer());
}
with the property declared as follows:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class RLPAttribute : Attribute
{
private readonly int order_;
public RLPAttribute([CallerLineNumber]int order = 0)
{
order_ = order;
}
public int Order { get { return order_; } }
}

Building on the above accepted solution, to get the exact Index you could use something like this
Given
public class MyClass
{
[Order] public string String1 { get; set; }
[Order] public string String2 { get; set; }
[Order] public string String3 { get; set; }
[Order] public string String4 { get; set; }
}
Extensions
public static class Extensions
{
public static int GetOrder<T,TProp>(this T Class, Expression<Func<T,TProp>> propertySelector)
{
var body = (MemberExpression)propertySelector.Body;
var propertyInfo = (PropertyInfo)body.Member;
return propertyInfo.Order<T>();
}
public static int Order<T>(this PropertyInfo propertyInfo)
{
return typeof(T).GetProperties()
.Where(property => Attribute.IsDefined(property, typeof(OrderAttribute)))
.OrderBy(property => property.GetCustomAttributes<OrderAttribute>().Single().Order)
.ToList()
.IndexOf(propertyInfo);
}
}
Usage
var myClass = new MyClass();
var index = myClass.GetOrder(c => c.String2);
Note, there is no error checking or fault tolerance, you can add pepper and salt to taste

If you are happy with the extra dependency, Marc Gravell's Protobuf-Net can be used to do this without having to worry about the best way to implement reflection and caching etc. Just decorate your fields using [ProtoMember] and then access the fields in numerical order using:
MetaType metaData = ProtoBuf.Meta.RuntimeTypeModel.Default[typeof(YourTypeName)];
metaData.GetFields();

Even it's a very old thread, here is my working solution based on #Chris McAtackney
var props = rootType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.OrderBy(p =>
(
p.GetCustomAttributes(typeof(AttrOrder), false).Length != 0 ? // if we do have this attribute
((p.GetCustomAttributes(typeof(AttrOrder), false)[0]) as AttrOrder).Order
: int.MaxValue // or just a big value
)
);
And the Attribute is like this
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class AttrOrder : Attribute
{
public int Order { get; }
public AttrOrder(int order)
{
Order = order;
}
}
Use like this
[AttrOrder(1)]
public string Name { get; set; }

Related

How to get fields and properties in order as declared in class? [duplicate]

I need to get all the properties using reflection in the order in which they are declared in the class. According to MSDN the order can not be guaranteed when using GetProperties()
The GetProperties method does not return properties in a particular
order, such as alphabetical or declaration order.
But I've read that there is a workaround by ordering the properties by the MetadataToken. So my question is, is that safe? I cant seem find any information on MSDN about it. Or is there any other way of solving this problem?
My current implementation looks as follows:
var props = typeof(T)
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.OrderBy(x => x.MetadataToken);
On .net 4.5 (and even .net 4.0 in vs2012) you can do much better with reflection using clever trick with [CallerLineNumber] attribute, letting compiler insert order into your properties for you:
[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
public sealed class OrderAttribute : Attribute
{
private readonly int order_;
public OrderAttribute([CallerLineNumber]int order = 0)
{
order_ = order;
}
public int Order { get { return order_; } }
}
public class Test
{
//This sets order_ field to current line number
[Order]
public int Property2 { get; set; }
//This sets order_ field to current line number
[Order]
public int Property1 { get; set; }
}
And then use reflection:
var properties = from property in typeof(Test).GetProperties()
where Attribute.IsDefined(property, typeof(OrderAttribute))
orderby ((OrderAttribute)property
.GetCustomAttributes(typeof(OrderAttribute), false)
.Single()).Order
select property;
foreach (var property in properties)
{
//
}
If you have to deal with partial classes, you can additionaly sort the properties using [CallerFilePath].
If you're going the attribute route, here's a method I've used in the past;
public static IOrderedEnumerable<PropertyInfo> GetSortedProperties<T>()
{
return typeof(T)
.GetProperties()
.OrderBy(p => ((Order)p.GetCustomAttributes(typeof(Order), false)[0]).Order);
}
Then use it like this;
var test = new TestRecord { A = 1, B = 2, C = 3 };
foreach (var prop in GetSortedProperties<TestRecord>())
{
Console.WriteLine(prop.GetValue(test, null));
}
Where;
class TestRecord
{
[Order(1)]
public int A { get; set; }
[Order(2)]
public int B { get; set; }
[Order(3)]
public int C { get; set; }
}
The method will barf if you run it on a type without comparable attributes on all of your properties obviously, so be careful how it's used and it should be sufficient for requirement.
I've left out the definition of Order : Attribute as there's a good sample in Yahia's link to Marc Gravell's post.
According to MSDN MetadataToken is unique inside one Module - there is nothing saying that it guarantees any order at all.
EVEN if it did behave the way you want it to that would be implementation-specific and could change anytime without notice.
See this old MSDN blog entry.
I would strongly recommend to stay away from any dependency on such implementation details - see this answer from Marc Gravell.
IF you need something at compile time you could take a look at Roslyn (although it is in a very early stage).
Another possibility is to use the System.ComponentModel.DataAnnotations.DisplayAttribute Order property.
Since it is builtin, there is no need to create a new specific attribute.
Then select ordered properties like this
const int defaultOrder = 10000;
var properties = type.GetProperties().OrderBy(p => p.FirstAttribute<DisplayAttribute>()?.GetOrder() ?? defaultOrder).ToArray();
And class can be presented like this
public class Toto {
[Display(Name = "Identifier", Order = 2)
public int Id { get; set; }
[Display(Name = "Description", Order = 1)
public string Label {get; set; }
}
What I have tested sorting by MetadataToken works.
Some of users here claims this is somehow not good approach / not reliable, but I haven't yet seen any evidence of that one - perhaps you can post some code snipet here when given approach does not work ?
About backwards compatibility - while you're now working on your .net 4 / .net 4.5 - Microsoft is making .net 5 or higher, so you pretty much can assume that this sorting method won't be broken in future.
Of course maybe by 2017 when you will be upgrading to .net9 you will hit compatibility break, but by that time Microsoft guys will probably figure out the "official sort mechanism". It does not makes sense to go back or break things.
Playing with extra attributes for property ordering also takes time and implementation - why to bother if MetadataToken sorting works ?
You may use DisplayAttribute in System.Component.DataAnnotations, instead of custom attribute. Your requirement has to do something with display anyway.
If you can enforce your type has a known memory layout, you can rely on StructLayout(LayoutKind.Sequential) then sort by the field offsets in memory.
This way you don't need any attribute on each field in the type.
Some serious drawbacks though:
All field types must have a memory representation (practically no other reference types other than fixed-length arrays or strings). This includes parent types, even if you just want to sort the child type's fields.
You can use this for classes including inheritance, but all parent classes need to also have sequential layout set.
Obviously, this doesn't sort properties but fields might be fine for POCOs.
[StructLayout(LayoutKind.Sequential)]
struct TestStruct
{
public int x;
public decimal y;
}
[StructLayout(LayoutKind.Sequential)]
class TestParent
{
public int Base;
public TestStruct TestStruct;
}
[StructLayout(LayoutKind.Sequential)]
class TestRecord : TestParent
{
public bool A;
public string B;
public DateTime C;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 42)] // size doesn't matter
public byte[] D;
}
class Program
{
static void Main(string[] args)
{
var fields = typeof(TestRecord).GetFields()
.OrderBy(field => Marshal.OffsetOf(field.DeclaringType, field.Name));
foreach (var field in fields) {
Console.WriteLine($"{field.Name}: {field.FieldType}");
}
}
}
Outputs:
Base: System.Int32
TestStruct: TestStruct
A: System.Boolean
B: System.String
C: System.DateTime
D: System.Byte[]
If you try to add any forbidden field types, you'll get System.ArgumentException: Type 'TestRecord' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed.
I did it this way:
internal static IEnumerable<Tuple<int,Type>> TypeHierarchy(this Type type)
{
var ct = type;
var cl = 0;
while (ct != null)
{
yield return new Tuple<int, Type>(cl,ct);
ct = ct.BaseType;
cl++;
}
}
internal class PropertyInfoComparer : EqualityComparer<PropertyInfo>
{
public override bool Equals(PropertyInfo x, PropertyInfo y)
{
var equals= x.Name.Equals(y.Name);
return equals;
}
public override int GetHashCode(PropertyInfo obj)
{
return obj.Name.GetHashCode();
}
}
internal static IEnumerable<PropertyInfo> GetRLPMembers(this Type type)
{
return type
.TypeHierarchy()
.SelectMany(t =>
t.Item2
.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(prop => Attribute.IsDefined(prop, typeof(RLPAttribute)))
.Select(
pi=>new Tuple<int,PropertyInfo>(t.Item1,pi)
)
)
.OrderByDescending(t => t.Item1)
.ThenBy(t => t.Item2.GetCustomAttribute<RLPAttribute>().Order)
.Select(p=>p.Item2)
.Distinct(new PropertyInfoComparer());
}
with the property declared as follows:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class RLPAttribute : Attribute
{
private readonly int order_;
public RLPAttribute([CallerLineNumber]int order = 0)
{
order_ = order;
}
public int Order { get { return order_; } }
}
Building on the above accepted solution, to get the exact Index you could use something like this
Given
public class MyClass
{
[Order] public string String1 { get; set; }
[Order] public string String2 { get; set; }
[Order] public string String3 { get; set; }
[Order] public string String4 { get; set; }
}
Extensions
public static class Extensions
{
public static int GetOrder<T,TProp>(this T Class, Expression<Func<T,TProp>> propertySelector)
{
var body = (MemberExpression)propertySelector.Body;
var propertyInfo = (PropertyInfo)body.Member;
return propertyInfo.Order<T>();
}
public static int Order<T>(this PropertyInfo propertyInfo)
{
return typeof(T).GetProperties()
.Where(property => Attribute.IsDefined(property, typeof(OrderAttribute)))
.OrderBy(property => property.GetCustomAttributes<OrderAttribute>().Single().Order)
.ToList()
.IndexOf(propertyInfo);
}
}
Usage
var myClass = new MyClass();
var index = myClass.GetOrder(c => c.String2);
Note, there is no error checking or fault tolerance, you can add pepper and salt to taste
If you are happy with the extra dependency, Marc Gravell's Protobuf-Net can be used to do this without having to worry about the best way to implement reflection and caching etc. Just decorate your fields using [ProtoMember] and then access the fields in numerical order using:
MetaType metaData = ProtoBuf.Meta.RuntimeTypeModel.Default[typeof(YourTypeName)];
metaData.GetFields();
Even it's a very old thread, here is my working solution based on #Chris McAtackney
var props = rootType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.OrderBy(p =>
(
p.GetCustomAttributes(typeof(AttrOrder), false).Length != 0 ? // if we do have this attribute
((p.GetCustomAttributes(typeof(AttrOrder), false)[0]) as AttrOrder).Order
: int.MaxValue // or just a big value
)
);
And the Attribute is like this
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class AttrOrder : Attribute
{
public int Order { get; }
public AttrOrder(int order)
{
Order = order;
}
}
Use like this
[AttrOrder(1)]
public string Name { get; set; }

Setting up a simple iequatable class c#

Cant find a simple answer. My problem is I am trying to compare the VALUE of an object in a list to the VALUE of an object...
my class:
public class MatchList
{
public int SomeInt { get; set; }
public decimal SomeDecimal { get; set; }
}
I create theMatchList. It seems that I can only compare the object and not the values for object with 'theMatchList.Contains...'
MatchList ML = new MatchList();
ML.SomeInt = 12;
ML.SomeDecimal = 2.3;
if (theMatchlist.Contains(ML))
{
DoSomething;
}
How do get to fire 'DoSomething'? Assuming that there is an entry in 'theMatchList' where the values equal 12 and 2.3 respectively. I know it has something to do with iequatable, but I dont quite understand how that works. Thanks in advance!
Your naming is a bit unclear, I assume that you actually have a List<MatchList> that you want to find a particular MatchList in (I suggest renaming MatchList to at least MatchItem in that case and preferable something more descriptive).
Then from the documentation of List<T>.Contains:
This method determines equality by using the default equality comparer, as defined by the object's implementation of the IEquatable<T>.Equals method for T (the type of values in the list).
So you will have to implement IEquatable<T> for your class. In addition, the advice is that
[i]f you implement Equals, you should also override the base class implementations of Object.Equals(Object) and GetHashCode so that their behavior is consistent with that of the IEquatable.Equals method.
If you implement GetHashCode, its result should not change over the lifetime of your object. In most cases, making the class immutable is sufficient. If you need to be able to update the fields, you need to implement GetHashCode differently.
So all in all, if you want to use Contains your class will end up looking something like below:
public class MatchList : IEquatable<MatchList>
{
// Note: Fields are readonly to satisfy GetHashCode contract
private readonly int someInt;
private readonly decimal someDecimal;
// Public constructor creates immutable object
public MatchList(int myInt, decimal myDecimal)
{
this.someInt = myInt;
this.myDecimal = myDecimal;
}
// Properties are now read-only too.
public int SomeInt { get { return this.someInt; } }
public decimal SomeDecimal { get { return this.someDecimal; } }
// Implementation of IEquatable<MatchList>
public bool Equals( MatchList other )
{
return (other != null)
&& (this.SomeInt == other.SomeInt)
&& (this.SomeDecimal == other.SomeDecimal);
}
// Override of Object.Equals
// Calls the IEquatable.Equals version if possible.
public override bool Equals( object obj )
{
return (obj is MatchList) && this.Equals(obj as MatchList);
}
public override int GetHashCode()
{
return (this.someInt * 17) ^ this.someDecimal.GetHashCode();
}
}
As I commented, your question is pretty unclear so I'll do my best to explain the concept.
It's pretty likely what you were trying to code is the items in the list not the list itself:
public class MatchItem : IEquatable<MatchItem>
{
public int SomeInt { get; set; }
public decimal SomeDecimal {get; set; }
public bool Equals(MatchItem item)
{
if(item == null)
return false;
return this.SomeInt == item.SomeInt && this.SomeDecimal == item.SomeDecimal;
}
// You should also override object.ToString, object.Equals & object.GetHashCode.
// Omitted for brevity here!
}
You'll note that has an implementation of IEquatable<MatchItem> which allows it to be compared to other instances of MatchItem.
Thereafter, this code will work:
var items = new List<MatchItem>()
{
new MatchItem{SomeInt = 1, SomeDecimal = 0.3M},
new MatchItem{SomeInt = 12, SomeDecimal = 2.3M}
};
var searchItem = new MatchItem{SomeInt = 1, SomeDecimal = 0.3M};
Console.WriteLine(items.Contains(searchItem)); // true
Working example: http://rextester.com/ZWNC6890

Dapper and Enums as Strings

I am trying to use Dapper and Dapper-Extensions and to serialize my enums on the database as string.
Right now they are serialized as integers (inside a VARCHAR field) instead.
Is there any way to do this?
Any custom type mapping that I can add?
I might need to move back to EF if i can't pull this through..
There's a way, which I think is more robust and clean.
The solution I provide will work for any enumeration, but it involves some extra coding. It also involves adding a custom type handler in Dapper. However, if this answer gets some votes, I will change the Dapper source code to include this solution automatically in the type handling and ask for a pull request.
I actually implemented this solution and use it in production.
Here goes.
First the struct (not a class, because the struct simply holds a string reference) that will be used as enumeration:
public struct Country
{
string value;
public static Country BE => "BE";
public static Country NL => "NL";
public static Country DE => "DE";
public static Country GB => "GB";
private Country(string value)
{
this.value = value;
}
public static implicit operator Country(string value)
{
return new Country(value);
}
public static implicit operator string(Country country)
{
return country.value;
}
}
Now we need a type handler for this struct
public class CountryHandler : SqlMapper.ITypeHandler
{
public object Parse(Type destinationType, object value)
{
if (destinationType == typeof(Country))
return (Country)((string)value);
else return null;
}
public void SetValue(IDbDataParameter parameter, object value)
{
parameter.DbType = DbType.String;
parameter.Value = (string)((dynamic)value);
}
}
Somewhere in the startup of the application we have to register the type handler with Dapper
Dapper.SqlMapper.AddTypeHandler(typeof(Country), new CountryHandler());
Now you can simply use Country as an "enum". For instance:
public class Address
{
public string Street { get; set; }
public Country Country { get; set; }
}
var addr = new Address { Street = "Sesamestreet", Country = Country.GB };
The downside of course is that the enumeration is not backed in memory by an integer but by a string.
Thanks to Marc Gravell reply:
The only way is to do the inserts manually.
Also using the following post: How do I perform an insert and return inserted identity with Dapper?
Below my solution.
Note that selects work automatically: you can use Dapper (Extensions) directly GetList<T>, there is no mapping to the enum back required.
public enum ComponentType
{
First,
Second,
Third
}
public class Info
{
public int Id { get; set; }
public ComponentType InfoComponentType { get; set; }
public static void SaveList(List<Info> infoList)
{
string ConnectionString = GetConnectionString();
using (SqlConnection conn = new SqlConnection(ConnectionString))
{
conn.Open();
foreach (Info info in infoList)
{
string sql = #"INSERT INTO [Info] ([InfoComponentType])
VALUES (#InfoComponentType);
SELECT CAST(SCOPE_IDENTITY() AS INT)";
int id = conn.Query<int>(sql, new
{
InfoComponentType = info.InfoComponentType.ToString()
}).Single();
info.Id = id;
}
conn.Close();
}
}
}
My technique is simliar to neeohw's but lets me use real enums. And it's generic so I don't have to write it many times.
There's an immutable struct that wraps the enum value. It has a single property and implicit conversions, plus a generic custom type handler.
public readonly struct DapperableEnum<TEnum> where TEnum : Enum
{
[JsonConverter(typeof(StringEnumConverter))]
public TEnum Value { get; }
static DapperableEnum()
{
Dapper.SqlMapper.AddTypeHandler(typeof(DapperableEnum<TEnum>), new DapperableEnumHandler<TEnum>());
}
public DapperableEnum(TEnum value)
{
Value = value;
}
public DapperableEnum(string description)
{
Value = EnumExtensions.GetValueByDescription<TEnum>(description);
}
public static implicit operator DapperableEnum<TEnum>(TEnum v) => new DapperableEnum<TEnum>(v);
public static implicit operator TEnum(DapperableEnum<TEnum> v) => v.Value;
public static implicit operator DapperableEnum<TEnum>(string s) => new DapperableEnum<TEnum>(s);
}
public class DapperableEnumHandler<TEnum> : SqlMapper.ITypeHandler
where TEnum : Enum
{
public object Parse(Type destinationType, object value)
{
if (destinationType == typeof(DapperableEnum<TEnum>))
{
return new DapperableEnum<TEnum>((string)value);
}
throw new InvalidCastException($"Can't parse string value {value} into enum type {typeof(TEnum).Name}");
}
public void SetValue(IDbDataParameter parameter, object value)
{
parameter.DbType = DbType.String;
parameter.Value =((DapperableEnum<TEnum>)value).Value.GetDescription();
}
}
I use the static constructor to automatically register the type handler at startup.
I use GetDescription / GetValueByDescription (same idea as this answer) to support strings that wouldn't be valid C# enum values. If you don't need this feature, ToString and Enum.Parse will work fine.
The JsonConverter attribute makes Json.Net use string values too. Of course remove it if you don't use Json.Net
Here's an example:
enum Holiday
{
Thanksgiving,
Christmas,
[Description("Martin Luther King, Jr.'s Birthday")]
MlkDay,
Other,
}
class HolidayScheduleItem : IStandardDaoEntity<HolidayScheduleItem>
{
public DapperableEnum<Holiday> Holiday {get; set;}
public DateTime When {get; set;}
}
And calling code can use the normal enum values.
var item = new HolidayScheduleItem()
{
Holiday = Holiday.MlkDay,
When = new DateTime(2021, 1, 18)
};
It works with plain Dapper or Dapper.Contrib:
await conn.ExecuteAsync("INSERT HolidayScheduleItem ([Holiday], [When])
VALUES(#Holiday, #When)", item);
await conn.InsertAsync(item);
I couldn't get the ITypeHandler suggestions to work with enums. However, I was profiling the SQL generated by Dapper and noticed it was declaring the enum parameters as int. So I tried adding a type map for the enum type.
Adding this Dapper config on application startup did the trick for me.
Dapper.SqlMapper.AddTypeMap(typeof(MyEnum), DbType.String);
Then I use connection.Execute(updateSql, model) as normal. Didn't need to use .ToString() or any other explicit conversions. The underlying columns are varchar(20).
Instead of passing in your data object you can pass in a dictionary built off your object w/ the enum converted into a string in the dictionary (so Dapper never sees the Enum)
iow instead of say
connection.Query<MyDataObjectType>(sql, myDataObject);
you can do
connection.Query<MyDataObjectType>(sql, myDataObject.AsDapperParams());
and then have a method like
public static Dictionary<string, object> AsDapperParams(this object o)
{
var properties = o.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(c => c.CanRead).ToArray();
return properties
.Select(c => new {Key = c.Name, Value = c.GetValue(o), Type = c.PropertyType})
.ToDictionary(
c => c.Key,
c => (c.Type.IsEnum || Nullable.GetUnderlyingType(c.Type)
?.IsEnum == true) ? c.Value.ToString() : c.Value);
}
I find this approach works well with DapperExtensions. Setup a Enum field as per normal in your class but then have a 2nd string field for the Enum which represents the value you will be persisting. You can then set the mappings to ignore the Enum field but persist the string value instead. e.g.
// enum field
public Frequency Frequency { get; set;}
// string field of the same enum that you are going to persist
public string DbFrequency
{
get { return this.Frequency.ToString(); }
set { this.Frequency = Enum.Parse<Frequency>(value); }
}
// in your mappings do this
Map(f => f.Frequency).Ignore();
Map(f => f.DbFrequency).Column("Frequency");
It would be to have 2nd string enum as a private member of the class but you have to make it public for this to work AFAIK

Removing duplicates from a list<int,int> [duplicate]

I have to distinct list of object but NOT only by ID because sometimes two different objects have same ID.
I have class:
public class MessageDTO
{
public MessageDTO(MessageDTO a)
{
this.MsgID = a.MsgID;
this.Subject = a.Subject;
this.MessageText = a.MessageText;
this.ViewedDate = a.ViewedDate;
this.CreatedDate = a.CreatedDate;
}
public int? MsgID { get; set; }
public string Subject { get; set; }
public string MessageText { get; set; }
public System.DateTime? ViewedDate { get; set; }
public System.DateTime? CreatedDate { get; set; }
}
How I can distinct list of:
List<MessageDTO> example;
Thanks
Use LINQ.
public class MessageDTOEqualityComparer : EqualityComparer<MessageDTO>
{
public bool Equals(MessageDTO a, MessageDTO b)
{
// your logic, which checks each messages properties for whatever
// grounds you need to deem them "equal." In your case, it sounds like
// this will just be a matter of iterating through each property with an
// if-not-equal-return-false block, then returning true at the end
}
public int GetHashCode(MessageDTO message)
{
// your logic, I'd probably just return the message ID if you can,
// assuming that doesn't overlap too much and that it does
// have to be equal on the two
}
}
Then
return nonDistinct.Distinct(new MessageDTOEqualityComparer());
You can also avoid the need for an extra class by overriding object.Equals(object) and object.GetHashCode() and calling the empty overload of nonDistinct.Distinct(). Make sure you recognize the implications of this decision, though: for instance, those will then become the equality-testing functions in all non-explicit scopes of their use. This might be perfect and exactly what you need, or it could lead to some unexpected consequences. Just make sure you know what you're getting into.
I you want to use other properties, you should implement IEqualityComparer interface. More on: msdn
class MsgComparer : IEqualityComparer<MessageDTO>
{
public bool Equals(MessageDTO x, MessageDTO Oy)
{
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(MessageDTO m)
{
//it must br overwritten also
}
}
Then:
example.Distinct(new MsgComparer());
You could also overwrite Equals in MessageDTO class:
class MessageDTO
{
// rest of members
public override bool Equals(object obj)
{
// your stuff. See: http://msdn.microsoft.com/en-us/library/ms173147%28v=vs.80%29.aspx
}
public override int GetHashCode()
{
}
}
Then it's enough:
example.Distinct();
You could use the extension method DistinctBy from the MoreLinq library:
string[] source = { "first", "second", "third", "fourth", "fifth" };
var distinct = source.DistinctBy(word => word.Length);
See here:
I recommend you using solution of #Matthew Haugen
In case you don't want to create a new class for that, there is a way to use LINQ by grouping you list by distinct field(s) then select the first item on this group. For example:
example.(e => new { e.MsgID, e.Subject }).Select(grp => grp.FirstOrDefault());

Relate two lists with LINQ extensions

I have two lists of different objects, one from a third party API and one from my database - and I'm trying to link the two as a relationship. Ideally with a similar effect of how DBML's create relationships for tables with foreign keys (Customer.Orders).
From third party:
class ApiObject {
public string ID { get; set; }
public string Title { get; set; }
public DateTime CreatedDate { get; set; }
... 30 other properties ...
}
From my database:
class DbmlObject {
public int ID { get; set; }
public string ApiID { get; set; }
public string OtherString { get; set; }
}
They are related through ApiObject.ID == DbmlObject.ApiID
I do not want to merge these, nor join them into some anonymous object (and explicitly list 30+ properties) - but rather to make the DbmlObject a linked property of ApiObject. i.e.: addressable as:
apiObject.DbmlObjects.First().OtherString or ideally apiObject.DbmlObject.OtherString since it is a 1 to 1 relationship.
In controller:
List<ApiObject> apiObjects = _thirdParty.GetObjects();
DbmlDataContext model = new DbmlDataContext();
List<DbmlObject> dbmlObjects = model.GetAllDbmlObjects();
// relate them here
foreach (var apiObject in apiObjects)
Console.Write(apiObject.DbmlObject.OtherString)
// NOTE: ideally this foreach loop should not make a DBML query on each iteration, just the single GetAllDbmlObjects query above.
It sounds like a join:
var combined = from api in apiObjects
join dbml in dbmlObjects on api.ID equals dbml.ApiID
select new { api, dbml }
In order to get DbmlObject "in" the ApiObject, you will need to either inherit ApiObject and construct a new one of that class, which includes the Dbml property, or create a entirely new class to return. If you need static typing this is the best you can do - of course you could (mis)use dynamic to get what you want.
In this case, you are mentioning (in comments) that the ApiObject class is from a third party library that you can't change - in this case I would probably choose to create a new type which takes an instance of both objects in the constructor and exposes the properties you need - a decorator. Yes, it looks like a lot of code, but it is not complex, good tools will autogenerate it for you - and you get the class that you need for your code to be succinct.
In case you want to go further with returning an IEnumerable<dynamic>, you could build a "combining dynamic" object based on DynamicObject that then responds to all the properties of ApiObject and DbmlObject - or just adds DbmlObject as a property. I am not saying this is the right way to go, it depends on what you need it for - remember you are losing type safety. Here is a simple example:
void Main()
{
dynamic dyn = new CombiningDynamic(new Foo { X = 3 }, new Bar { Y = 42 });
Console.WriteLine(dyn.X);
Console.WriteLine(dyn.Y);
}
public class Foo
{
public int X {get;set;}
}
public class Bar
{
public int Y { get;set;}
}
public class CombiningDynamic : DynamicObject
{
private object [] innerObjects;
public CombiningDynamic(params object [] innerObjects)
{
this.innerObjects = innerObjects;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
foreach(var instance in innerObjects)
{
Type t = instance.GetType();
PropertyInfo prop = t.GetProperty(binder.Name);
if (prop != null && prop.CanRead)
{
result = prop.GetValue(instance, null);
return true;
}
}
result = null;
return false;
}
}
Remember, this is example code. If you really go this way, you would want to perhaps override some more of the methods (TrySetMember, ...), and you most definetely would want to cache the reflection results so you don't need to walk the types each time - reflection is (comparatively) slow.

Categories

Resources