LINQ to SQL从sql server函数“return table as”生成“null”而不是“not null”

LINQ to SQL generate “null” instead of “not null” from sql server function “return table as”

我正在使用SQL Server Express 2012高级服务和Visual Studio 2013更新2。我已经将这个例子简化到核心。我的数据库中有下表:

1
2
3
4
5
6
7
8
9
CREATE TABLE [dbo].[Realty]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
...
...
[RankingBonus] [int] NOT NULL,
[Ranking]  AS ([Id]+[RankingBonus]) PERSISTED NOT NULL,
...
)

在此表上具有全文表功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
CREATE FUNCTION [dbo].[GetFilteredRealtyFulltext]
(@fulltextcriteria nvarchar(4000))
RETURNS TABLE
AS
RETURN (SELECT
realty.Id AS realtyId,
...
realty.Ranking, --THIS IS IMPORTANT FOR THE QUIESTION!
...,
( COALESCE(ftR.Rank,0) + COALESCE(ftObec.Rank,0) + COALESCE(ftOkres.Rank,0) + COALESCE(ftpobvod.Rank,0)) AS FtRank

FROM realty
--these joins and where conditions are not important for this stackoverflow question
--this only shows why I use table function with return table
--it is because this is the only way I found how to generate LINQ to SQL with fulltext as IQueryable<T>
JOIN Category ON realty.CategoryId = Category.Id
LEFT JOIN ruian_cobce ON realty.cobceId = ruian_cobce.cobce_kod
LEFT JOIN ruian_obec ON realty.obecId = ruian_obec.obec_kod
LEFT JOIN okres ON realty.okresId = okres.okres_kod
LEFT JOIN ExternFile ON realty.Id = ExternFile.ForeignId AND ExternFile.IsMain = 1 AND ExternFile.ForeignTable = 5
INNER JOIN Person ON realty.OwnerId = Person.Id
Left JOIN CONTAINSTABLE(Realty, *, @fulltextcriteria) ftR ON realty.Id = ftR.[Key]
Left JOIN CONTAINSTABLE(ruian_obec, *, @fulltextcriteria) ftObec ON realty.obecId = ftObec.[Key]
Left JOIN CONTAINSTABLE(Okres, *, @fulltextcriteria) ftOkres ON realty.okresId = ftOkres.[Key]
Left JOIN CONTAINSTABLE(pobvod, *, @fulltextcriteria) ftpobvod ON realty.pobvodId = ftpobvod.[Key]
WHERE Person.ConfirmStatus = 1
AND ( COALESCE(ftR.Rank,0) + COALESCE(ftObec.Rank,0) + COALESCE(ftOkres.Rank,0) + COALESCE(ftpobvod.Rank,0))  > 0
)
GO

当我将getfilteredrealtyfulltext函数放到dbml中时,设计器生成列排名为nullable int。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_Ranking", DbType="Int")]
public System.Nullable<int> Ranking
{
  get
  {
    return this._Ranking;
  }
  set
  {
    if ((this._Ranking != value))
    {
       this._Ranking = value;
    }
  }
}

我希望它只生成整数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_Ranking", DbType="Int NOT NULL"")]
public int Ranking
{
  get
  {
    return this._Ranking;
  }
  set
  {
    if ((this._Ranking != value))
    {
       this._Ranking = value;
    }
  }
}

表realty在dbml文件中生成正确,排名仅为整数,但表函数生成错误。怎么了?

更新:

将问题与问题的核心联系起来。


问题是,SQL Server认为来自GetFilteredRealtyFulltext函数的Ranking列可以为空,即使基础表列创建为NOT NULL。通过对数据库运行以下查询,可以看到这一点:

1
2
3
4
5
6
7
8
9
10
11
12
select
o.name ObjectName,
c.name ColumnName,
c.is_nullable ColumnIsNullable
from
sys.all_objects o
inner join
sys.all_columns c
on
o.object_id = c.object_id
where
o.[name] = 'GetFilteredRealtyFulltext'

我发现,如果您基于realty表创建视图,SQL Server也会做同样的事情。谷歌搜索并没有找到任何权威性的答案,但在数据库管理员网站上确实导致了类似的问题。这导致了这样的讨论,这让我尝试使用ISNULL。使用下面的示例中的ISNULLhack,在dbml文件中给出了正确的列定义,一旦我从dbml文件中删除了前一个函数并重新添加了它。

强制计算列NotNullable需要Is:

1
2
3
4
5
6
7
8
9
CREATE TABLE [dbo].[Realty]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
...
...
[RankingBonus] [int] NOT NULL,
[Ranking]  AS ISNULL([Id]+[RankingBonus], 0) PERSISTED NOT NULL,
...
)

然后强制函数也不能为空:

1
2
3
4
5
6
7
8
9
10
11
CREATE FUNCTION [dbo].[GetFilteredRealtyFulltext]
(@fulltextcriteria nvarchar(4000))
RETURNS TABLE
AS
RETURN (SELECT
realty.Id AS realtyId,
realty.RankingBonus,
ISNULL(realty.Ranking, 0) as Ranking  --ISNULL hack
FROM realty
)
GO