RavenDb, Query on properties of abstacts class implementations - c#

I have 3 objects Vehicle, Car and MotorCycle. Vehicle is the abstract base class for Car and MotorCycle.
public abstract class Vehicle
{
public String Color {get;set};
public Int Price {get;set};
public DateTime ReleaseDate {get;set};
}
public class Car : Vehicle
{
public Int EnginePower {get;set};
public Int TrunkSpace {get;set};
public bool GearBoxType {get;set};
public Int Seats {get;set};
}
public class MotorCycle : Vehicle
{
public Int EnginePower {get;set};
public String Type{get;set};
}
I want to stock all the Vehicles (Cars, motorcycles...) in the same collection of documents in RavenDb. So I am using this convention and it works :
documentStore.Conventions.FindTypeTagName =
type => type.IsSubclassOf(typeof(Vehicle)) ?
DocumentConvention.DefaultTypeTagName(typeof(Vehicle)) :
DocumentConvention.DefaultTypeTagName(type);
Now I want to do indexs for my requests for all vehicles. But i want to request on Vehicle properties and on implementations (Car, MotorCycle) properties.
Examples of requests i would :
All vehicles with Color="red", EnginePower>100
All vehicles with Color="red", EnginePower>100, Seats=5
All vehicles with Type="Scooter"
etc....
And the result in only one collection of Vehicule type, order by RealeaseDate.
How to do a query like that in C# and linq ?

RavenDB will save the entities as their deriving types and build any automatic index based on those types.
What you need to do is create a new index that indexes the properties in the base class across all the different types and then select from that index.
public class VehicleIndex : AbstractMultiMapIndexCreationTask
{
public VehicleIndex()
{
this.AddMap<Car>(vehicle => from v in vehicle select new { v.Price , v.Color , v.ReleaseDate });
this.AddMap<Motorcycle>(vehicle => from v in vehicle select new { v.Price , v.Color , v.ReleaseDate });
}
}
I have written a more in depth blog post about this using interfaces but exactly the same technique works for abstract base classes. See blog post here
It is actually a very powerful technique and allows you to fully leverage the schema-less nature of Raven.

I came across this thread and would like to correct the comments / answer posted here so far.
It is possible to create an index of all inheritances.
Generally speaking, it is best to save all your inheritances in seperate collections. So, in other words, to not save Car as a Vehicle in Raven, but as a Car.
You can then use a MultiMapIndex to query on similarities between the different inheritances, but also retain the option to create a seperate index for a specific inheritance, should the need arise.
To get the result you need, simply save the results as a new type wherein you include all properties you want to index (or even create new ones based on input of the child classes)
public class VehicleIndex : AbstractMultiMapIndexCreationTask
{
public class Mapping
{
public string Price { get; set; }
public string Color { get; set; }
public string ReleaseDate { get; set; }
public int? EnginePower { get; set;}
}
public VehicleIndex()
{
this.AddMap<Car>(vehicle => from v in vehicle select new Mapping()
{
Price = v.Price,
Color = v.Color,
ReleaseDate = v.ReleaseDate,
EnginePower = v.EnginePower
});
this.AddMap<Motorcycle>(vehicle => from v in vehicle select new Mapping()
{
Price = v.Price,
Color = v.Color,
ReleaseDate = v.ReleaseDate,
EnginePower = v.EnginePower
});
}
}
I saved EnginePower as an int? so you can add childs to this class that do not have EnginePower and store them as null. This allows you to filter on motorcycles and cars by doing where(a => a.EnginePower.HasValue && (a.EnginePower > 50 || a.EnginePower < 100))
EDIT: I have since added a custom overload that simply scans the whole solution for classes deriving from T, and adding them to the index as a new map. This means that if a new child gets added, you don't have to manually add them to index definition but simply recompile your indexes and you're good to go.
public void AddMapForPolymorphic<TBase>(Expression<Func<IEnumerable<TBase>, IEnumerable>> expr)
{
AddMap(expr);
var children = Modules.Modules.Instance.GetModules()
.SelectMany(a => AssemblyHelper.GetClassesWithBaseType<TBase>(a.GetType().Assembly)
);
var addMapGeneric = GetType()
.GetMethod("AddMap", BindingFlags.Instance | BindingFlags.NonPublic);
foreach (var child in children)
{
if (!child.HasAttribute<IgnorePolymorpAttribute>())
{
var genericEnumerable = typeof(IEnumerable<>)
.MakeGenericType(child);
var delegateType = typeof(Func<,>)
.MakeGenericType(genericEnumerable, typeof(IEnumerable));
var lambdaExpression = Expression.Lambda(delegateType, expr.Body, Expression
.Parameter(genericEnumerable, expr.Parameters[0].Name));
addMapGeneric
.MakeGenericMethod(child).Invoke(this, new[] { lambdaExpression });
}
}
}

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; }

