2011-03-05 17 views
7

Tengo una aplicación que utiliza NHibernate como ORM. Tengo una clase persistente:NHibernate: devolver objeto complejo de la función 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; } 
} 

y tengo una función de SQL en el servidor:

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 
) 

Quiero ser capaz de llamar a esta función y asignar el resultado de la misma en la siguiente clase

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

¿Es posible hacerlo con NHibernate 3.0? ¿Es posible hacerlo con FluentNHibernate?
¡Gracias de antemano!

ACTUALIZADO
I Mapa clase de ajuste en la tabla tMatchesTest.
Estructura de la tabla tMatchesTest es:

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 solución que encontré en mi propia:
1. Crear consulta con nombre como esto

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

y ejecutar la consulta como esta :

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

Esta es la forma más corta y simple que he encontrado hasta ahora para realizar la tarea.

+0

¿Podría publicar la estructura de la tabla tMatchesTest? –

+0

Agregué la estructura de esta tabla en la publicación original. – StuffHappens

+0

es el código de ejemplo de la función SQL? ¿Su contenido va a ser más complejo? ¿Por qué confiar en la función para la búsqueda real? – Jaguar

Respuesta

1

IResultTransformer se utiliza para transformar los resultados de la consulta en tipos visibles para la aplicación.

Además, mapear la llamada a la función SQL como named SQL query dará un código más limpio.

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

Ya que tenemos un resultado de varias mesas, AliasToBeanResultTransformer no es directamente utilizable. En su lugar, lo subclasificaremos y convertiremos el resultado al tipo deseado.

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

No estoy seguro de si esto funcionará para usted, pero siempre puede utilizar el método de ajuste para establecer el tipo de devolución. la consulta se verá como sigue:

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

espera que usted pueda obtener la consulta de selección a sí mismo .. acaba de ver cómo nhibernate construye la consulta por el disparo de dicha consulta y comprobación del sql usando NHibernate Profiler.

espero que esto ayude

+0

Cuando intento hacerlo, aparece la siguiente excepción 'No persistir para: ConsoleApplication8.Domain.Entities.FResult' – StuffHappens

+0

sí porque AddEntity solo funciona con las clases mapeadas – Jaguar

+0

No sé si tenía una entidad no asignada – Baz1nga

0

Bueno, la única manera que se me ocurre que podría funcionar es la siguiente:

Mapa de su función a una vista, deja el nombre que FResult que tendrá sólo 3 columnas:

[mt1Id, mt2Id, sc] 

ahora puedes mapear una entidad en esa vista y luego asignarla a una entidad FResult con una identificación compuesta [many-to-one{mt1Id}, many-to-one{mt2Id}] y tener todas las características de HQL y criterios o si no estás interesado en todo eso y tu sólo quieren una función, esto debería funcionar, pero tenga en cuenta que tendrá todavía sólo de salida los ID de las entidades y los valores de los resultados de la función:

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

en cuenta que estoy 'froming' directamente a la función pero escribió la consulta mientras piensa en una vista por lo que probablemente tendrá que modificar el SQL para una función

Ahora, el resultado es un ArrayList de objeto [], donde objeto [0] y el objeto [1] se hidratan Match objetos y objeto [2] es el valor sc

En este punto, si es absolutamente necesario, puede simplemente construir un FResult objeto y aplicar los resultados ya que la clase FResult no es una clase totalmente asignada NHibernate está limitada en cuanto a que puede hacer con ella (para hacer que muestre un IList<FResult> directamente).

1

me gustaría utilizar instanciación dinámica, así:

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 
) 

luego escribir la ctor en su clase de informes:

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

Y para ejecutar la consulta ...

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

Una posible solución es crear una vista en lugar de una función (o una que llama a la función, si se requiere una función):

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 

Con una vista, puede asignar en NHibernate exactamente como si mapearas una mesa. SQL Server se quejará si NHibernate intenta realizar actualizaciones a los objetos, obviamente, a menos que desee crear activadores INSTEAD OF.

+0

¿Qué sucede si necesito una función que toma un parámetro. Esta solución no funcionará. ¡Gracias por tu respuesta! – StuffHappens

Cuestiones relacionadas