Отображение объектов с помощью LINQ и SubSonic - PullRequest
1 голос
/ 22 октября 2009

Я создаю небольшой проект с SubSonic 3.0.0.3 ActiveRecord, и у меня возникла проблема, которую я, похоже, не могу решить.

Вот запрос LINQ:

var result = from r in Release.All()
             let i = Install.All().Count(x => x.ReleaseId == r.Id)
             where r.ProductId == productId
             select new ReleaseInfo
             {
                 NumberOfInstalls = i,
                 Release = new Release
                 {
                     Id = r.Id,
                     ProductId = r.ProductId,
                     ReleaseNumber = r.ReleaseNumber,
                     RevisionNumber = r.RevisionNumber,
                     ReleaseDate = r.ReleaseDate,
                     ReleasedBy = r.ReleasedBy
                 }
             };

Объект ReleaseInfo является пользовательским классом и выглядит следующим образом:

public class ReleaseInfo
{
    public Release Release { get; set; }
    public int NumberOfInstalls { get; set; }
}

Release и Install - это классы, созданные SubSonic.

Когда я наблюдаю за результатом, свойство Release имеет значение null.

Если я сделаю этот запрос проще и посмотрю результат, значение не будет нулевым.

var result = from r in Release.All()
             let i = Install.All().Count(x => x.ReleaseId == r.Id)
             where r.ProductId == productId
             select new Release
             {
                 Id = r.Id,
                 ProductId = r.ProductId,
                 ReleaseNumber = r.ReleaseNumber,
                 RevisionNumber = r.RevisionNumber,
                 ReleaseDate = r.ReleaseDate,
                 ReleasedBy = r.ReleasedBy
             };

Это проблема с моим запросом LINQ или ограничение SubSonic?

Ответы [ 4 ]

1 голос
/ 20 января 2010

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

Вот фрагмент: строка 269 - 298 из SubSonic.Linq.Structure.DbQueryProvider

IEnumerable<T> result;
Type type = typeof (T);
//this is so hacky - the issue is that the Projector below uses Expression.Convert, which is a bottleneck
//it's about 10x slower than our ToEnumerable. Our ToEnumerable, however, stumbles on Anon types and groupings
//since it doesn't know how to instantiate them (I tried - not smart enough). So we do some trickery here.
    if (type.Name.Contains("AnonymousType") || type.Name.StartsWith("Grouping`") || type.FullName.StartsWith("System.")) {
    var reader = _provider.ExecuteReader(cmd);
    result = Project(reader, query.Projector);
    } else
    {
        using (var reader = _provider.ExecuteReader(cmd))
        {
            //use our reader stuff
            //thanks to Pascal LaCroix for the help here...
            var resultType = typeof (T);
            if (resultType.IsValueType)
            {
                result = reader.ToEnumerableValueType<T>();
            }
            else
            {
                result = reader.ToEnumerable<T>();
            }
        }
    }
    return result;

Оказывается, что SubSonic ToEnumerable пытается сопоставить имена столбцов в устройстве чтения данных со свойствами объекта, на который вы пытаетесь проецировать. Запрос SQL из моего Linq выглядит так:

SELECT [t0].[Id], [t0].[ProductId], [t0].[ReleaseDate], [t0].[ReleasedBy], [t0].[ReleaseNumber], [t0].[RevisionNumber], [t0].[c0]
FROM (
  SELECT [t1].[Id], [t1].[ProductId], [t1].[ReleaseDate], [t1].[ReleasedBy], [t1].[ReleaseNumber], [t1].[RevisionNumber], (
    SELECT COUNT(*)
    FROM [dbo].[Install] AS t2
    WHERE ([t2].[ReleaseId] = [t1].[Id])
    ) AS c0
  FROM [dbo].[Release] AS t1
  ) AS t0
WHERE ([t0].[ProductId] = 2)

Обратите внимание, что [t0]. [C0] не совпадает с именем моего свойства NumberOfInstalls. Таким образом, значение c0 никогда не проецируется на мой объект.

ИСПРАВЛЕНИЕ: Вы можете просто взять оператор if и использовать в 10 раз более медленную проекцию, и все будет работать.

1 голос
/ 04 ноября 2009

Я думаю, что проблема может заключаться в том, что вы по сути дублируете функциональность ORM. Главное, что нужно понять, это строка:

from r in Release.All()

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

Используя эту логику, вы сможете сделать следующее:

 var result = from r in Release.All()
              select new ReleaseInfo {
                  Release = r,
                  NumberOfInstalls = Install.All().Count(x => x.ReleaseId == r.Id)
              };

При этом вам следует взглянуть на вызов Install.All (), потому что он может быть крайне неэффективным. Что это будет делать, это извлекать каждую установку из базы данных, объединять эти установки в объекты, а затем сравнивать идентификатор каждой записи в .NET, чтобы проверить, удовлетворяет ли запись этому условию. Вы можете использовать метод .Find в SubSonic, чтобы возвращать только определенные записи на уровне базы данных, что должно значительно повысить производительность. Даже несмотря на это, раздувание объектов может все еще быть дорогим, и вы можете рассмотреть представление или хранимую процедуру здесь. Но в качестве простого первого шага должно работать следующее:

var result = from r in Release.All()
             select new ReleaseInfo {
                 Release = r,
                 NumberOfInstalls = Install.Find(x => x.ReleaseId == r.Id).Count()
             };
0 голосов
/ 18 мая 2010

Это было исправлено в 3.0.0.4? Я был так взволнован, чтобы найти этот пост. После двух дней попыток выяснить, почему мои прогнозы не работали - за исключением случаев, когда имена свойств точно соответствовали запросу - я оказался здесь. Я настолько зависим от SS SimpleRepository, что сейчас уже поздно возвращаться. Такая ошибка наносит вред. Есть ли шанс, что это улажено?

На данный момент я пошел в 10 раз медленнее, чтобы я мог, по крайней мере, выпустить его для своего клиента. Очень предпочел бы, чтобы более быстрый метод работал правильно:)

0 голосов
/ 13 января 2010

У нас есть ошибка с проекциями, которая срабатывает в определенных случаях - я думаю, что она исправлена, но мне нужно больше ее проверить. Я приглашаю вас попробовать последние версии - я думаю, что мы исправили это ... извините, что был настолько расплывчатым, но ошибка работала между 3.0.0.1 и 3.0.0.3, и я не смог ее найти.

...