KB Article K18063
Visible to All Users

How to create a criterion, which returns an intersection of two many-to-many sets

Description:
There are three persistent classes: Client, Region and News. Clients-Regions and News-Regions are many-to-many associations between these objects. That is, both Client and News have a Regions collection. I need to select News for a given Client. That is, the intersection of Client.Regions and News.Regions sets is not empty. For example:
Client: John Doe
Client Regions: Nevada, Arizona.
News #1: Celebration in Grand Canyon
News Regions: Arizona, Colorado
News #2: Earthquake in California
News Regions: California, Oregon

That is, news #1 must be selected for John Doe.
Here is the implementation of my classes:

C#
using DevExpress.Xpo; public class News : XPObject { public string Title; [Association("News-Regions")] public XPCollection<Region> Regions { get { return GetCollection<Region>("Regions"); } } } public class Region : XPObject { public string Name; [Association("Clients-Regions")] public XPCollection<Client> Clients { get { return GetCollection<Client>("Clients"); } } [Association("News-Regions")] public XPCollection<News> News { get { return GetCollection<News>("News"); } } } public class Client : XPObject { public string FullName; [Association("Clients-Regions")] public XPCollection<Region> Regions { get { return GetCollection<Region>("Regions"); } } }
Visual Basic
Imports DevExpress.Xpo Public Class News Inherits XPObject Public Title As String <Association("News-Regions")> _ Public ReadOnly Property Regions() As XPCollection(Of Region) Get Return GetCollection(Of Region)("Regions") End Get End Property End Class Public Class Region Inherits XPObject Public Name As String <Association("Clients-Regions")> _ Public ReadOnly Property Clients() As XPCollection(Of Client) Get Return GetCollection(Of Client)("Clients") End Get End Property <Association("News-Regions")> _ Public ReadOnly Property News() As XPCollection(Of News) Get Return GetCollection(Of News)("News") End Get End Property End Class Public Class Client Inherits XPObject Public FullName As String <Association("Clients-Regions")> _ Public ReadOnly Property Regions() As XPCollection(Of Region) Get Return GetCollection(Of Region)("Regions") End Get End Property End Class


Answer:
It's possible to create such a filter via two nested Contains filters. Since you need to get News, create an XPCollection with the News element type (XPCollection<News>). The News class has a Regions collection, which contains objects of type Region, which in turn has a Clients collection with Client type objects. As such, the filter string will look like this:

C#
CriteriaOperator filter = CriteriaOperator.Parse("Regions[Clients[?]]", client);
Visual Basic
Dim filter As CriteriaOperator = CriteriaOperator.Parse("Regions[Clients[?]]", client)

The above expression can be also represented by two nested ContainsOperator objects:

C#
CriteriaOperator filter = new ContainsOperator("Regions", new ContainsOperator("Clients", new BinaryOperator("This", client)));
Visual Basic
Dim filter As CriteriaOperator = New ContainsOperator("Regions", New ContainsOperator("Clients", New BinaryOperator("This", client)))

Here is the same filter written in Simplified Criteria Syntax:

C#
using DevExpress.Xpo; using DevExpress.Data.Filtering; using(UnitOfWork uow = new UnitOfWork()) { Client client = uow.FindObject<Client>(new OperandProperty("FullName") == "John Doe"); CriteriaOperator filter = new OperandProperty("Regions")[new OperandProperty("Clients")[new OperandProperty("This") == new OperandValue(client)]]; XPCollection<News> newsForJohnDoe = new XPCollection<News>(uow, filter); ... }
Visual Basic
Imports DevExpress.Xpo Imports DevExpress.Data.Filtering Using uow As UnitOfWork = New UnitOfWork() Dim client As Client = uow.FindObject(Of Client)(New OperandProperty("FullName") = "John Doe") Dim filter As CriteriaOperator = New OperandProperty("Regions").Item(New OperandProperty("Clients").Item(New OperandProperty("This") = New OperandValue(client))) Dim newsForJohnDoe As XPCollection(Of News) = New XPCollection(Of News)(uow, filter) ... End Using

Although filter expressions written in Simplified Criteria Syntax are usually longer and appear more difficult to understand than the same filter written as a string for the parser, we still recommend that you prefer Simplified Criteria Syntax to CriteriaOperator.Parse, because the former is checked at a compilation time, while the latter is validated (parsed) only at runtime.
There is a test project attached to this article. You'll find the download link below, in the Attachment section.
See Also:
Build Criteria - Cheat Sheet
Simplified Criteria Syntax
Criteria Operators
How to: Build Complex Criteria
How to filter a collection by an associated objects' properties
What criteria should I use to get objects for a collection property which is empty?
How to use the ContainsOperator for objects in a many-to-many relationship

Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.