使用 Linq 对字符串集合进行 NHibernate 查询会导致错误或空集合

本文关键字:集合 错误 空集 查询 NHibernate Linq 字符串 使用 | 更新日期: 2023-09-27 17:57:07

我在选择字符串集合时遇到问题,并使用以下小示例重现了它。

给定以下 SQL:

CREATE TABLE [Post] (
    [Id]    INT     IDENTITY NOT NULL,
    [Name]  NVARCHAR(20) NOT NULL,
    CONSTRAINT [PK_Post] PRIMARY KEY CLUSTERED ([Id])
)
CREATE TABLE [Category] (
    [Id]    INT     IDENTITY NOT NULL,
    [PostId]    INT NOT NULL,
    [Name]  NVARCHAR(20) NOT NULL,
    CONSTRAINT [FK_Category_Post] FOREIGN KEY ([PostId]) REFERENCES [Post]([Id])
)
INSERT INTO [Post] ([Name]) VALUES ('Post 1')
INSERT INTO [Category] ([PostId], [Name]) VALUES (1, 'Alpha')

还有代码(我用了LINQPad):

void Main()
{
    using (var sessionFactory = Fluently.Configure()
        .Database(MsSqlConfiguration.MsSql2008.Dialect<MsSql2012Dialect>().ConnectionString(@"Data Source=(localdb)'Projects;Initial Catalog=NhTest;"))
        .Mappings(x => {
            x.FluentMappings.Add(typeof(PostMap));
        })
        .BuildSessionFactory())
    using (var session = sessionFactory.OpenSession())
    {
        var post = session.Get<Post>(1);
        Debug.Assert(post.Categories.First() == "Alpha");
        try {
            var second = session.Query<Post>()
                .Where(x => x.Id == 1)
                .Select(x => new {
                    x.Categories,
                    x.Name,
                })
                .Single();
        }
        catch (Exception ex) {
            Debug.Fail(ex.ToString());
        }
        var third = session.Query<Post>()
            .Where(x => x.Id == 1)
            .Select(x => new {
                x.Categories,
                x.Name,
            })
            .ToList().First();
        Debug.Assert(third.Categories.Count() == 1, "Category count was " + third.Categories.Count());
    }
}
// Define other methods and classes here
class Post
{
    public virtual int Id { get; protected set; }
    public virtual string Name { get; protected set; }
    public virtual IList<string> Categories { get; protected set; }
}
class PostMap : ClassMap<Post>
{
    public PostMap()
    {
        Id(x => x.Id);
        Map(x => x.Name);
        HasMany(x => x.Categories)
            .Table("Category")
            .Element("Name")
            .KeyColumn("PostId");
    }
}

第一个断言通过了,在我看来,这验证了我将类别映射到帖子。

try 块中的查询引发异常

'System.Linq.EnumerableQuery`1[System.Collections.Generic.IList`1[System.String]]' cannot be converted to type 'System.Linq.IQueryable`1[System.Object[]]

所以我把它改成了你在代码中看到的第三次尝试,调用.ToList().First()。此查询不会引发任何异常,但类别列表返回为空。

我做错了什么吗?有没有更好的映射技术可以在这里使用?或者是否有解决方法可以使我的查询正常工作?

使用 Linq 对字符串集合进行 NHibernate 查询会导致错误或空集合

这是NHibernate中的一个错误。它将尝试获取可枚举类型参数的类映射数据。当投影映射类的枚举时,这可以完美地工作。但是,在此实例中,枚举类型参数为字符串。字符串没有类映射,因此它在查询中替换了一个空常量,然后导致了以后的问题。

为了进一步说明这一点,此示例的 SQL 查询为:

SELECT c.Name, p.Id, p.Name FROM Post p LEFT JOIN Category c ON p.Id = c.PostId WHERE p.Id = 1

这将返回如下数据:

 c.Name | p.Id   | p.Name 
--------------------------
 Cat 1  | 1      | Post A
 Cat 2  | 1      | Post A

然后,NHibernate将在内存中按操作(包括空检查)执行分组,以为每个唯一值创建类别列表 p.Id。分组依据操作是使用列索引执行的。

这是应该发生的事情。该错误导致在按操作分组之前将结果转换为:

 NULL   | Cat 1  | 1      | Post A
 NULL   | Cat 2  | 1      | Post A

然后,NHibernate过滤了第0列为空的结果,这意味着它们都不会存活。

包含修复程序的拉取请求如下所示:https://github.com/nhibernate/nhibernate-core/pull/262

您可以创建CategoryMapCategory实体并更改为:

public virtual IList<Category> Categories { get; protected set; }

我假设是 linq 到 nhibernate 查询限制。

尝试更改

var second = session.Query<Post>()
    .Where(x => x.Id == 1)
    .Select(x => new {
        x.Categories,
        x.Name,
    })
    .Single();

ToList().Single()而不是Single().

我以前见过这个问题,问题是查询将返回多行,然后强制转换为单个匿名类型。查询失败,因为中间结果包含多行而不是单行。