【问题标题】:LinqToSQl and the Member access not legal on type exceptionLinqToSQl 和成员访问在类型异常上不合法
【发布时间】:2009-02-07 16:28:06
【问题描述】:

基本问题...

我有一个执行以下代码的方法:

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

GetGigs() 方法通过 LinqToSql 从我的数据库中获取 Gigs...

所以,当 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() 方法替换为在 代码 中(而不是通过 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;
        }
    }




}

}

【问题讨论】:

  • 我遇到了同样的错误,在我的情况下,它似乎与在我的 WHERE 子句中使用相关子查询有关。如果我找到任何信息,我会给你一个更新。

标签: linq linq-to-sql


【解决方案1】:

我认为问题在于 GetGigs 中的“让”语句。使用“让”意味着您将最终查询的一部分与要获取的主集分开定义。问题是'let',如果它不是一个标量,会导致一个嵌套查询。嵌套查询并不是真正的 Linq to sql 的强项,因为它们也被延迟执行。在您的查询中,您将嵌套查询的结果放入要返回的主集的投影中,然后进一步附加 linq 运算符。

当这种情况发生时,嵌套查询会更深地隐藏在将要执行的查询中,这会导致嵌套查询不在要执行的查询的外部投影中,因此必须合并到SQL 查询运行到数据库上。这是不可行的,因为它是嵌套在主 sql 查询内的投影中的嵌套查询,并且 SQL 没有像“投影中的嵌套查询”这样的概念,因为您无法在投影中获取一组元素SQL,只有标量。

【讨论】:

  • 那么如何在不使用 let 的情况下加载行为集合?​​
  • 您可以按照您的方式使用它,但是您不能将查询运算符附加到该查询:嵌套查询必须位于外部投影上。因此,GetGigs 本身的查询将与嵌套查询一起运行。
【解决方案2】:

我有同样的问题,似乎对我有用的是分离出一个返回 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 
               }

           };
}

请注意,虽然这应该可以纠正手头的问题,但似乎还有另一个问题是在投影的每个元素中都在访问延迟的行为查询,我猜这会导致每次向数据库发出单独的查询外投影中的行。

【讨论】:

  • 非常感谢,遇到了同样的问题,把它放在变量中确实可以解决问题。我同意,这确实感觉像是 Linq to SQL 中的一个错误。
  • 我在调试时在我的扩展方法中设置了断点,它们甚至没有被命中。为我的查询的扩展方法部分创建变量后,一切正常,断点被命中。
  • 我刚刚遇到了这个问题,并通过谷歌找到了我自己提出的解决方案,这再次解决了我遇到的问题。我希望我可以说这是我第一次在 StackOverflow 上这样做。
  • 如何推迟调用?您写道:var acts = GetActs(g.ID); // Don't worry this call is deferred - g 的范围仅在 LINQ 查询内。所以参数g.ID 无法解析,应该会导致编译错误。我认为这个答案中缺少一些东西。
  • GetActs 的调用并没有真正推迟,但假设它返回 IQueryable 结果,则假定运行底层查询被推迟到获取底层数据(例如 @987654326 @)。已经有一段时间了,但我相信我是在指出,即使方法调用是在具有理解 LINQ 语法的 return 语句之前的一个单独的命令步骤,查询也没有解决。本质上,它应该达到与原始代码相同的结果,但没有我所指的 Linq2Sql 怪癖。
【解决方案3】:

我在你的类中没有看到任何东西来表明 LINQ to SQL 是如何计算出哪一列是哪一列等等。

您是否希望 WithArtist 方法在 .NET 中执行,或转换为 SQL?如果您希望将其转换为 SQL,则需要使用适当的 LINQ to SQL 属性装饰您的 Gig 类(或以其他方式配置您的数据上下文)。如果您希望它在代码中执行,只需将第一个参数类型从IQueryable&lt;Gig&gt; 更改为IEnumerable&lt;Gig&gt;

【讨论】:

  • 有自动生成的 LinqToSQl gig 类。上面定义的 gig 类是模式驱动的对象,我正在将其适应 LinqToSQl 数据...
  • 那么,您从 GetGigs() 返回的 Gig 类型是不是 LINQ to SQL 知道的?
  • @Jon :我知道你很忙(这个问题现在已经很老了),但是你有没有机会用一些代码来详细说明你的答案..重点是 要转换成SQL,角度。我在这里遇到了完全相同的问题。我正在使用存储库模式(例如 GetActs())+ 管道和过滤器(例如 With Artist())并且我的存储库不处理其他 children 属性(例如 Gigs 是父级, Acts 属性是子属性),因此我试图操纵查询(例如 WithActs())以动态返回关联的子对象,如果消费者为此编写代码的话。这个问题要了我的命:(
  • @Pure Krome:我建议您作为一个单独的问题提出。写出正确的答案会更容易,而且可能会有细微的差别。它也会引起更多人的关注:)
【解决方案4】:

我发现如果将 IQueryable(或表)变量 Gigs 转换为这样的列表,可以解决这样的问题(我最近也遇到过)

return from g in DBContext.Gigs.ToList()
...

如果这仍然不起作用,请对所有 IQueryables 执行相同的操作。在我看来,背后的原因是某些查询太复杂而无法翻译成 SQL。但是如果你将它“具体化”成一个列表,你就可以进行各种查询。

请注意,您应该尽早添加“过滤器”(where 条件),因为过多的内存消耗可能会成为问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-13
    • 1970-01-01
    • 1970-01-01
    • 2011-05-03
    • 2014-06-26
    • 1970-01-01
    • 2016-05-27
    相关资源
    最近更新 更多