Преобразование ESQL в LINQ для сущностей. Сортировать по связанным объектам - PullRequest
4 голосов
/ 09 декабря 2011

Я использую EF + RIA и, к сожалению, у меня возникают проблемы с сортировкой по связанным объектам.Для этой цели есть ESQL-запрос, который я реализовал (нашел только это решение):

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

Таблицы имеют следующую структуру:

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

<SomeEntity>:
    int Id;
    string Name;  

Есть ли способ переписать это (сортировка), используя подход LINQ to Entities?

1 Ответ

2 голосов
/ 11 декабря 2011

Вот моя попытка, я не могу гарантировать, что она сработает. Мне нужно больше думать о том, как получить динамическое имя столбца, я не уверен в этом. РЕДАКТИРОВАТЬ: вы можете использовать строку для столбца заказа.

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 (); // вызвать toList или перечислить для выполнения запроса, поскольку LINQ отложил выполнение.

РЕДАКТИРОВАТЬ: Я думаю, что заказ после выбранных остановок от заказа по. Я переместил оператор выбора в после заказа по. Я также добавил «query =», но я не уверен, если это необходимо. У меня нет способа проверить это в данный момент.

РЕДАКТИРОВАТЬ 3: Сегодня я запустил LINQPad и сделал несколько поправок к тому, что у меня было раньше. Я смоделировал ваши данные в подходе Code-first к использованию EF, и он должен быть близок к тому, что у вас есть. Этот подход работает лучше, если вы просто пытаетесь получить список атрибутов (чего у вас нет). Чтобы обойти это, я добавил свойство Entity в класс MyAttribute. Этот код работает в 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 /8556/dinamicheskii-linq-orderby-na-ienumerable-t-iqueryable-t
    System.Reflection.PropertyInfo propertyInfo=obj.GetType().GetProperty(property);
    return propertyInfo.GetValue(obj, null);
}
...