4

I am using EF + RIA and unfortunately meet some problems with sorting by related entities. For such purpose there is ESQL query that I implemented (found only this solution):

var queryESQL = string.Format(
@" select VALUE ent from SomeEntities as ent 
   join Attributes as ea ON ea.EntityId = ent.Id 
   where ea.AttributeTypeId = @typeId
   order by ea.{0} {1}", columnName, descending ? "desc" : "asc");

var query = ObjectContext.CreateQuery<SomeEntity>(queryESQL, new ObjectParameter("typeId", attributeTypeId));                                                        

Tables have following structure:

<Attribute>:
    int Id;
    decimal DecimalColumn;
    string StringColumn;
    int EntityId;
    int AttributeTypeId;

<SomeEntity>:
    int Id;
    string Name;  

Is there any way to rewrite this stuff(sorting), using LINQ to Entities approach?

Jonathan Leffler
  • 666,971
  • 126
  • 813
  • 1,185
Anatolii Gabuza
  • 5,848
  • 2
  • 32
  • 52

1 Answers1

2

Here's my attempt, I can't guarantee it will work. I need to think more on how to get a dynamic column name, I'm not sure on that one. EDIT: you can use a string for the order column.

int typeId = 1115;
bool orderAscending = false;
string columnName = "StringColumn";
var query = from ent in SomeEntities
join ea in Attributes on ea.EntityId = ent.Id
where ea.AttributeTypeId = typeId;

if(orderAscending)
{
  query = query.OrderBy(ea => columnName).Select(ea => ea.Value);
}
else
{
  query = query.OrderByDescending(ea => columnName).Select(ea => ea.Value);
}

var results = query.ToList(); // call toList or enumerate to execute the query, since LINQ has deferred execution.

EDIT: I think that ordering after the select stops is from ordering by. I moved the select statement to after the order by. I also added the "query =", but I'm not sure if that is needed. I don't have a way to test this at the moment.

EDIT 3: I fired up LINQPad today and made a few tweaks to what I had before. I modeled your data in a Code-first approach to using EF and it should be close to what you have. This approach works better if you're just trying to get a list of Attributes (which you aren't). To get around that I added an Entity property to the MyAttribute class. This code works in LINQPAD.

void Main()
{
    // add test entities as needed. I'm assuming you have an Attibutes collection on your Entity based on your tables.
    List<MyEntity> SomeEntities = new List<MyEntity>();
    MyEntity e1 = new MyEntity();
    MyAttribute a1 =  new MyAttribute(){ StringColumn="One", DecimalColumn=25.6M, Id=1, EntityId=1, AttributeTypeId = 1, Entity=e1 };
    e1.Attributes.Add(a1);
    e1.Id = 1;
    e1.Name= "E1";
    SomeEntities.Add(e1);

    MyEntity e2 = new MyEntity();
    MyAttribute a2 = new MyAttribute(){ StringColumn="Two", DecimalColumn=198.7M, Id=2, EntityId=2, AttributeTypeId = 1, Entity=e2 };
    e2.Attributes.Add(a2);
    e2.Id = 2;
    e2.Name = "E2";
    SomeEntities.Add(e2);

    MyEntity e3 = new MyEntity();
    MyAttribute a3 = new MyAttribute(){ StringColumn="Three", DecimalColumn=65.9M, Id=3, EntityId=3, AttributeTypeId = 1, Entity=e3 };
    e3.Attributes.Add(a3);
    e3.Id = 3;
    e3.Name = "E3";
    SomeEntities.Add(e3);

    List<MyAttribute> attributes = new List<MyAttribute>();
    attributes.Add(a1);
    attributes.Add(a2);
    attributes.Add(a3);

    int typeId = 1;
    bool orderAscending = true;
    string columnName = "StringColumn";
    var query = (from ent in SomeEntities
    where ent.Attributes.Any(a => a.AttributeTypeId == typeId)
    select ent.Attributes).SelectMany(a => a).AsQueryable();
    query.Dump("Pre Ordering");
    if(orderAscending)
    {
      // query =  is needed
      query = query.OrderBy(att => MyEntity.GetPropertyValue(att, columnName));
    }
    else
    {
      query = query.OrderByDescending(att => MyEntity.GetPropertyValue(att, columnName));
    }

    // returns a list of MyAttributes. If you need to get a list of attributes, add a MyEntity property to the MyAttribute class and populate it
    var results = query.Select(att => att.Entity).ToList().Dump();
}

// Define other methods and classes here
}
    class MyAttribute
    {
    public int Id { get; set; }
    public decimal DecimalColumn { get; set; }
    public string StringColumn { get; set; }
    public int EntityId { get; set; }
    public int AttributeTypeId { get; set; }
    // having this property will require an Include in EF to return it then query, which is less effecient than the original ObjectQuery< for the question
    public MyEntity Entity { get; set; }

    }
    class MyEntity
    {
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<MyAttribute> Attributes { get; set; }
    public MyEntity()
    {
    this.Attributes = new List<MyAttribute>();
    }

    // this could have been on any class, I stuck it here for ease of use in LINQPad
    // caution reflection may be slow
    public static object GetPropertyValue(object obj, string property)
{
// from Kjetil Watnedal on http://stackoverflow.com/questions/41244/dynamic-linq-orderby
    System.Reflection.PropertyInfo propertyInfo=obj.GetType().GetProperty(property);
    return propertyInfo.GetValue(obj, null);
}
AlignedDev
  • 7,650
  • 7
  • 49
  • 87
  • I'm not shure, but there will not be any `Column1` property within `OrderBy` context. `ea` represent there an `SomeEntity`. – Anatolii Gabuza Dec 11 '11 at 22:08
  • I had to leave right as I was finishing my answer. Column1 was just a place holder at that time. I've updated my answer to show how you can dynamically order your queries. – AlignedDev Dec 12 '11 at 14:10
  • The main issue here is to sort by `Attributes`. Sorting by `Name` is not the goal here. – Anatolii Gabuza Dec 12 '11 at 16:46
  • The variable columnName in my example can be passed into a method which matches your SQL example: '...order by ea.{0} {1}", columnName...'. 'ea' is a reference to the attributes table, so I think it matches what you need? I changed it from Name to StringColumn if that helps. – AlignedDev Dec 12 '11 at 16:58
  • 1
    I was just learning about using the ObjectContext.CreateQuery as you have and it seems to be as a good as an approach as using the all LINQ approach. http://msdn.microsoft.com/en-us/library/bb339670.aspx – AlignedDev Dec 14 '11 at 13:56
  • You right! I just wander if there is any way for sorting by related (one-to-many) entities using LINQ, because there is really big bunch of code already written using LINQ and I don't want to break consistency. – Anatolii Gabuza Dec 14 '11 at 15:19
  • I've tried your solution, but seems it does not work for me. There is no errors, but data is still unsorted. – Anatolii Gabuza Dec 16 '11 at 10:01