我在选择字符串集时遇到问题,并使用以下小例子进行复制。
给出以下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[]]
因此,我已将其更改为您在代码中看到的第3次尝试,并调用.ToList().First()
。此查询不会抛出任何异常,但类别列表将返回空。
我做错了吗?有没有更好的映射技术可以在这里使用?或者有解决方法让我的查询有效吗?
答案 0 :(得分:5)
这是NHibernate中的一个错误。它会尝试获取Enumerable类型参数的类映射数据。在投影映射类的Enumerable时,这非常有效。但是,在这种情况下,Enumerable类型参数是字符串。 String没有类映射,所以它在查询中替换了一个null常量,然后导致问题。
为了进一步说明这一点,此示例的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
答案 1 :(得分:1)
您可以创建CategoryMap
,Category
实体并更改为:
public virtual IList<Category> Categories { get; protected set; }
我认为这是对nhibernate查询限制的一种限制。
答案 2 :(得分:1)
尝试更改
var second = session.Query<Post>()
.Where(x => x.Id == 1)
.Select(x => new {
x.Categories,
x.Name,
})
.Single();
ToList().Single()
代替Single()
。
之前我见过这个问题,问题是查询会返回多行,然后强制转换为单个匿名类型。查询失败,因为中间结果包含多行而不是单行。