NHibernate - 从sql函数返回复杂对象

时间:2011-03-05 09:19:35

标签: c# sql-server nhibernate orm fluent-nhibernate

我有一个使用NHibernate作为ORM的应用程序。我有一个持久的类:

public class Match : IEntity
{
    public virtual int ID { get; set; }
    public virtual string Word { get; set; }
    public virtual int WordIntervalBeginning { get; set; }
    public virtual int WordIntervalEnding { get; set; }
}

我在服务器端有一个SQL函数:

CREATE FUNCTION ftMatchTest
( )
RETURNS TABLE 
AS
RETURN 
(
    SELECT mt1.*, mt2.*,
    CASE WHEN mt1.Word = mt2.Word THEN 1 ELSE 0 END AS sc
    FROM
        dbo.tMatchesTest mt1, dbo.tMatchesTest mt2
)

我希望能够调用此函数并将结果映射到以下类

public class FResult
{
    public Match Match1 { get; set; }
    public Match Match2 { get; set; }
    public int sc { get; set; }
}

用NHibernate 3.0可以做到吗?是否可以使用FluentNHibernate进行此操作?
提前谢谢!

已更新
我将Match类映射到tMatchesTest表中 tMatchesTest表的结构是:

CREATE TABLE [dbo].[tMatchesTest](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [Word] [varchar](50) NOT NULL,
    [WordIntervalBeginning] [int] NOT NULL,
    [WordIntervalEnding] [int] NOT NULL,
 CONSTRAINT [PK_tMatchesTest] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

UPDATED2
我自己找到的解决方案:
1.像这样创建命名查询

<?xml version="1.0" encoding="utf-8" ?>

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" 
                   namespace=" ConsoleApplication8.Domain.Entities"
                   assembly="ConsoleApplication8">

  <resultset name="fresult-resset">
    <return alias="Match1" class="Match"/>
    <return alias="Match2" class="Match"/>
    <return-scalar column="sc" type="int"/>
  </resultset>


  <sql-query name="getfresult" resultset-ref="fresult-resset">
    SELECT {Match1.*}, {Match2.*},
    CASE WHEN Match1.Word = Match2.Word THEN 1 ELSE 0 END sc
    FROM dbo.tMatchesTest Match1, dbo.tMatchesTest Match2
  </sql-query>

</hibernate-mapping>

并执行如下查询:

Session.GetNamedQuery("getfresult")
                .SetResultTransformer(new AliasToBeanResultTransformer(typeof(FResult)))
                .List<FResult>();

这是迄今为止我发现执行任务的最简单方法。

5 个答案:

答案 0 :(得分:1)

我能想到的唯一方法就是:

将您的函数映射到视图,我们将其命名为FResult,它只有3列:

[mt1Id, mt2Id, sc]

现在您可以在该视图上映射实体,然后将其映射到具有复合ID FResult的实体[many-to-one{mt1Id}, many-to-one{mt2Id}],并拥有HQL和标准的所有功能,或者如果您是对这一切并不感兴趣而你只是想要一个函数,这应该有效,但请记住,你仍然只是输出实体的ID和函数的结果值:

var result = Session.CreateSqlQuery(
    @"select {m1.*}, {m2.*}, ft.sc 
      from dbo.ftMatchTest ft, Match m1, Match m2 
      where m1.Id = ft.mt1Id and m2.Id = ft.mt2Id")
      .AddEntity("m1",typeof(Match))
      .AddEntity("m2",typeof(Match))
      .AddScalar("sc", NHibernateUtil.BlaBla)
      .List();

请注意,我正在“直接”使用该函数,但我在考虑视图的同时编写了查询,因此您可能需要修改函数的SQL

现在,结果是object []的arraylist,其中object [0]和object [1]是水合Match个对象,而object [2]是sc值

此时,如果它绝对必要,你可以构造一个FResult对象并应用结果,因为FResult类不是完全映射的类NHibernate是有限的,因为它可以做它(使其直接输出IList<FResult>)。

答案 1 :(得分:1)

我会使用动态实例化,如下所示:

CREATE FUNCTION ftMatchTest
( )
RETURNS TABLE 
AS
RETURN 
(
    -- select each column
    SELECT mt1.ID ID1, mt2.ID ID2, <etc>
    CASE WHEN mt1.Word = mt2.Word THEN 1 ELSE 0 END AS sc
    FROM
        dbo.tMatchesTest mt1, dbo.tMatchesTest mt2
)

然后在报告类上编写演员:

public class FResult
{
    public FResult(int id1, int id2, ..., int sc)
    {
        Match1 = new Match(id1, ...);
        Match2 = new Match(id2, ...);
        this.sc = sc;
    }

    public Match Match1 { get; set; }
    public Match Match2 { get; set; }
    public int sc { get; set; }
}

并运行查询...

var result = Session.CreateSqlQuery(
   @"select new FResult(ft.ID1, ft.ID2, ...,  ft.sc) 
   from dbo.ftMatchTest ft")

答案 2 :(得分:1)

IResultTransformer用于将查询结果转换为应用程序可见类型。

此外,将SQL函数调用映射为named SQL query将提供更清晰的代码。

var list = Session.GetNamedQuery("ftMatchTest")
    .SetResultTransformer(new AliasToFResultTransformer())
    .List<FResult>();

由于我们有多表结果,因此AliasToBeanResultTransformer不能直接使用。相反,我们将它子类化并将结果转换为所需的类型。

public class AliasToFResultTransformer : AliasToBeanResultTransformer
{
    public AliasToFResultTransformer() : base(typeof(FMatches)) {}

    object IResultTransformer.TransformTuple(object[] tuple, string[] aliases)
    {
        FMatches fm = base.TransformTuple( tuple, aliases ) as FMatches;

        return fm.ToFResult();
    }

    public class FMatches
    {
        public int sc { get; set; }
        public virtual int Mt1ID { get; set; }
        public virtual string Mt1Word { get; set; }
        public virtual int Mt1WordIntervalBeginning { get; set; }
        public virtual int Mt1WordIntervalEnding { get; set; }
        public virtual int Mt2ID { get; set; }
        public virtual string Mt2Word { get; set; }
        public virtual int Mt2WordIntervalBeginning { get; set; }
        public virtual int Mt2WordIntervalEnding { get; set; }

        public FResult ToFResult()
        {
            return new FResult {
                sc = this.sc,
                Match1 = new Match {
                    Id = this.Mt1Id,
                    Word = this.Mt1Word,
                    WordIntervalBeginning = this.Mt1WordIntervalBeginning,
                    WordIntervalEnding = this.Mt1WordIntervalEnding
                },
                Match2 = new Match {
                    Id = this.Mt2Id,
                    Word = this.Mt2Word,
                    WordIntervalBeginning = this.Mt2WordIntervalBeginning,
                    WordIntervalEnding = this.Mt2WordIntervalEnding
                }
            }
        }
    }
}

答案 3 :(得分:0)

我不确定这是否适合我们,但您可以始终使用addentity方法来设置返回类型。查询将如下所示:

Session.CreateSqlQuery(selectStatement).AddEntity(typeof(FResult)).SetString(variableNameIfAny,value).List<FResult>();

希望你自己可以获得select查询..只需看看nhibernate如何通过触发这样的查询并使用NHibernate PRofiler检查sql来构建查询。

希望这有帮助

答案 4 :(得分:0)

一种可能的解决方案是创建视图而不是函数(或者如果需要函数则调用函数的函数):

CREATE VIEW [MatchTestResults]
AS
SELECT mt1.*, 
       mt2.*,
       CASE WHEN mt1.Word = mt2.Word THEN 1 ELSE 0 END AS sc
FROM
    dbo.tMatchesTest mt1, 
    dbo.tMatchesTest mt2

使用视图,您可以将其映射到NHibernate中,就像映射表格一样。如果NHibernate尝试提交对象的更新,SQL Server会抱怨,除非你想创建INSTEAD OF触发器。