views:

62

answers:

1

I'm reading the documentation about DetachedCriteria. The documentation clearly shows that setting an alias for your projection is optional. However, whenever I omit the alias my model properties contain no data. Here are my two test models.

[ActiveRecord("INCIDENT")]
public class Incident : ActiveRecordBase<Incident>
{
    [PrimaryKey(PrimaryKeyType.Native, "INCIDENT_ID", ColumnType = "Int32")]
    public virtual int IncidentId { get; set; }

    [Property("CREATION_DATETIME", ColumnType = "DateTime")]
    public virtual DateTime? CreationDatetime { get; set; }

    [BelongsTo("CAUSE_CD")]
    public virtual Cause Cause { get; set; }
}

[ActiveRecord("CAUSE")]
public class Cause : ActiveRecordBase<Cause>
{
    [PrimaryKey(PrimaryKeyType.Native, "CAUSE_CD", ColumnType = "String")]
    public virtual string CauseCd { get; set; }

    [Property("CAUSE_DESC", ColumnType = "String", NotNull = true)]
    public virtual string CauseDesc { get; set; }
}

Here is what I use to query the database.

DetachedCriteria incidentCriteria = DetachedCriteria.For<Incident>("i")
.SetProjection(Projections.ProjectionList()
   .Add(Projections.Property("i.IncidentId"))
   .Add(Projections.Property("i.CreationDatetime"))
)
.SetResultTransformer(Transformers.AliasToBean<Incident>());
IList<Incident> incidents = Incident.FindAll(incidentCriteria);

Both projections properties do not get populated unless I set an alias. So my question is, why is the alias optional? I'm sure I'm simply missing something else. But if I put a random alias (e.g. abc) it'll return an error saying that it could not find property "abc" in the Incident class. Fair enough, I add the appropriate alias to match my property names. And voila! My properties are now property being populated.

Now comes the issue of when I want to query a lookup table. I add

.Add(Projections.Property("c.CauseDesc"), "CauseDesc")

to my ProjectionList and append

.CreateCriteria("i.Cause", "c")

But now it complains that it can't find "CauseDesc" from my Incident model.

What am I missing from this whole criteria ordeal?

Update: The following code

IList<Incident> results = sess.CreateCriteria<Incident>("i")
    .SetProjection(Projections.ProjectionList()
        .Add(Projections.Property("i.IncidentId"), "IncidentId")
        .Add(Projections.Property("i.CreationDatetime"), "CreationDatetime")
        .Add(Projections.Property("c.CauseDesc"), "CauseDesc")
    )
    .Add(Expression.Gt("i.IncidentId", 1234567))
    .CreateAlias("Cause", "c")
    .List<Incident>();

This does create a valid query (I checked it with a profiler) but it seems to be having issues populating my generic list. It gives me error "The value \"System.Object[]\" is not of type \"oms_dal.Models.Incident\" and cannot be used in this generic collection.\r\nParameter name: value". However, all works fine if I don't use a projection but then it selects 50+ fields which I don't want. Does that mean I'm forced to use a DTO in this circumstance?

+3  A: 

You need to specify the projection property name like...

.Add(Projections.Property("i.IncidentId"), "IncidentId")

also, in general you do not project into the same domain object. You should create an incident dto like...

public class IncidentDTO  
{
    public int IncidentID { get; set; }
    public DateTime CreationDatetime { get; set; } 
}

and then...

.SetProjection(Projections.ProjectionList()
   .Add(Projections.Property("i.IncidentId"), "IncidentId")
   .Add(Projections.Property("i.CreationDatetime"), "CreationDatetime")
)
.SetResultTransformer(Transformers.AliasToBean<IncidentDTO>());

If you want Incidents matching some criteria (not a DTO), then don't set projections/resulttransformer. Instead you simply do something like this...

IList<Incident> incidents = session.CreateCriteria<Incident>()
    .CreateAlias("Cause", "c")  //now you can access Cause properties via `c.`
    .Add(Restrictions.Eq("c.CauseDesc", "some cause")) 
    .List<Incident>();

See how the root criteria object doesn't need an alias. If it helps, I only use CreateCriteria for the initial object. If I need to reference child objects, I use CreateAlias.

dotjoe
About your last example, I keep getting error "The value \"System.Object[]\" is not of type \"oms_dal.Models.Incident\" and cannot be used in this generic collection.\r\nParameter name: value" when I set a projection. Please look at my updated answer.
Mike
Yes, if you set the projection you must use a DTO. Projections are NOT used to control the loading of properties in a domain object. They are used to project data into a DTO for display purposes.
dotjoe
Thank you! Waayy clearer to me now. :)
Mike