2011-03-05 15 views
7

Ho un'applicazione che utilizza NHibernate come ORM. Ho una classe persistente:NHibernate - Restituire oggetto complesso dalla funzione sql

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; } 
} 

e ho una funzione SQL sul lato server:

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 
) 

Voglio essere in grado di chiamare questa funzione e mappare il risultato da esso nella seguente classe

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

È possibile farlo con NHibernate 3.0? È possibile farlo con FluentNHibernate?
Grazie in anticipo!

AGGIORNATO
ho map classe Partita nella tabella tMatchesTest.
Struttura della tabella 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
La soluzione che ho trovato per conto mio:
1. Creare query denominata come questo

<?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> 

ed eseguire la query come questa :

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

Questo è il modo più semplice e semplice che ho trovato finora per eseguire l'operazione.

+0

Potresti postare la struttura della tabella tMatchesTest? –

+0

Ho aggiunto la struttura di questa tabella nel post originale. – StuffHappens

+0

è il codice di esempio della funzione SQL? Il suo contenuto sarà più complesso? Perché fare affidamento sulla funzione per il quering effettivo? – Jaguar

risposta

1

IResultTransformer viene utilizzato per trasformare i risultati della query in tipi visibili dell'applicazione.

Inoltre, il mapping della chiamata di funzione SQL come named SQL query darà un codice più pulito.

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

Poiché abbiamo un risultato più tavoli, AliasToBeanResultTransformer non è direttamente utilizzabile. Invece lo sottoclasseremo e convertiremo il risultato nel tipo desiderato.

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 
       } 
      } 
     } 
    } 
} 
0

Non sono sicuro se questo funzionerà per te, ma puoi sempre utilizzare il metodo di addentity per impostare il tipo di ritorno. la query sarà simile segue:

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

spera che possiate ottenere la query di selezione da soli .. basta vedere come NHibernate costruisce la query sparando una query e verificando la sql utilizzando NHibernate Profiler.

Spero che questo aiuti

+0

Quando provo a farlo, ottengo la seguente eccezione 'No persister per: ConsoleApplication8.Domain.Entities.FResult' – StuffHappens

+0

sì, perché AddEntity funziona solo con le classi mappate – Jaguar

+0

Non so se avesse un'entità non mappata – Baz1nga

0

Ebbene l'unico modo che posso pensare che potrebbe funzionare è questo:

mappa la funzione di una visione, consente di denominarlo FResult che avrà solo 3 colonne:

ora è possibile mappare un'entità su quella vista e quindi associarla a un'entità con un ID composito [many-to-one{mt1Id}, many-to-one{mt2Id}] e disporre di tutti i campanelli e fischietti di HQL e dei criteri o se non si è interessati a tutto ciò e al propriovogliono solo una funzione, questo dovrebbe funzionare, ma tenere a mente che si vuole ancora solo in uscita l'Ids delle entità e dei valori dei risultati dalla funzione:

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(); 

notare che io sono 'froming' la funzione direttamente, ma i ha scritto la query mentre pensava a una vista quindi probabilmente dovrai modificare sql per una funzione

Ora, il risultato è un array di oggetti [] dove oggetto [0] e oggetto [1] sono idratati oggetti Match e oggetto [2] è il valore sc

A questo punto, se è assolutamente necessario, è possibile costruire un FResult oggetto e applicare i risultati poiché la classe FResult non è una classe completamente mappata. NHibernate è limitato in quanto può fare con esso (per fare in modo che emetta direttamente uno IList<FResult>).

1

userei istanziazione dinamica, in questo modo:

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 
) 

quindi scrivere i ctor sulla classe di report:

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; } 
} 

E per eseguire la query ...

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

Una possibile soluzione è quella di creare una vista invece di una funzione (o uno che chiama la funzione, se è richiesta una funzione):

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 

Allo scopo, è possibile mappare in NHibernate esattamente come faresti per mappare un tavolo. SQL Server si lamenterà se NHibernate tenta di eseguire il commit degli aggiornamenti agli oggetti, ovviamente, a meno che non si desideri creare trigger INSTEAD OF.

+0

Cosa succede se ho bisogno di un funzione che prende un parametro. Questa soluzione non funzionerà. Grazie per la tua risposta! – StuffHappens

Problemi correlati