Как написать выражение для свойства linq to sql?

Заранее извиняюсь за длинный вопрос. Я приложил немало усилий, чтобы сделать свой вопрос как можно более ясным за один раз. Пожалуйста, потерпите меня. ;о) любая помощь будет принята с благодарностью!

У меня есть классы Branch и Text:

class Branch
 int ID
 Text WebDescription
 and a bunch of other properties

class Text
 int ID
 string UK
 string NL
 string FR
 string IT
 and a bunch of other properties as well

Я хочу отображать только идентификатор ветки и ее описание на соответствующем языке. Мне нужен только один запрос (без дополнительных циклов), который извлекает только два поля (а не весь объект).

Я нашел три решения

Через объектную модель в запросе

// good: no round trips
// good: clean sql
// bad:  impossible to use the currentUserLanguage parameter
var lang = "NL";
var dbProject = new ProjectDataContext();
var query = from b in dbProject.GetTable<Branch>()
            select new
               {
                 b.ID,
                 WebDescription = b.WebDescriptionObject.NL // <-- problem
               };
var text = query.First().WebDescription;

Через объектную модель после запроса

// good: no round trips (eager loading of text object)
// good: possible to use the currentUserLanguage parameter
// bad:  loads the *whole* branch and text object, not just two fields
var lang= "NL";
var dbProject = new ProjectDataContext();
var query = from b in dbProject.GetTable<Branch>()
            select new
              {
                b.ID,
                WebDescription = b.GetWebDescriptionAsString(lang)
              };
var text = query.First().WebDescription;

Использование выражения

// good: I have the feeling I am on the right track
// bad: This doesn't work :o( throws an exception
var lang= "NL";
var dbProject = new ProjectDataContext();
var query = from b in dbProject.GetTable<Branch>()
            select new
              {
                b.ID,
                WebDescription = b.GetWebDescriptionAsExpression(lang)
              };
var text = query.First().WebDescription;

Вот код для двух методов GetWebDescriptionAsString и GetWebDescriptionAsExpression.

public string GetWebDescriptionAsString(string lang)
{
  if (lang== "NL") return WebDescriptionObject.NL;
  if (lang== "FR") return WebDescriptionObject.FR;
  if (lang== "IT") return WebDescriptionObject.IT;
  return WebDescriptionObject.UK;
}

public Expression<Func<Branch, string>> GetWebDescriptionAsExpression(string lang)
{
  if (lang== "NL") return b => b.WebDescriptionObject.NL;
  if (lang== "FR") return b => b.WebDescriptionObject.FR;
  if (lang== "IT") return b => b.WebDescriptionObject.IT;
  return b => b.WebDescriptionObject.UK;
}

person RandomProgrammer    schedule 31.01.2009    source источник


Ответы (5)


Без реального ответа на вопрос самым чистым подходом было бы изменить структуру текста в более нормализованную форму, например:

Text
    ID

TextTranslation
    ID
    TextID
    Lang
    TextValue

где каждый текст имеет несколько переводов, по одному на каждый язык.

Запрос станет примерно таким:

var q = 
    from branch in dbProject.Branches
    join text in dbProject.Texts on branch.TextID = text.ID
    join translation in dbProject.TextTranslations on text.ID = translation.TextID
    where translation.Lang == lang
    select new
    {
        branch.ID,
        WebDescription = translation.TextValue
    };

У этого подхода есть и другие преимущества, например, добавление нового языка не изменит структуру модели.

person Aleris    schedule 31.01.2009
comment
Да, знаю. Но sql-эксперты пообещали нам, что это с точки зрения производительности (не уверен, что он прав). Во всяком случае, изменить структуру больше не вариант. - person RandomProgrammer; 31.01.2009
comment
Я почти уверен, что он не прав :) Решением было бы поместить условие прямо в запрос, например: WebDescription = lang == NL ? b.Text.NL : lang == FR ? b.Text.FR : ... Не проверял, но думаю, что это даст правильный sql. - person Aleris; 31.01.2009

Это было бы очень легко сделать, если бы вы использовали хранимую процедуру. Вы против использования SP в качестве решения?

person E.J. Brennan    schedule 31.01.2009

Если хранимая процедура работает, я с удовольствием ее использую.

person RandomProgrammer    schedule 31.01.2009

Спасибо за быстрый ответ.

Я сделал быструю попытку. UDF уже был там, я просто не знал, как его использовать. Производительность сильно упала. Первое решение в 3 раза быстрее. Насколько я понимаю, этот подход потребует дополнительных обращений к базе данных. Это правильно?

var query = from b in dbProject.GetTable<Branch>()
            select new
              {
                b.ID,
                WebDescription = db.fGetText(b.WebDescriptionID, (currentUserLanguage))
              };
person RandomProgrammer    schedule 31.01.2009
comment
Непонятно, кого / что вы спрашиваете ... вы пытаетесь вернуть все идентификаторы веток, а затем их описания, или одну ветку и одно описание? - person E.J. Brennan; 31.01.2009

Не понимая всей вашей проблемы

создайте хранимую процедуру следующим образом:

CREATE PROCEDURE spGetTheTextINeed @Language char(2), @BranchID int
AS
  /* I don't know how your database is structured so you need to write this */
  SELECT MyText from MyTable WHERE Language=@Language and Branch=@BranchID


Затем вам нужно добавить sp в свой DBML, а затем вы можете просто вызвать нужный вам sp с соответствующими параметрами:

var query = myDataContext.spGetTheTextINeed("NL",[your branch number]) 

  Dim str As String 
  str = query.MyText 

Приведенный выше код не является точным - я не понимаю ваших полных требований, но это должно помочь вам начать работу.

person E.J. Brennan    schedule 31.01.2009