LinqToSQl и членский доступ не являются законными по типу исключения

Основная проблема...

У меня есть метод, который выполняет следующий код:

IList<Gig> gigs = GetGigs().WithArtist(artistId).ToList();

Метод GetGigs() получает Gigs из моей базы данных через LinqToSql...

Итак, когда выполняется GetGigs(). WithArtist (artistId).ToList(), я получаю следующее исключение:

Member access 'ListenTo.Shared.DO.Artist Artist' of 'ListenTo.Shared.DO.Act' not legal on type 'System.Collections.Generic.List`1[ListenTo.Shared.DO.Act] 

Обратите внимание, что функция расширения "WithArtist" выглядит так:

    public static IQueryable<Gig> WithArtist(this IQueryable<Gig> qry, Guid artistId)
    {
        return from gig in qry
               where gig.Acts.Any(act => (null != act.Artist) && (act.Artist.ID == artistId))
               orderby gig.StartDate
               select gig;
    }

Если я заменил метод GetGigs() на метод, который создает коллекцию концертов в code (а не в DB через LinqToSQL), я НЕ получаю исключение.

Поэтому я уверен, что проблема связана с моим кодом LinqToSQl, а не с структурой объекта.

Однако у меня нет ИДЕИ, почему версия LinqToSQl не работает, поэтому я включил весь связанный код ниже. Любая помощь будет ОЧЕНЬ с благодарностью принята!

Код LinqToSQL....

    public IQueryable<ListenTo.Shared.DO.Gig> GetGigs()
    {
        return from g in DBContext.Gigs
               let acts = GetActs(g.ID)
               join venue in DBContext.Venues on g.VenueID equals venue.ID
               select new ListenTo.Shared.DO.Gig
               {
                   ID = g.ID,
                   Name = g.Name,
                   Acts = new List<ListenTo.Shared.DO.Act>(acts),
                   Description  = g.Description,
                   StartDate    = g.Date,
                   EndDate      = g.EndDate,
                   IsDeleted    = g.IsDeleted,
                   Created      = g.Created,
                   TicketPrice  = g.TicketPrice,
                   Venue        =  new ListenTo.Shared.DO.Venue { 
                                    ID = venue.ID, 
                                    Name = venue.Name, 
                                    Address = venue.Address,
                                    Telephone = venue.Telephone,
                                    URL = venue.Website 
                   }

               };
    }



    IQueryable<ListenTo.Shared.DO.Act> GetActs()
    {
        return from a in DBContext.Acts

               join artist in DBContext.Artists on a.ArtistID equals artist.ID into art
               from artist in art.DefaultIfEmpty()

               select new ListenTo.Shared.DO.Act
               {
                    ID = a.ID,
                    Name = a.Name,
                    Artist = artist == null ? null : new Shared.DO.Artist
                    {
                       ID =  artist.ID,
                       Name = artist.Name
                    },
                    GigId = a.GigID

               };
    }

    IQueryable<ListenTo.Shared.DO.Act> GetActs(Guid gigId)
    {
        return GetActs().WithGigID(gigId);
    } 

Я включил код для объектов Act, Artist и Gig ниже:

public class Gig : BaseDO
{

    #region Accessors

    public Venue Venue
    {
        get;
        set; 
    }

    public System.Nullable<DateTime> EndDate
    {
        get;
        set;
    }

    public DateTime StartDate
    {
        get;
        set;
    }

    public string Name
    {
        get;
        set;
    }

    public string Description
    {
        get;
        set;
    }

    public string TicketPrice
    {
        get;
        set;
    }

    /// <summary>
    /// The Act object does not exist outside the context of the Gig, therefore,
    /// the full act object is loaded here.
    /// </summary>
    public IList<Act> Acts
    {
        get;
        set;
    }

    #endregion
}

public class Act : BaseDO
{
    public Guid GigId { get; set; }
    public string Name { get; set; }
    public Artist Artist { get; set; }
}

