Many to Many data relations

Topics: General, Standard packages, Troubleshooting
May 2, 2011 at 3:25 AM

What's the best way to represent many to many data relations in Composite Data?

To use a classic M:N example, Classrooms can have many Students and Students can attend many classes. Is this possible to do easily in Composite? Ideally, an end user should be able to select the data from a list of checkboxes. They shouldn't have to deal with the Intermediate entity as they would be clicking on items that add IEnumerable<DataReference<ClassRoom>> to the Student Data Object.

How is this (common) data structure possible in C1?

Coordinator
May 2, 2011 at 12:23 PM

We do have some work in this area before we are perfect - the auto generated Forms UI (the add/edit UI you get for free when defining a new data type) have no true N:M features, so you need to define UI at a lower level which require a lot more work. There are some pseudo N:M features you could consider, see below.

Once you are writing UI code that is dependent on some data types (data types in Composite C1 are CLR types, interfaces) you probably would like to have those data type defined in C#, rather than having the types auto generated by Composite C1 at runtime.

I won’t dive into details here, but leave with a few relevant links.

Defining data types using code: http://docs.composite.net/C1/Data/Defining-Data-IData-types-in-C1-using-C.aspx

Building custom applications: http://docs.composite.net/C1/Console/Tree-definitions-intro.aspx and http://docs.composite.net/C1/Console/FormWorkflows.aspx

Pesudo N:M

The may be situations where “many to many” can be implemented using a simple string field, populating it with a comma separated list of keys. There are visual tooling available for this, making this the easy many-to-many implementation. This may work well in many situations, perhaps with somne custom caching / wrapping.

Composite C1 ships with two widgets for string fields, that let users select multiple elements and then store the selection in a single string field: Composite.Widgets.String.DataIdMultiSelector and Composite.Widgets.String.Selector (with param Multiple=True).

Also, if you use the build in Get(Data)Xml functions, and build up predicates for filtering data you will find functions like Composite.Utils.Predicates.GuidInCommaSeparatedList and Composite.Utils.Predicates.StringInCommaSeparatedList which will search a string field containing comma separated values for a specific string or GUID.

There isn’t much computer science glory in this approach, but as the features are in Composite C1 is right now, this can save you a whole lot of code.

May 14, 2012 at 10:41 PM

I'm trying to figure out how best to apply this.

I have a DataType called GlobalArticle (it's a page metatype) that has a field

  • Data type named Continent.
  • Unlimited Length String
  • A Widget Composite.Widget.String.Selector 
    • GUIDs from Data.Continents.GetContinentsXml
    • Multiple selection on
    • String field populated with GUIDs

I have a function GetGlobalArticlesByContinent which should return a filtered list of GlobalArticle data items based on a Continent parameter.

I have added a FieldPredicatesFilter on the Continent field.

However, There is no GUIDinCommaSeparatedList. The only Composite.Utils.Predicates Function I can find is the StringInCommaSeparatedList.

What am I doing wrong? When I try to use the StringInCommaSeparatedList i get an error: 

System.InvalidOperationException: Failed to execute function with local name 'GetGlobalArticleXml' ---> System.InvalidOperationException: Failed to get value for function 'Data.GlobalArticle.GetGlobalArticleXml' ---> System.InvalidOperationException: Failed to get value for function 'Data.GlobalArticle.FieldPredicatesFilter' ---> System.InvalidOperationException: When called from 'VisitLambda', rewriting a node of type 'System.Linq.Expressions.ParameterExpression' must return a non-null value of the same type. Alternatively, override 'VisitLambda' and change it to not visit children of this type.
   at System.Linq.Expressions.ExpressionVisitor.VisitAndConvert[T](ReadOnlyCollection`1 nodes, String callerName)