C# ASP.Net MVC dynamic object properties

I am working on a ASP.NET MVC project, with C#, and EF code first.
I am required to add dynamic properties to entities. For example -
I have a car as a base object. I can add custom properties for it like engine power, length, color etc etc.
Properties can be boolean, int, string or select options (ie, i have to create checkbox, input or select html elements when a user inputs values for those properties).
Properties must have custom validation rules (i.e., required, number only, range only etc).
Can you guys give me any clues or direction how to accomplish this?
Thank you
If you truly have dynamic properties you won't be able to do this (directly) with EF6 since EF6 assumes a relation database. and a relational database needs to know which columns to expect.
Now you got 2 options.
Option1:
use a non-relational database with EF 7. you can look here https://msdn.microsoft.com/en-us/magazine/dn890367.aspx for some more details about EF7 but basically in a non relation database you can store any json blob - so also your dynamic properties
Option 2: Use a key value pair within your object. and store those properties
class KeyValuePair {
int Id {get; set;}
string name {get; set;}
string stringValue {get; set;}
}
class BaseObject {
int Id {get; set;}
list<KeyValuePair> dynamicProperties {get; set;}
}
Now your car can just inherit from this baseobject. You still need to do the work to create your KeyValuePair objects. (And if you want to store strings, ints etc you can make Different KeyValuePair types, one for each storage type)
Be carefull with performance though if you use dynamic properties like this.
Update:
If you want to validate a dynamic object like this you want to implement IValidatableObject
so you get
class Car: BaseObject, IValidatableObject {
public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
/* code to validate your properties here, for example you need at least 1 engine, 4 wheels etc */
yield return ValidationResult.Success;
}
}
You can create and use tables in DB dynamically, although it's not so simply.
First, you'll need to store metadata about your tables — what are their names, what are properties they have, what are the types of those properties, and so on.
Second, you'll need to generate entities to access these tables, and also, EntityTypeConfiguration classes, like here:
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
}
public class FooTypeConfiguration : EntityTypeConfiguration<Foo>
{
public FooTypeConfiguration()
{
ToTable("Foos");
HasKey(t => t.Id);
Property(t => t.Name).HasMaxLength(200)
.IsRequired();
}
}
You can generate DLL dynamically without intermediate C# code with help of System.Reflection.Emit. Or you can generate C# code and use System.CodeDom.Compiler to compile it (this way is simpler). You can also try Roslyn compiler (but I don't have enough experience to advise it).
Third, you'll need to load compiled DLL and create DbContext using modelBuilder.Configurations.AddFromAssembly(...).
You can find required type in assembly and use it to access data:
string typeName = ...;
var type = dynamicLoadedAssembly.GetType(typeName);
var set = dbContext.Set(type); // non-generic DB Set
You can use System.Reflection or dynamic typing to work with these objects.
Finally, if you'll generate C# code, you can generate properties and implementation of some interface to access these properties by names:
public interface IAccessorByName : IReadOnlyDictionary<string, object>
{
object this[string name] { get; set; }
}
public Foo : IAccessorByName
{
private static readonly IDictionary<string, Func<Foo, object>> getters = new Dictionary<string, Func<Foo, object>>
{
{ "Id", (foo) => foo.Id },
{ "Name", (foo) => foo.Name },
};
private static readonly IDictionary<string, Action<Foo, object>> setters = new Dictionary<string, Action<Foo, object>>
{
{ "Id", (foo, value) => { foo.Id = (int)value; } },
{ "Name", (foo, value) => { foo.Name = (string)value; } },
};
public int Id { get; set; }
public string Name { get; set; }
public object this[string name]
{
get { return getters[name](this); }
set { setters[name](this, value); }
}
}
With similar interface you can create, read, update, and delete objects dynamically:
string typeName = "Foo";
var fooType = dynamicLoadedAssembly.GetType(typeName);
var foo = (IAccessorByName)Activator.CreateInstance(fooType);
foo["Id"] = 1;
foo["Name"] = "Jon Skeet";
var fooSet = dbContext.Set(fooType);
fooSet.Add(foo);
dbContext.SaveChanges();

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.

Get properties in order of declaration using reflection

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; }

