For performance reasons I am mapping a set of entities to a view creating a flat table (in a way transforming a TPT inheritance in a TPH). This is to be used only a specific method.
[view]
id
property1
property2
propertyN
complex_type_collection_property1
complex_type_collection_property2
complex_type_collection_propertyN
Is there a way to map the complex_type_collection_properties to a complex property on the materialized object?
e.g.
[Object]
id = [view].Id
property1 = [view].property1
property2 = [view].property2
propertyN = [view].propertyN
Collection = [{property1 = [view].complex_type_collection_property1, ...}, ...]
Thank you!
Ok I created example, I assume that you already have entity(fromDB variable at my code, lets imagine that it taken from DB) or entities retrieved from DB. Than you can cast each entity as shown below. Solution is comprehensive and little sophisticated. You should also specify type parameterNormalized<City> that corresponds to your complex type (if you will want to change complex type to another one there will not needed to do any changes at casting method). Also names of your complex properties(City1, City2, etc) must contain name of collection property(City), it is - some conventions. And I created BaseClass with shared properties, our two classes derrived from it (so you able to not copy fields from one class to another, instead you should place them into BaseClass). Also you can have the only one collection, i.e. property of type List<T>.
public class City
{
public string Name { get; set; }
public int Population { get; set; }
}
public class BaseClass
{
public int id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
public class POCO : BaseClass
{
public City City1 { get; set; }
public City City2 { get; set; }
public City City3 { get; set; }
}
public class Normalized<T> : BaseClass
{
public List<T> City { get; set; }
public static explicit operator Normalized<T>(POCO self)
{
if (self == null)
return null;
var normal = new Normalized<T>();
foreach (var prop in typeof(BaseClass).GetProperties())
prop.SetValue(normal, prop.GetValue(self));
var complexProp = typeof(Normalized<T>).GetProperties().Where(x => x.PropertyType.GetInterfaces().Any(y => y.Name == "ICollection")).First();
complexProp.SetValue(normal, new List<T>((typeof(POCO).GetProperties().Where(x => x.Name.Contains(complexProp.Name)).Select(x => (T)x.GetValue(self)).ToList())));
return normal;
}
}
public static void Main(string[] args)
{
var fromDB = new POCO
{
Age = 20,
id = 1,
Name = "Mike",
City1 = new City { Name = "Moscow", Population = 10 },
City2 = new City { Name = "London", Population = 20 },
City3 = null
};
var normal = (Normalized<City>)fromDB;
Console.WriteLine(normal.City.Select(x => x == null ? "EMPTY" : x.Name).Aggregate((a, b) => { return a + ", " + b; }));
}
Related
Is there a way to convert a nested object to an object. For example lets say I have an object of class ABC as shown below.
class ABC
{
int id {get;set;}
XYZ myobj {get;set}
}
class XYZ
{
string name {get;set;}
string addr {get;set;}
}
Is there any way to convert this ABC class object to UVW class object like below.
class UVW
{
int id {get;set;}
string name {get;set;}
string addr {get;set;}
}
If you are looking for a provision in C# to convert an object of ABC to an object of UVW, then am afraid there is no such thing.
If you want, you could just write your own conversion logic though (after making the properties public):
private UVW ConvertToUVW(ABC abc)
{
if (abc == null)
{
return null;
}
var uvw = new UVW();
uvw.id = abc.id;
uvw.name = abc.myobj?.name;
uvw.addr= abc.myobj?.addr;
return uvw;
}
You can use Automapper's automatic flattening for this. It works by naming convention, to map a Foo.Bar in the source to a FooBar destination, or by configuration where the first configured match wins in case of conflicts.
Given these source types:
class SourceRoot
{
public int Id { get; set; }
public Foo Foo { get; set; }
public Bar Bar { get; set; }
}
class Foo
{
public string Name { get; set; }
public string Addr { get; set; }
}
class Bar
{
public string Name { get; set; }
public string Addr { get; set; }
}
Which is to be mapped to this target:
class TargetFlattened
{
public int Id { get; set; }
public string Name { get; set; }
public string Addr { get; set; }
public string BarAddr { get; set; }
}
You can automap from source to flattened target like this:
var source = new SourceRoot
{
Id = 42,
Foo = new Foo
{
Addr = "FooAddr",
Name = "FooName"
},
Bar = new Bar
{
Addr = "BarAddr",
Name = "BarName"
}
};
var configuration = new MapperConfiguration(cfg => {
// First, map complex property types
cfg.CreateMap<Foo, TargetFlattened>();
cfg.CreateMap<Bar, TargetFlattened>();
// Then the root type
cfg.CreateMap<SourceRoot, TargetFlattened>()
.IncludeMembers(a => a.Foo, a => a.Bar)
.ReverseMap();
});
var target = configuration.CreateMapper().Map<TargetFlattened>(source);
Note that this will yield:
Id: 42
Addr: FooAddr
BarAddr: BarAddr
Name: FooName
The IncludeMembers() configuration will map members that don't adhere to the naming convention (Name instead of FooName for Foo.Name).
Meaning, if you have multiple complex properties with a Name property, you'll get the first included member's Name property mapped into your target's Name.
If you swap them to .IncludeMembers(a => a.Bar, a => a.Foo), your Name will be "BarName".
I have three lists which contains three same properties in each collection. I want to combine a result into one collection. Ex classes structure is as below
public class Order
{
public int ProductId { get; set; }
public int CustomerId { get; set; }
public int OrderId { get; set; }
// Few other Properties of OrderDetail
}
public class PaymentDetail
{
public int ProductId { get; set; }
public int CustomerId { get; set; }
public int OrderId { get; set; }
// Few other Properties form PaymentDetail
}
public class CouponUsageDetail
{
public int ProductId { get; set; }
public int CustomerId { get; set; }
public int OrderId { get; set; }
// Few other Properties form CouponUsageDetail
}
This type of output is coming from one API service where each class is in form of list object (JSON format) and we need to perform some operations on this. All three properties (ProductId,CustomerId, OrderId) contains same values in each collection which means all three properties are repeated in each collection. We need to perform some look ups on these collections. So in normal way what we can do with it is as - start a foreach from Order list and filter all three matching properties of PaymentDetail and CouponUsageDetail. But it would be costlier in term of performance when the data size is increased. So thought of making nesting structure upfront and avoid lookups. If we make the output nested as below this will help to avoid lookups on PaymentDetail and CouponUsageDetail.
Ex - we are receiving JOSN in below format
{"Orders":[{"ProductId":301,"CustomerId":101,"OrderId":201},{"ProductId":701,"CustomerId":501,"OrderId":601}],"PaymentDetails":[{"ProductId":301,"CustomerId":101,"OrderId":201},{"ProductId":701,"CustomerId":501,"OrderId":601}],"CouponUsageDetails":[{"ProductId":301,"CustomerId":101,"OrderId":201},{"ProductId":701,"CustomerId":501,"OrderId":601}]}
and with this output we want to form object as
public class OrderDetails
{
public int ProductId { get; set; }
public int CustomerId { get; set; }
public int OrderId { get; set; }
// Few other Properties of OrderDetail
List<PaymentDetail> PaymentDetail { get; set; }
List<CouponUsageDetail> CouponUsageDetail { get; set; }
}
Can you guide, what would be the best optimum usage of linq where we can combine all these three matching properties and make it just one nested structure better?
Thank You!
Note: I know this structure needs to be normalized but please ignore the normalization rule here as this is not in our control.
What are you describing sounds like two standard multi-key LINQ group joins. They are quite efficient (LINQ to Objects implementation uses prepared fast hash based lookups), so no further optimizations are needed:
var orderDetails = (
from o in data.Orders
join p in data.PaymentDetails
on new { o.ProductId, o.CustomerId, o.OrderId }
equals new { p.ProductId, p.CustomerId, p.OrderId }
into orderPaymentDetails
join c in data.CouponUsageDetails
on new { o.ProductId, o.CustomerId, o.OrderId }
equals new { c.ProductId, c.CustomerId, c.OrderId }
into orderCouponUsageDetails
select new OrderDetails
{
ProductId = o.ProductId,
CustomerId = o.CustomerId,
OrderId = o.OrderId,
// Few other Properties of OrderDetail
PaymentDetail = orderPaymentDetails.ToList(),
CouponUsageDetail = orderCouponUsageDetails.ToList(),
})
.ToList();
There seems to be a number of questions here combined, I'll try to work through them:
Data Models and Deserialization
With respect to generating a single structure from your API response, I would recommend using the Newtonsoft.Json libraries, available on NuGet Json.NET. They will allow you to deserialize the response from your API, into a single object, which given the sample you provided, should contain a collection of each of your models, Order, PaymentDetail, CouponUsageDetail:
public class APIResponceContainer
{
[JsonProperty("Orders")]
public List<Order> Orders { get; set; }
[JsonProperty("PaymentDetails")]
public List<PaymentDetail> PaymentDetails { get; set; }
[JsonProperty("CouponUsageDetails")]
public List<CouponUsageDetail> CouponUsageDetails { get; set; }
public APIResponceContainer()
{
Orders = new List<Order>();
PaymentDetails = new List<PaymentDetail>();
CouponUsageDetails = new List<CouponUsageDetail>();
}
}
Be aware to add the required attributes to each of your models as so:
public class Order
{
[JsonProperty("ProductId")]
public int ProductId { get; set; }
[JsonProperty("CustomerId")]
public int CustomerId { get; set; }
[JsonProperty("OrderId")]
public int OrderId { get; set; }
}
Deserialization then happens from your JSON string, as such:
StringReader stringReader = new StringReader(myJSONString);
JsonSerializer js = JsonSerializer.Create();
APIResponceContainer APIResponce = (APIResponceContainer)js.Deserialize(stringReader, typeof(APIResponceContainer));
Queries
As discussed in the comments, your data is unfortunately in terrible need of normalization. However, what I have inferred is that you would like to produce a flat structure, maintaining the "Few other Properties" and "key properties", for a combination of Order, PaymentDetail and CouponUsageDetail. You can use Linq for this, importantly I would recommend you choose yourself a "Primary Key". In other words, one property that can independently tie all the others together. In the example below, I have choose OrderID since it should be unique (?):
var flatSequence =
from order in APIResponce.Orders
join coupon in APIResponce.CouponUsageDetails on order.OrderId equals coupon.OrderId
join payment in APIResponce.PaymentDetails on order.OrderId equals payment.OrderId
select new
{
// Here extract all the properties you care about
OrderID = order.OrderId,
Customer = order.CustomerId,
Product = order.ProductId,
// All the "other Properties" ?
BankDetail = payment.PaymentOnlyProperty
};
Here I have extracted to var, however if you know the final flat structure you would like, of course determine a class of your own to receive the result.
Please comment if there is any questions.
You can use inheritance.
public class ResultCollection : Collection1
{
List<Collection2> Collection2s { get; set; }
List<Collection3> Collection3s { get; set; }
}
and then
var result = new ResultCollection {
PropId1 = Collection1.PropId1,
PropId2 = Collection1.PropId2,
...
Collection2s = Collection2,
Collection3s = Collection3
}
An automapper can be helpful here.
https://docs.automapper.org/en/stable/
I have solution, but i am not sure if it's ok for you. It depends on data format that you have on the begining.
solution:
class Program
{
static void Main(string[] args)
{
var collection1 = new Collection1() { PropId1 = 1, PropId2 = 2, PropId3 = 3 };
var list2 = new List<Collection2>()
{
new Collection2
{
PropId1 = 11,
PropId2 = 22,
PropId3 = 33
},
new Collection2
{
PropId1 = 22,
PropId2 = 33,
PropId3 = 44
}
};
var list3 = new List<Collection3>()
{
new Collection3
{
PropId1 = 111,
PropId2 = 222,
PropId3 = 333
},
new Collection3
{
PropId1 = 222,
PropId2 = 333,
PropId3 = 444
}
};
var result = new ResultCollection(collection1, list2, list3);
//or
var result2 = new ResultCollection(collection1) //but in this case you have to change your constructor
{
Collection2s = list2,
Collection3s = list3
};
Console.ReadLine();
}
}
public class Collection1
{
public int? PropId1 { get; set; }
public int? PropId2 { get; set; }
public int? PropId3 { get; set; }
}
public class Collection2
{
public int? PropId1 { get; set; }
public int? PropId2 { get; set; }
public int? PropId3 { get; set; }
}
public class Collection3
{
public int? PropId1 { get; set; }
public int? PropId2 { get; set; }
public int? PropId3 { get; set; }
}
public class ResultCollection : Collection1
{
public ResultCollection() { }
public ResultCollection(Collection1 collection, List<Collection2> list2, List<Collection3> list3)
{
foreach (PropertyInfo prop in collection.GetType().GetProperties())
{
PropertyInfo prop2 = collection.GetType().GetProperty(prop.Name);
if (prop2.CanWrite)
prop2.SetValue(this, prop.GetValue(collection, null), null);
}
Collection2s = list2;
Collection3s = list3;
}
public List<Collection2> Collection2s { get; set; }
public List<Collection3> Collection3s { get; set; }
}
But can you give an example of input data?
I have different classes sharing some properties of same type and name. I wish to assign same property values to each other. I explain my intention better in comments in the following pseudo-code. Is it possible in C#?
Ponder that there are a plethora of common properties but in unrelated classes, must we assign them one-by-one?
Second case is about sharing same properties but some of them may be nullable, who knows!
Side note: the classes already exist, cannot be altered, touched. Kinda sealed.
Can't it be done using nameofoperator and two for loops? Compare property names if matched, assign?
using System;
namespace MainProgram
{
class HomeFood
{
public DateTime Date { get; set; }
public string food1 { get; set; }
public string food2 { get; set; }
public int cucumberSize { get; set; }
}
class AuntFood
{
public string food2 { get; set; }
public int cucumberSize { get; set; }
public DateTime Date { get; set; }
public string food1 { get; set; }
// extra
public double? length { get; set; }
}
class GrandpaFood
{
public string? food2 { get; set; }
public int cucumberSize { get; set; }
public DateTime? Date { get; set; }
public string food1 { get; set; }
// extra
}
static class Program
{
public static void Main(string[] args)
{
var home = new HomeFood
{
Date = new DateTime(2020, 1, 1),
food1 = "cucumber",
food2 = "tomato",
cucumberSize = 123
};
var aunt = new AuntFood();
/*
First case: same types
Expected for-each loop
assigning a class's property values
to other class's property values
or for-loop no matter
foreach(var property in HomeFood's properties)
assign property's value to AuntFood's same property
*/
var home2 = new HomeFood();
var grandpa = new GrandpaFood
{
Date = new DateTime(2020, 1, 1),
food1 = "dfgf",
food2 = "dfgdgfdg",
cucumberSize = 43534
};
/*
Second case: similar to first case
with the exception of same type but nullable
or for-loop no matter
foreach(var property in GrandpaFood's properties)
assign property's value to GrandpaFood's same property
we don't care if it is null e.g.
Home2's same property = property's value ?? default;
*/
}
}
}
Based on the comments in the questions, this is just to show how it can be done with reflection.
Disclaimer, this is just a very simplified example on how to use reflection to sync properties. It does not handle any special cases (modifiers, read only, type mismatch, etc)
I would strongly suggest to use automapper to achieve the qp goals.
public class Type1
{
public string Property1 { get; set; }
public string Property2 { get; set; }
}
public class Type2
{
public string Property1 { get; set; }
public string Property3 { get; set; }
}
class Program
{
static void Main(string[] args)
{
var t1 = new Type1 { Property1 = "Banana" };
var t2 = new Type2();
var properties1 = typeof(Type1).GetProperties().ToList();
var properties2 = typeof(Type2).GetProperties().ToList();
foreach(var p in properties1)
{
var found = properties2.FirstOrDefault(i => i.Name == p.Name);
if(found != null)
{
found.SetValue(t2, p.GetValue(t1));
}
}
Console.WriteLine(t2.Property1);
}
}
The short answer is, apply OOP. Define a base Food class and inherit from it in any specific food classes you have. You can put all the shared props in the base class.
public class Food
{
public string food2 { get; set; }
// other shared stuff
}
class GrandpaFood : Food
{
// other specific stuff
}
As others have said, use some of the Object Oriented properties, like inheriting a super class of implement an interface.
In case you go for inheritance, consider making the super class (the one you inherit from) abstract. This means that the super class itself cannot be instantiated, which greatly reduces the risk of violating the Liskov Substitutional Principle. Also it often reflects the real problem better. In your example, this would also be the case, as “food” is not an actual thing in the real world, but rather a group of things.
I like to define Enums for my static, data shaping properties of my objects. For example, status and type columns I always like to have defined in an enum with a name and a long form description like this:
public enum TordDocumentDocumentTypes
{
[System.ComponentModel.Description("Annual Meeting Notice or Materials")]
AnnualMeeting = 1,
[System.ComponentModel.Description("Annual Formula Rate Posting")]
AnnualFormulaRate =2
}
I also like to then turn these enums into something that can be persited into a lookup table. I tend to let the code do the "driving" here. Meaning that I don't let the lookups change from the DB side, instead I make it so the code changes the lookup. I'll often have a function somewhere on a app page to do this.
Does anyone have a better way that they perform these common functions of capturing status and types?
Querying with EntityFramework:
It should support what you are trying to accomplish.
https://www.entityframeworktutorial.net/EntityFramework5/enum-in-entity-framework5.aspx
Querying with ADO.NET:
If you are using ADO.NET you can cast the the status value representation coming back from the database into an enum (vice versa for inserts). You could accomplish this by creating an extension method for an integer. This could be called when creating your DTO collection.
Heres A Code Example:
public enum Status : int
{
Unknown = 0,
Shipped = 1,
Pending = 2,
BackOrdered = 3,
}
public class DatabaseRecord
{
public int ID { get; set; }
public string Item { get; set; }
public int OrderStatus { get; set; }
}
public class DTO
{
public int ID { get; set; }
public string Item { get; set; }
public Status OrderStatus { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<DatabaseRecord> Database = new List<DatabaseRecord>()
{
new DatabaseRecord(){ ID = 1, Item = "Socks", OrderStatus = 1},
new DatabaseRecord(){ ID = 1, Item = "Shoes", OrderStatus = 2},
new DatabaseRecord(){ ID = 1, Item = "TShirt", OrderStatus = 11}
};
List<DTO> DTOCollection = Database.Select(x => new DTO{
ID = x.ID,
Item = x.Item,
OrderStatus = x.OrderStatus.ToEnum<Status>(Status.Unknown)
}).ToList();
foreach(var memberOfDTO in DTOCollection)
{
Console.WriteLine($"{ memberOfDTO.OrderStatus }");
}
}
}
public static class Extenstions
{
public static T ToEnum<T>(this int integer, T defaultValue) where T : struct, IConvertible
{
if (Enum.IsDefined(typeof(T), integer))
{
return (T)Enum.Parse(defaultValue.GetType(), integer.ToString());
}
return Activator.CreateInstance<T>();
}
}
Output:
Screenshot Of Console
Shipped
Pending
Unknown
Let say I have a base class and two derived classes.
public abstract class BaseClass
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Owner { get; set; }
public DateTime DateAdded { get; set; }
}
public class Foo : BaseClass
{
[CustomAttr(Order = 2)]
public string Country { get; set; }
[CustomAttr(Order = 5)]
public decimal Amount { get; set; }
public string Other { get; set; }
}
public class Bar : BaseClass
{
[CustomAttr(Order = 3)]
public string Organization { get; set; }
[CustomAttr(Order = 1)]
public string Keywords { get; set; }
}
By default, the order of the properties is based on how they are declared in the class so if in the BaseClass since there is no [CustomAttr(Order = n) assume this is the correct order.
Now since in the two derived class, there is a defined custom attribute that will identify the property order the behavior should be sorted by:
Id
Country
Name
Description
Amount
Owner
DateAdded
Other
So what will happen those with CustomAttr[(Order = n)] should be placed to their property order and for those that do not have we assume they are in there proper order. This should also have a similar behavior if I use the Bar class.
The use case of this is I need to have the right order of the properties of the class in the List<T> to have the correct order of column in an excel file.
What I did is I have to add the CustomAttr[(Order = n)] to all of the properties to sort them however this is a tedious thing to do which require you to change the order of all properties if you try to change one of the property order.
Any way that I can achieve this?
You can use reflection to read the names of all of the properties of a class in the order they are declared. You can then elaborate this in your logic and sort the fields accordingly.
Try the following:
PropertyInfo[] propertyInfos = typeof(Bar).GetProperties();
foreach (var propInfo in propertyInfos)
Console.WriteLine(propInfo.Name);
This will write all of the properties in the Bar class (this is just an example, you can replace that by any of your classes), including the properties inherited from its superclass (BaseClass). Expected output:
Organization
Keywords
Id
Name
Description
Owner
DateAdded
Notice though that this method lists the properties from the class first, and then goes up in the hierarchy listing each superclass (this is why Bar's members are being listed before BaseClass's members). You can elaborate the code a little bit more to change the order according to your needs.
The following (non-optimized) code first creates a list of all of the given class' hierarchy, starting from the base class towards the given T class. After that, it iterates over each of the classes, discovering only the properties defined in each class (I'm passing the GetProperties() method a parameter stating that I just want the properties that are public, instance/non-static, and declared on the specific class I am currently consulting).
private static void ListAllOrderedPropertiesOfType(Type targetType)
{
// Generate a list containing the whole hierarchy of classes, from the base type to the type T
var typesList = new List<Type>();
for (Type t = targetType; t != typeof(Object); t = t.BaseType)
typesList.Insert(0, t);
// Iterate from the base type to type T, printing the properties defined for each of the types
foreach (Type t in typesList)
{
PropertyInfo[] propertyInfos = t.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
foreach (var propInfo in propertyInfos)
Console.WriteLine(propInfo.Name);
}
}
So if you wanted to know all of the properties for type Bar, ordered from the topmost base class towards the Bar class, you could call this method like that:
ListAllOrderedPropertiesOfType(typeof(Bar));
Expected output would be the properties in the following order:
Id
Name
Description
Owner
DateAdded
Organization
Keywords
With that you know the fields' declaration order and their custom orders (through your CustomAttr attribute). You can now implement a sort method to order the fields according to their declaration orders and CustomAttr orders, according to your needs.
But I guess this is a little beyond the scope of my answer (which intends to show you how to get the order of declaration of properties, from a base class towards any given specific class).
I created a generic solution by reading your attribute and creating a comparer what will compare in the order of your attribute order. in the contructor of the comparer I am reading over reflection your attributes. While comparing I take one property after the other and when equal (zero), I go to the next.
The logic works also with inheritance, so even on the base class you can have CustomAttr.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
public class CustomAttr : Attribute
{
public CustomAttr():base()
{}
public CustomAttr(int Order)
{
this.Order = Order;
}
public int Order {get ; set ; }
}
public abstract class BaseClass
{
public int Id { get; set; }
[CustomAttr(Order = 20)]
public string Name { get; set; }
public string Description { get; set; }
public string Owner { get; set; }
public DateTime DateAdded { get; set; }
}
public class Foo : BaseClass
{
[CustomAttr(Order = 2)]
public string Country { get; set; }
[CustomAttr(Order = 5)]
public decimal Amount { get; set; }
public string Other { get; set; }
}
public class Bar : BaseClass
{
[CustomAttr(Order = 3)]
public string Organization { get; set; }
[CustomAttr(Order = 1)]
public string Keywords { get; set; }
}
class app
{
static void Main()
{
List<Bar> listToOrder = new List<Bar>();
listToOrder.Add(new Bar() { Id = 5, Keywords = "Hello", Organization = "Arlando" });
listToOrder.Add(new Bar() { Id = 12, Keywords = "Table", Organization = "Fuelta" , Name = "Deep"});
listToOrder.Add(new Bar() { Id = 12, Keywords = "Table", Organization = "Fuelta", Name = "Inherit" });
listToOrder.Add(new Bar() { Id = 1, Keywords = "Muppet", Organization = "Coke" });
listToOrder.Add(new Bar() { Id = 6, Keywords = "Grumpy", Organization = "Snow" });
listToOrder.Add(new Bar() { Id = 9, Keywords = "Johny", Organization = "White" });
listToOrder.Add(new Bar() { Id = 12, Keywords = "Table", Organization = "Bruno" });
listToOrder.Add(new Bar() { Id = 12, Keywords = "Table", Organization = "Fuelta" });
listToOrder.Add(new Bar() { Id = 7, Keywords = "Set", Organization = "Voltra" });
listToOrder.Add(new Bar() { Id = 45, Keywords = "Brr", Organization = "Elvis" });
listToOrder.Add(new Bar() { Id = 15, Keywords = "Tsss", Organization = "Marion" });
OrderComparer<Bar> myOrder = new OrderComparer<Bar>();
listToOrder.Sort(myOrder);
foreach (Bar oneBar in listToOrder)
{
Console.WriteLine(oneBar.Id + " " + oneBar.Keywords + " " + oneBar.Organization);
}
Console.ReadKey();
}
private class OrderComparer<T> : IComparer<T>
{
SortedList<int, PropertyInfo> sortOrder = new SortedList<int, PropertyInfo>();
public OrderComparer()
{
Type objType = typeof(T);
foreach (PropertyInfo oneProp in objType.GetProperties())
{
CustomAttr customOrder = (CustomAttr) oneProp.GetCustomAttribute(typeof(CustomAttr), true);
if (customOrder == null) continue;
sortOrder.Add(customOrder.Order, oneProp);
}
}
public int Compare(T x, T y)
{
Type objType = typeof(T);
int result = 0;
int i = 0;
while (result == 0 && i < sortOrder.Count)
{
result = ((IComparable)sortOrder.ElementAt(i).Value.GetValue(x)).CompareTo(sortOrder.ElementAt(i).Value.GetValue(y));
i++;
}
return result;
}
}
}
See this example for order using List with system.Linq
class Program
{
class test
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Owner { get; set; }
public DateTime DateAdded { get; set; }
}
static void Main(string[] args)
{
List<test> listOrder = new List<test>();
listOrder.Add(new test() { Id = 1, Name = "john", Description = "test", Owner = "test", DateAdded = DateTime.Now });
listOrder.Add(new test() { Id = 1, Name = "max", Description = "test1", Owner = "test1", DateAdded = DateTime.Now });
listOrder.Add(new test() { Id = 1, Name = "phil", Description = "test2", Owner = "test2", DateAdded = DateTime.Now });
List<test> sortbyName = listOrder.OrderBy(item => item.Name).ToList();
List<test> sortbyDescription = listOrder.OrderBy(item => item.Description).ToList();
List<test> sortbyOwner = listOrder.OrderBy(item => item.Owner).ToList();
}
}