public class Artist : BaseDO
{
    public string Name { get; set; }
    public string Profile { get; set; }
    public DateTime Formed { get; set; }
    public Style Style { get; set; }
    public Town Town { get; set; }
    public string OfficalWebsiteURL { get; set; }
    public string ProfileAddress { get; set; }
    public string Email { get; set; }
    public ImageMetaData ProfileImage { get; set; }

}

public class BaseDO: IDO
{
    #region Properties

    private Guid _id;

    #endregion

    #region IDO Members

    public Guid ID
    {
        get
        {
            return this._id;
        }
        set
        {
            this._id = value;
        }
    }




}

}

Ответ 1

У меня была такая же проблема, и мне казалось, что это трюк, - это отделить встроенный вызов статического метода, который возвратил IQueryable < > , чтобы я сохранил этот отложенный запрос в переменной и ссылался на это.

Я думаю, что это ошибка в Linq to SQL, но, по крайней мере, существует разумное решение. Я еще не проверял это, но мое предположение заключается в том, что эта проблема может возникнуть только при ссылке на статические методы другого класса в выражении запроса независимо от того, является ли возвращаемый тип этой функции IQueryable < > . Так что, возможно, это класс, который содержит метод, лежащий в основе проблемы. Как я уже сказал, я не смог подтвердить это, но, возможно, стоит изучить.

ОБНОВЛЕНИЕ: На всякий случай, когда решение не ясно, я хотел бы указать его в контексте примера из исходного сообщения.

public IQueryable<ListenTo.Shared.DO.Gig> GetGigs()
{
    var acts = GetActs(g.ID); // Don't worry this call is deferred

    return from g in DBContext.Gigs
           join venue in DBContext.Venues on g.VenueID equals venue.ID
           select new ListenTo.Shared.DO.Gig
           {
               ID = g.ID,
               Name = g.Name,
               Acts = new List<ListenTo.Shared.DO.Act>(acts),
               Description  = g.Description,
               StartDate    = g.Date,
               EndDate      = g.EndDate,
               IsDeleted    = g.IsDeleted,
               Created      = g.Created,
               TicketPrice  = g.TicketPrice,
               Venue        =  new ListenTo.Shared.DO.Venue { 
                                ID = venue.ID, 
                                Name = venue.Name, 
                                Address = venue.Address,
                                Telephone = venue.Telephone,
                                URL = venue.Website 
               }

           };
}

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

Ответ 2

Я думаю, что проблема заключается в утверждении 'let' в GetGigs. Использование "let" означает, что вы определяете часть окончательного запроса отдельно от основного набора для извлечения. проблема в том, что 'let', если это не скаляр, приводит к вложенному запросу. Вложенные запросы на самом деле не Linq на sql, поскольку они также откладываются. В вашем запросе вы помещаете результаты вложенного запроса в проекцию основного набора для возврата, который затем добавляется вместе с операторами linq.

Когда это произойдет, вложенный запрос будет погружен глубже в запрос, который будет выполнен, и это приведет к ситуации, когда вложенный запрос не находится во внешней проекции выполняемого запроса и, следовательно, должен быть объединен в SQL-запрос запустился в БД. Это не выполнимо, так как это вложенный запрос в проекции, вложенной в основной SQL-запрос, и SQL не имеет понятия, такого как "вложенный запрос в проекции", поскольку вы не можете получить набор элементов внутри проекции в SQL, только скаляры.

Ответ 3

Я ничего не вижу в ваших классах, чтобы указать, как LINQ to SQL предназначен для определения того, какой столбец является и т.д.

Вы ожидали, что метод WithArtist будет запущен в .NET или преобразован в SQL? Если вы ожидаете, что он будет преобразован в SQL, вам нужно будет украсить ваш класс Gig соответствующими атрибутами LINQ to SQL (или настроить другой контекст данных). Если вы хотите, чтобы он выполнялся в коде, просто измените первый тип параметра с IQueryable<Gig> на IEnumerable<Gig>.