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 BasicImports 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 BasicDim 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 BasicDim 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 BasicImports 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