Object A has a collection of object B. In NHibernate using LINQ, how to retrieve a list of A having at least one B [closed]

StackOverflow https://stackoverflow.com/questions/14195896

Frage

Let's take as example these domain objects:

public class A 
{
    public Guid Id { get; set; }
    public ICollection<B> CollectionOfB { get; set; }
}

public class B 
{
    public Guid Id { get; set; }
    public string Name { get; set; }
}

I need to retrieve any A object having a B with these names name1, name2 using NHibernate 3.x.

For example, imagine that you get which B ones want to retrieve from an array of names as string string[] names = new string[] { "name1", "name2" }.

I thought about .Query<A>().Where(someA => some.CollectionOfB.Any(someB => names.Contains(someB.Name))), and I doubt that this would be compiled as an SQL query by the NHibernate LINQ provider. maybe this would be compiled into a not very optimal SQL query.

What would be the optimal way of building this query using NHibernate 3.x LINQ provider?

War es hilfreich?

Lösung

The query you thought about:

var someAs = session.Query<A>().Where(someA => some.CollectionOfB.Any(someB => names.Contains(someB.Name)));

works fine, I tested it on a similar structure. NHibernate issues a correlated subquery containing an IN clause.

[edit] In regards to your comment, it's optimal in the sense that the intent of the query is clear from the code. Performance has to be measured and database engines use a query optimizer that can often translate ugly looking queries into very efficient ones, so, like many things, you can't judge a query on appearance.

I changed my sample query to use an inner join and got a better execution plan (it eliminated a sort operation). If I re-write the query using LINQ query syntax it uses the better execution plan but the query does not contain an inner join; instead it uses the older style join in the where clause.

var someAs = (from someA in session.Query<A>()
              join someB in session.Query<B>() on someA.Id equals someB.A_Id
              where names.Contains(someB.Name)
              select someA).ToArray();

Andere Tipps

if you extend class B to have a reference to A:

public class B 
{
    public A A {get;set;}
    public Guid Id { get; set; }
    public string Name { get; set; }
}

And then do subquery like this:

var aObjectsToRetrieve = Query<B>().Where(x=>x.Name.StartsWith("name1")).Select(x=>x.A.Id);

And then apply Exists() in the query of A to get the A objects you need. If you have an array of names, you could try something like this (or a conjunction depending on your scenario):

  var disjunction = new Disjunction();
  var names = new[] {"name1", "name2"};
  foreach (string name in names)
            disjunction.Add(Restrictions.InsensitiveLike(PropName<User>(x => x.Name), name));
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top