Search code examples
c#nhibernatequeryover

NHibernate QueryOver and string.format


I am working with QueryOver in NHibernate and I want to customize one property of my projected DTO using the following syntax:

IEnumerable<PersonResponseMessage> persons =
    session.QueryOver<PersonEntity>()
        .SelectList(list => list
            .Select(p => p.Active).WithAlias(() => dto.Active)
            .Select(p => p.Alert).WithAlias(() => dto.Alert)
            .Select(p => p.Comments).WithAlias(() => dto.Comments)
            .Select(p => string.Format("{0}api/Person/{1}", uriHelper.Root, p.Id)).WithAlias(() => dto.DetailsUrl)
        )
        .TransformUsing(Transformers.AliasToBean<PersonResponseMessage>())
        .List<PersonResponseMessage>();

Unfortunately NHibernate cannot do this and throws an exception saying that:

Variable P referenced from scope "" is not defined


Solution

  • There are in common two ways. Partially we can move that concat operation on the DB side, as documented here:

    In this case, we'll use the Projections.Concat:

    .SelectList(list => list
        .Select(p => p.Active).WithAlias(() => dto.Active)
        .Select(p => p.Alert).WithAlias(() => dto.Alert)
        .Select(p => p.Comments).WithAlias(() => dto.Comments)
    
        // instead of this
        //.Select(p => string.Format("{0}api/Person/{1}", uriHelper.Root, p.Id))
        //       .WithAlias(() => dto.DetailsUrl)
    
        // use this
        .Select(p => Projections.Concat(uriHelper.Root, Projections.Concat, p.Id))
               .WithAlias(() => dto.DetailsUrl)
        )
        .TransformUsing(Transformers.AliasToBean<PersonResponseMessage>())
        .List<PersonResponseMessage>();
    

    But I would vote for ex-post processing on the Application tier, in C#:

    .SelectList(list => list
        .Select(p => p.Active).WithAlias(() => dto.Active)
        .Select(p => p.Alert).WithAlias(() => dto.Alert)
        .Select(p => p.Comments).WithAlias(() => dto.Comments)
    
        // just the ID
        .Select(p => p.Id).WithAlias(() => dto.Id)
        )
        .TransformUsing(Transformers.AliasToBean<PersonResponseMessage>())
        .List<PersonResponseMessage>()
        // do the concat here, once the data are transformed and in memory
        .Select(result => 
        {
            result.DetailsUrl = string.Format("{0}api/Person/{1}", uriHelper.Root, p.Id)
            return result;
        });