Implicit conversion between types in C#

I have the following business objects:
public abstract class Product
{
public int Id { get; set; }
public bool OnStock { get; set; }
}
public class ProductForImport : Product
{
public int ImportId { get; set; }
}
public class ProductForExport : Product
{
public int ExportId { get; set; }
public bool IsExportable { get; set; }
public bool IsUsable { get; set; }
public string OtherParam {get; set;}
public static implicit operator ProductForExport(ProductForImport pfi)
{
ProductForExport p = new ProductForExport();
p.Id = pfi.Id;
p.IsExportable = true;
p.ExportId = 0;
return p;
}
}
so I can convert between the two types:
static void Main(string[] args)
{
ProductForExport pfe = new ProductForExport();
pfe.Id = 1;
pfe.OnStock = true;
ProductForImport pfi = new ProductForImport();
pfi.ImportId = 200;
ProductForExport pfe2 = (ProductForExport)pfi;
}
this works OK.
I have 100.000 ProductsForImport items.
If I understand correctly, if I convert them to ProductsForExport items, I'll have 100.000 +100.000 items in memory - that's reasonable.
My problem is: I have to send these "ProductForExport" objects through JSON services, each service just need some subset of the properties of each type:
servicecall1 should return ProductForExport1{ExportId,IsExportable}
servicecall2 should return ProductForExport2{ExportId,IsUsable}
Question: should I write an implicit conversion similar to the above example for these new types - ProductForExport1 and ProductForExport2 (so basically create 100.000+100.000 new objects)
or
somehow can I just "hide" the unwanted properties with some magic from the original type without the need to create new instances?
thanks,
b.
If you ned such kind of decoupling and separation of entities - you can create DTO object along with each business object and use DTO to communicate with Service.
But if you have a lot of business entities consider an other approach to avoid maintenance hell.
public sealed class ExportProductDto
{
public(ProductForExport exportProduct)
{
// initialize fields
this.ExportId = exportProduct.ExportId;
}
public int ExportId { get; private set; }
}
BTW,
An overkill solution with operator overload, use Adapter pattern to convert between product types
To decouple adapting from entities itself implement following interface your self:
public interface IProductAdapter<TImport, TExport>
{
TImport ToImportProduct(TExport exportProduct);
TExport ToExportProduct(TImport importProduct);
}
Or an other adapter approach:
// Implement this interface for ProductForImport class
// public class ProductForImport : IExportProductAdapter, Product
public interface IExportProductAdapter
{
ProductForExport ToExportProduct();
}
// Implement this interface for ProductForExport class
// public class ProductForExport : IImportProductAdapter, Product
public interface IImportProductAdapter
{
ProductForImport ToImportProduct();
}
EDIT: Answer to comments
// An example of IExportProductAdapter adapter implementation
public sealed class ProductForImport : Product, IExportProductAdapter
{
public int ImportId { get; set; }
public ProductForExport ToExportProduct()
{
ProductForExport p = new ProductForExport();
p.Id = this.Id;
p.IsExportable = true;
p.ExportId = 0;
return p;
}
}
And then instead of:
ProductForExport pfe2 = (ProductForExport)pfi;
You can do:
ProductForExport pfe2 = pfi.ToExportProduct();
I would create light objects specifically for returning through the service with only the required fields. Then use Automapper or something like that to map them.
I don't recommend using operator overloading if you can avoid it. I have seen many issues where a developer didn't realize when the operator overload was being called and something unexpected happened.
If you are using WCF, you can apply the IgnoreDataMemberAttribute to properties you wish not to serialize.
Have a look at the ScriptIgnoreAttribute to exclude properties from json serialization.
It took me a few reads but I don't think your problem is about implicit conversion as much as how to send data via json right?
If you have your object collections of Import or Export object you can use the JavaScriptSerilizer and some anonymous types to slice and dice what data you send.
You can use Linq to select specific properties of your object in a collection, and define an anonymous type "on-the-fly" to serialize out as a json string like this:
List<ProductForExport> exportList; //the list to export
JavaScriptSerializer jss = new JavaScriptSerializer();
string output = string.Empty;
output = jss.Serialize(new
{
isExportable = True, //static named properties
iTotalProducts = exportList.Count, //dynamic values
productDataArray = exportList //all data in an array object
});
//Or build the result using just a few properties of the collection:
foreach (ExportProduct exProd in exportList)
{
output += jss.Serialize(new
{
exProd.IsExportable,
exProd.ExportID
});
}

Categories

Resources