문제

I have the following objects:

public abstract class Event {
    public virtual Guid Id { get; set; }
    public virtual string Description { get; set; }
}

public class TeamEvent : Event {
    public virtual Guid Id { get; set; }
    public virtual Team Team { get; set; }
}

public class PersonEvent : Event {
    public virtual Guid Id { get; set; }
    public virtual Person  Person { get; set; }
}

public class Team {
    public virtual Guid Id { get; set; }
    public virtual string Name { get; set; }
}

public class Person {
    public virtual Guid Id { get; set; }
    public virtual string Forename { get; set; }
    public virtual string Surname { get; set; }
}

And Mappings:

    <discriminator column="Discriminator"
            not-null="true"
            type="System.String"/>

    <subclass
        name="TeamEvent"
        discriminator-value="TeamEvent">
        <many-to-one name="Team" column="TeamId"/>
    </subclass>

    <subclass
        name="PersonalEvent"
        discriminator-value="PersonalEvent">
        <many-to-one name="Person" column="PersonId"/>
    </subclass>
</class>
<class name="Person" table="Persons">
    <id name="Id">
        <generator class="assigned"/>
    </id>
    <property name="Forename"/>
    <property name="Surname"/>
</class>
<class name="Team" table="Teams">
    <id name="Id">
        <generator class="assigned"/>
    </id>
    <property name="TeamName"/>
</class>

What I'd like to do is a query that would get all events regardless of base type in one query to populate a DTO like:

public class EventDTO {
   public virtual Guid EventId { get; set; }
   public virtual string EventDescription { get; set; }
   public virtual Guid PersonId { get; set; }
   public virtual string PersonForename { get; set; }
   public virtual string PersonSurname { get; set; }
   public virtual Guid TeamId { get; set; }
   public virtual string TeamName { get; set; }
}

Where if it's a person event, the team fields are null and vice versa.

In SQL I'd just do something like:

Select * from Events
left join Team on Events.TeamId = Team.Id
left join Person on Events.Person = Person.Id

I can't do this in QueryOver as the Events c# object doesn't have properties for Team/Person. I can do it like so:

var events = session.QueryOver<Event>
               .Where ( /* insert my restrictions */ )
               .Future<Event>();

var teamEvents = session.QueryOver<TeamEvent>
               .Where ( /* insert my restrictions */ )
               .Future<TeamEvent>();
var personEvents = session.QueryOver<PersonEvent>
               .Where ( /* insert my restrictions */ )
               .Future<PersonEvent>();

/* union the results in memory */

The amount of data coming back from these queries should be such that it's feasible to just union the results in memory. My question is really is there a more elegant way of doing this? without doing three separate queries.

I should point out this is just a quick example I've typed up to highlight my question so there may be some typos, etc in the above code samples. In the real world problem there is other parts of the system depending on the mappings, so changing the schema/mappings/objects is not an option.

And whilst I'd prefer to use query over, I don't mind using criteria or hql if it's possible to do it in a simpler way there. Also this query will only return a max of 20 rows, of entities that are frequently accessed and thus stored in second level cache, so even an N + 1 isn't a major issue if it makes the code simple.

I should also point out the reason the three queries one isn't so simple is because there needs to be 10 restrictions that need to be duplicated per query, and there are a couple of other subclasses of event.

Thanks for any help.

도움이 되었습니까?

해결책

When I hit problems like this I stop fighting NH and create a DB view. that I map as mutable=false. This way I don't get in a mess with paging or with my where clauses and merging multiple sets on client.

Not sure if there is a nicer way (especially as you have related tables that you want to flatten) than what you have come up with. As you have said that you don't have much data then merging on the client might be best.

다른 팁

You should be able to make it to work with HQL and writing a condition like :

from Event where (..person event restrictions..) OR (..team event restrictions..)

since querying over abstract base class will force nHibernate to do the left joins and even though you specify derived properties they are accessible. So in simplified form , following query

var personEvents = session
                    .CreateQuery("from Event where Person!=null")
                    .List<Event>();

will give you PersonEvent entities.

So you should be able to construct simpler code BUT it really feels strange that you want to do load different subclasses in one query and process them at once etc. Future queries seem elegenant enough as they are focused on small (single) piece of responsibility - what would you do if there is a need for another event ? It feels better to add another query rather than modify one master query again and again.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top