Default persistence enforce set to true would anyway delete association table's lines - codefluent

The property defaultPersistenceEnforce is set to true at project level. We consider following example.
Meeting ----*> Contact <---- Case
When we have one contact referenced in the table Case, the call of stored procedure Contact_Delete() would fail as exepected.
But when there's no reference in table Case the execution of stored procedure Contact_Delete() would delete the lines inside association table Meeting_Contacts_Contact.
It doesn't make much sense since I would like to prevent such behavior. In particular, I didn't specify any cascade relationship for deletion.
I would like to make sure that tuples are not being erased in such tables, in particular when there are referenced.
How can I have the stored procedure Contact_Delete() to only delete Contact and don't consider references ?
Thanks for your answer,
Enclosed the model part and the definition of Contact_Delete stored procedure.
<cf:project defaultNamespace="WcfServices.Model" xmlns:cf="http://www.softfluent.com/codefluent/2005/1" xmlns:cfx="http://www.softfluent.com/codefluent/modeler/2008/1" xmlns:cfps="http://www.softfluent.com/codefluent/producers.sqlserver/2005/1" xmlns:cfom="http://www.softfluent.com/codefluent/producers.model/2005/1" xmlns:cfsps="http://www.softfluent.com/codefluent/producers.sqlpivotscript/2013/1" defaultPersistenceEnforce="true" createDefaultMethodForms="true" createDefaultApplication="false" createDefaultHints="false">
<cf:import path="Default.Surface.cfp" />
<cf:producer name="SQL Server" typeName="CodeFluent.Producers.SqlServer.SqlServerProducer, CodeFluent.Producers.SqlServer">
<cf:configuration produceViews="true" targetDirectory="..\WcfServices.persistence" cfx:targetProject="..\WcfServices.persistence\WcfServices.persistence.sqlproj" cfx:targetProjectLayout="Update, DontRemove" />
</cf:producer>
<cf:producer name="Business Object Model (BOM)" typeName="CodeFluent.Producers.CodeDom.CodeDomProducer, CodeFluent.Producers.CodeDom">
<cf:configuration compileWithVisualStudio="true" compile="false" codeDomProviderTypeName="CSharp" targetDirectory="..\WcfServices.model" cfx:targetProject="..\WcfServices.model\WcfServices.model.csproj" cfx:targetProjectLayout="Update">
<subProducer typeName="Ixcys.Producers.ServiceModelProducer.ServiceProducer, Ixcys.Producers.ServiceModelProducer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" compileWithVisualStudio="true" compile="false" codeDomProviderTypeName="CSharp" targetDirectory="..\WcfServices.proxy" silverlightTargetVersion="Unspecified" jsonOptions="EnableJson" cfx:targetProject="..\WcfServices.web\WcfServices.web.csproj" cfx:targetProjectLayout="Update" />
</cf:configuration>
</cf:producer>
<cf:producer name="SQL Server Pivot Script" typeName="CodeFluent.Producers.SqlServer.SqlPivotScriptProducer, CodeFluent.Producers.SqlServer">
<cf:configuration targetDirectory="..\WcfServices.web" cfx:targetProject="..\WcfServices.web\WcfServices.web.csproj" cfx:targetProjectLayout="Update" />
</cf:producer>
<cf:entity name="Contact" namespace="Example.Model.Contact" categoryPath="/WcfServices.Model">
<cf:property name="ContactId" key="true" persistenceEnforce="true" />
<cf:property name="Name" persistenceEnforce="true" />
</cf:entity>
<cf:entity name="Case" namespace="Example.Model.Contact" categoryPath="/WcfServices.Model">
<cf:property name="CaseId" key="true" persistenceEnforce="true" />
<cf:property name="Description" persistenceEnforce="true" />
<cf:property name="InChargeContact" typeName="Example.Model.Contact.Contact" persistenceEnforce="true" />
</cf:entity>
<cf:entity name="Meeting" namespace="Example.Model.Contact" categoryPath="/WcfServices.Model">
<cf:property name="MeetingId" key="true" persistenceEnforce="true" />
<cf:property name="Date" typeName="date" persistenceEnforce="true" />
<cf:property name="Label" persistenceEnforce="true" />
<cf:property name="Contacts" typeName="Example.Model.Contact.ContactCollection" persistenceEnforce="true" />
</cf:entity>
</cf:project>
The stored procedure Contact_Delete
CREATE PROCEDURE [dbo].[Contact_Delete]
(
#Contact_ContactId [uniqueidentifier],
#_rowVersion [rowversion]
)
AS
SET NOCOUNT ON
DECLARE #error int, #rowcount int
DECLARE #tran bit; SELECT #tran = 0
IF ##TRANCOUNT = 0
BEGIN
SELECT #tran = 1
BEGIN TRANSACTION
END
DELETE FROM [Meeting_Contacts_Contact]
WHERE ([Meeting_Contacts_Contact].[Contact_ContactId] = #Contact_ContactId)
SELECT #error = ##ERROR, #rowcount = ##ROWCOUNT
DELETE FROM [Contact]
WHERE (([Contact].[Contact_ContactId] = #Contact_ContactId) AND ([Contact].[_rowVersion] = #_rowVersion))
SELECT #error = ##ERROR, #rowcount = ##ROWCOUNT
IF(#rowcount = 0)
BEGIN
IF #tran = 1 ROLLBACK TRANSACTION
RAISERROR (50001, 16, 1, 'Contact_Delete')
RETURN
END
IF #tran = 1 COMMIT TRANSACTION
RETURN
GO

This is by design. The delete procedure generated for an entity also deletes the records of the many to many table.
A simple workaround is to create a CFQL method DeleteById:
DELETE(Id) WHERE Id = #Id
This method generates the following stored procedure:
CREATE PROCEDURE [dbo].[Meeting_DeleteById]
(
#Id [uniqueidentifier]
)
AS
SET NOCOUNT ON
DECLARE #deletedcount int
DELETE FROM [Meeting]
WHERE ([Meeting].[Meeting_Id] = #Id)
SELECT #deletedcount = ##ROWCOUNT
SELECT #deletedcount
RETURN
GO

Related

Can a Raw SQL method that doesn't return data be called from the collection object?

The following entity has a raw method that truncates (deletes) all records in a SQL database. I would like the method to be called from the collection object. Not a game killer if it can't but is it possible?
UserCollection.TruncateDatabase()
instead of
User.TruncateDatabase()
<cf:entity name="User" namespace="Amikids.ActiveDirectoryCache" categoryPath="/Amikids.ActiveDirectoryCache">
<cf:property name="Id" key="true" />
<cf:property name="DirectReports" typeName="{0}.UserCollection" relationPropertyName="ReportsTo" />
<cf:property name="ReportsTo" typeName="{0}.User" relationPropertyName="DirectReports" />
<cf:property name="UserInfo" typeName="{0}.Lightweight.UserInfo" />
<cf:method name="TruncateDataBase">
<cf:body text="RAW" rawText="TRUNCATE TABLE dbo.[User]" language="tsql" />
</cf:method>
<cf:method name="LoadByObjectGuid" body="LOADONE(guid objectGuid) WHERE UserInfo.objectGUID = #objectGuid" />
<cf:method name="LoadByDistinguishedName" body="LOADONE(string distinguishedName) WHERE UserInfo.DistinguishedName = #distinguishedName" />
</cf:entity>
I think you can use a raw delete method: https://www.softfluent.com/Documentation/Methods_Delete.html
<cf:entity name="User">
<cf:method name="TruncateDataBase">
<cf:body text="DELETE() raw" rawBody="TRUNCATE TABLE dbo.[User]" language="tsql" />
</cf:method>
</cf:entity>

Can I sort by CreationTimeUTC using NON-RAW CFQL?

I am not able to sort by CreationTimeUTC. I know it can be done using a RAW CFQL query, but is there a way using normal CFQL. Steps I tried are below:
Made CreationTimeUtc show in the entity as a property as shown below (sorry for putting it in a code snippet, stackoverflow is sometimes quirky with XML and this was the only way to get it to show)
<cf:property name="CreationTimeUtc" set="false" persistent="false" readOnLoad="true" typeName="datetime" dbType="DateTime" dataMember="false" persistenceName="_trackCreationTime" />
Created the CFQL query as shown below (sorry for using blockquote, stackoverflow is quirky again and it wouldn't format correctly in a codeblock):
LOADONE(Amikids.TimeTracking.PunchCard punchCard) WHERE IsVoid = FALSE
ORDER BY CreationTimeUtc DESC
Upon building the Model I get the following error:
Error 52 CF0291: An attempt to create a persistence order by column
snippet 'CreationTimeUtc' has been detected in procedure
'CompanyCodeAdjustment_LoadActiveByPunchCard'. Make sure order by
column identifiers declared in the body are valid in the context. If
you really want to create a snippet, you should change the project,
method or view 'checkLevel' attribute, currently set to 'Default'.
Below is the full XML for the entity.
<cf:entity name="CompanyCodeAdjustment" namespace="Amikids.TimeTracking" categoryPath="/Amikids.TimeTracking" persistenceName="CompanyCodeAdjustment">
<cf:property name="Id" key="true" />
<cf:property name="PunchCard" typeName="{0}.PunchCard" relationPropertyName="CompanyCodeAdjustments" />
<cf:property name="PunchCardAdjustmentStatus" typeName="{0}.PunchCardAdjustmentStatus" />
<cf:property name="NewValue" />
<cf:property name="CreationUser" set="false" persistent="false" readOnLoad="true" dataMember="false" persistenceName="_trackCreationUser" />
<cf:property name="CreationTimeUtc" set="false" persistent="false" readOnLoad="true" typeName="datetime" dbType="DateTime" dataMember="false" persistenceName="_trackCreationTime" />
<cf:property name="Reason" />
<cf:property name="ReviewedBy" />
<cf:property name="IsVoid" typeName="bool" />
<cf:property name="CompanyName" />
<cf:method name="LoadActiveByPunchCard" body="LOADONE(Amikids.TimeTracking.PunchCard punchCard) WHERE IsVoid = FALSE ORDER BY CreationTimeUtc DESC" />
</cf:entity>
The CodeFluent Query Language (CFQL) provide a special syntax to access special columns such as the creation time column: $column.creationtime. However this is not available in the ORDER BY statement so you must use inline SQL [$Customer{TrackCreationTimeColumn.FullName}$]:
LOAD(datetime now)
WHERE $column.creationtime < #now
ORDER BY [$Customer{TrackCreationTimeColumn.FullName}$]
<cf:entity name="Customer" namespace="Model1">
<cf:property name="Id" key="true" />
<cf:method name="LoadOrderedByCreationDate" body="LOAD(datetime now) WHERE $column.creationtime < #now ORDER BY [$Customer{TrackCreationTimeColumn.FullName}$]" checkLevel="None" />
</cf:entity>

CFQL Search method using an OR expression among multiple properties

I'm trying to ellaborate a SEARCH CFQL method.
I have either a property TownId set or a property TownLabel in the entity Address:
TownId is set if the address got affected a Town from a referential (Towns are stored in another DB and are referenced only by ID).
TownLabel is set if none of the known Towns was found. (e.g. Name of a town abroad)
In my SEARCH method I want to look for users having as a town, either the townLabel OR possible towns from the referential whoes name contains the value townLabel.
Long story short, my CFQL methods is as follow
SEARCH(string[] townIds, string townLabel) WHERE Adresses.TownId IN (#townIds) OR Adresses.TownLabel LIKE '%'+#townLabel+'%'
Despite the OR operator, the generated SQL appears with the AND operator as below.
My question: is it normal ?
CREATE PROCEDURE [dbo].[User_AdvancedSearch]
(
#townIds [nvarchar] (max) = NULL,
#townLabel [nvarchar] (256) = NULL,
#_orderBy0 [nvarchar] (64) = NULL,
#_orderByDirection0 [bit] = 0
)
AS
SET NOCOUNT ON
DECLARE #sql nvarchar(max), #paramlist nvarchar(max)
SELECT #sql=
'SELECT DISTINCT [User].[User_UserId], [User].[User_Name], [User].[User_Association_AssociationId], [User].[_trackLastWriteTime], [User].[_trackCreationTime], [User].[_trackLastWriteUser], [User].[_trackCreationUser], [User].[_rowVersion]
FROM [User]
LEFT OUTER JOIN [Address] ON ([User].[User_UserId] = [Address].[Address_User_UserId])
WHERE ((1 = 1) AND (1 = 1))'
SELECT #paramlist = '#townIds nvarchar (max),
#townLabel nvarchar (256),
#_orderBy0 nvarchar (64),
#_orderByDirection0 bit'
IF #townIds IS NOT NULL
SELECT #sql = #sql + ' AND ([Address].[Address_TownId] IN ((SELECT [Item] FROM [dbo].cf_SplitString(#townIds, nchar(1)))))'
IF #townLabel IS NOT NULL
SELECT #sql = #sql + ' AND (([Address].[Address_TownLabel] LIKE ((''%'' + #townLabel) + ''%'')))'
EXEC sp_executesql #sql, #paramlist,
#townIds,
#townLabel,
#_orderBy0,
#_orderByDirection0
RETURN
GO
The schema is given below
<cf:project defaultNamespace="WcfServices.Model" xmlns:cf="http://www.softfluent.com/codefluent/2005/1" xmlns:cfx="http://www.softfluent.com/codefluent/modeler/2008/1" xmlns:cfps="http://www.softfluent.com/codefluent/producers.sqlserver/2005/1" xmlns:cfom="http://www.softfluent.com/codefluent/producers.model/2005/1" xmlns:cfsps="http://www.softfluent.com/codefluent/producers.sqlpivotscript/2013/1" createDefaultMethodForms="true" createDefaultApplication="false" createDefaultHints="false">
<cf:import path="Default.Surface.cfp" />
<cf:entity name="Association" namespace="Example.Model.Association" categoryPath="/WcfServices.Model">
<cf:property name="AssociationId" key="true" persistenceEnforce="true" />
<cf:property name="Label" persistenceEnforce="true" />
<cf:property name="Users" typeName="Example.Model.Association.UserCollection" relationPropertyName="Association" />
</cf:entity>
<cf:producer name="SQL Server" typeName="CodeFluent.Producers.SqlServer.SqlServerProducer, CodeFluent.Producers.SqlServer">
<cf:configuration produceViews="true" targetDirectory="..\WcfServices.persistence" cfx:targetProject="..\WcfServices.persistence\WcfServices.persistence.sqlproj" cfx:targetProjectLayout="Update, DontRemove" />
</cf:producer>
<cf:producer name="Business Object Model (BOM)" typeName="CodeFluent.Producers.CodeDom.CodeDomProducer, CodeFluent.Producers.CodeDom">
<cf:configuration compileWithVisualStudio="true" compile="false" codeDomProviderTypeName="CSharp" targetDirectory="..\WcfServices.model" cfx:targetProject="..\WcfServices.model\WcfServices.model.csproj" cfx:targetProjectLayout="Update">
</cf:configuration>
</cf:producer>
<cf:entity name="User" namespace="Example.Model.Association" categoryPath="/WcfServices.Model">
<cf:property name="UserId" key="true" persistenceEnforce="true" />
<cf:property name="Name" persistenceEnforce="true" />
<cf:property name="Association" typeName="Example.Model.Association.Association" relationPropertyName="Users" />
<cf:property name="Adresses" typeName="Example.Model.Association.AddressCollection" relationPropertyName="User" persistenceEnforce="true" />
<cf:method name="AdvancedSearch" body="SEARCH(string[] townIds, string townLabel) WHERE Adresses.TownId IN (#townIds) OR Adresses.TownLabel LIKE '%%'+#townLabel+'%%'" />
</cf:entity>
<cf:producer name="SQL Server Pivot Script" typeName="CodeFluent.Producers.SqlServer.SqlPivotScriptProducer, CodeFluent.Producers.SqlServer">
<cf:configuration targetDirectory="..\WcfServices.web" cfx:targetProject="..\WcfServices.web\WcfServices.web.csproj" cfx:targetProjectLayout="Update" />
</cf:producer>
<cf:entity name="Address" namespace="Example.Model.Association" categoryPath="/WcfServices.Model">
<cf:property name="AddressId" key="true" persistenceEnforce="true" />
<cf:property name="Line" persistenceEnforce="true" />
<cf:property name="User" typeName="Example.Model.Association.User" relationPropertyName="Adresses" persistenceEnforce="true" />
<cf:property name="TownLabel" />
<cf:property name="TownId" typeName="guid" />
</cf:entity>
</cf:project>
EDIT 15/09/2016 - adding original CFQL + schema.
Original CFQL method:
SEARCH (string firstname, string name, int[] groups, string postalCode, string townLabel, string mail, string[] townId, guid structureId, guid countryId) WHERE Firstname LIKE '%'+#firstname+'%' AND Name LIKE '%'+#name+'%' AND (Address.PostalCode LIKE '%'+#postalCode+'%' OR StructureContacts.Address.PostalCode LIKE '%'+#postalCode+'%') AND ( Address.TownLabel LIKE '%'+#townLabel+'%' OR StructureContacts.Address.TownLabel LIKE '%'+#townLabel+'%' OR Address.TownId IN (#townId) OR StructureContacts.Address.TownId IN(#townId)) AND (StructureContacts.Groups.Value IN (#groups) OR Groups.Value IN (#groups)) AND StructureContacts.Structure.StructureId = #structureId and (Address.Email LIKE '%'+#mail+'%' OR StructureContacts.Address.Email LIKE '%'+#mail+'%') AND (Address.CountryId = #countryId OR StructureContacts.Address.CountryId = #countryId) order by Name, Firstname
Schema:
<cf:project defaultNamespace="Example.Models" xmlns:cf="http://www.softfluent.com/codefluent/2005/1">
<cf:enumeration name="enumStructureType" namespace="Example.Models.Contact.Structure" categoryPath="/RH">
<cf:enumerationValue name="ENTERPRISE" />
<cf:enumerationValue name="ASSOCIATION" />
<cf:enumerationValue name="OTHER_ASSOCIATION" />
<cf:enumerationValue name="SUPPLIER" />
<cf:enumerationValue name="PUBLIC_ORGANIZATION" />
</cf:enumeration>
<cf:entity name="Structure" defaultUsePersistenceDefaultValue="false" defaultPersistenceEnforce="false" namespace="Example.Models.Contact.Structure" categoryPath="/RH" storeName="ExampleStoreName">
<cf:property name="StructureId" key="true" />
<cf:property name="Sigle" />
<cf:property name="CorporateName" />
<cf:property name="Code" />
<cf:property name="Comment">
<cf:rule typeName="StringValidate" maxLength="2000" />
</cf:property>
<cf:property name="Siret" xmlns:cf="http://www.softfluent.com/codefluent/2005/1" collectionKey="false">
<cf:rule typeName="RegularExpressionValidate" expression="[\d]{14,14}" />
</cf:property>
<cf:property name="Type" xmlns:cf="http://www.softfluent.com/codefluent/2005/1" defaultValue="EstablishmentType.PRINCIPAL" typeName="{0}.Contact.Structure.enumStructureType" collectionKey="false" />
<cf:property name="Groups" set="true" cascadeSave="After" cascadeDelete="Before" typeName="{0}.Contact.Contact.GroupCollection" />
<cf:property name="ApeId" typeName="guid" />
<cf:property name="Address" cascadeSave="After" cascadeDelete="Before" typeName="{0}.Contact.Contact.Address" relationSchema="Contact" />
<cf:property name="EstablishmentType" typeName="{0}.Contact.Structure.enumEstablishmentType" />
<cf:property name="ParentStructure" readOnSave="true" typeName="{0}.Contact.Structure.Structure" relationPropertyName="ChildrenStructures" />
<cf:property name="ChildrenStructures" set="true" cascadeSave="After" cascadeDelete="Before" typeName="{0}.Contact.Structure.StructureCollection" relationPropertyName="ParentStructure" />
<cf:property name="OpeningHours" maxLength="2000" />
<cf:method name="SimpleSearchStructures" body="SEARCH (string keyword) WHERE CorporateName LIKE '%%'+#keyword+'%%' OR Code LIKE '%%'+#keyword+'%%' OR Comment LIKE '%%'+#keyword+'%%' OR Sigle LIKE '%%'+#keyword+'%%' OR Siret LIKE '%%'+#keyword+'%%' OR Address.Email LIKE '%%'+#keyword+'%%' ORDER BY CorporateName" />
<cf:method name="AdvancedSearchStructures" body="SEARCH (string corporateName, string email, string postalCode, string townLabel, string[] townIds, int[] groups, guid countryId, int[] types) WHERE CorporateName LIKE '%%'+#corporateName+'%%' AND Address.Email LIKE '%%'+#email+'%%' AND (Address.TownLabel LIKE '%%'+#townLabel+'%%' OR Address.TownId IN (#townIds)) AND Address.PostalCode LIKE '%%'+#postalCode+'%%' AND Address.CountryId = #countryId AND Groups.Value IN (#groups) AND Type IN (#types) ORDER BY CorporateName" />
<cf:rule typeName="OnBeforeSave" methodName="BeforeSave" />
<cf:method name="DeleteById" body="DELETE(guid id) WHERE StructureId = #id" />
</cf:entity>
<cf:entity name="StructureAssociation" baseTypeName="Example.Models.Contact.Structure.Structure" namespace="Example.Models.Contact.Structure" categoryPath="/Example.Models" storeName="ExampleStoreName">
<cf:property name="ParutionDateOJ" xmlns:cf="http://www.softfluent.com/codefluent/2005/1" typeName="date" persistenceEnforce="true" collectionKey="false" />
<cf:property name="BasinId" xmlns:cf="http://www.softfluent.com/codefluent/2005/1" typeName="guid" relationSchema="Contact" persistenceEnforce="true" collectionKey="false" />
<cf:property name="ReceiptNum" xmlns:cf="http://www.softfluent.com/codefluent/2005/1" persistenceEnforce="true" collectionKey="false" />
<cf:property name="PrefectureId" typeName="guid" persistenceEnforce="true" />
<cf:property name="CreationDate" typeName="date" persistenceEnforce="true" />
<cf:property name="CartePecheAappmaId" persistenceEnforce="true" />
<cf:method name="LoadAssociationStructure" body="LOADONE WHERE EstablishmentType = 3" />
</cf:entity>
<cf:entity name="StructureContact" defaultUsePersistenceDefaultValue="false" setType="List" namespace="Example.Models.Contact.Structure" categoryPath="/RH" storeName="ExampleStoreName">
<cf:property name="Function" defaultValue="enumFunctionType.PRESIDENT" typeName="{0}.Contact.Structure.enumFunctionType" persistenceEnforce="true" collectionKey="false" />
<cf:property name="StartDateFunction" usePersistenceDefaultValue="true" typeName="date" persistenceEnforce="true" collectionKey="false" />
<cf:property name="EndDateFunction" usePersistenceDefaultValue="true" typeName="date" persistenceEnforce="true" collectionKey="false" />
<cf:property name="Contact" typeName="{0}.Contact.Contact.Contact" relationPropertyName="StructureContacts" relationSchema="Contact" persistenceEnforce="true" />
<cf:property name="Address" cascadeSave="After" cascadeDelete="Before" typeName="{0}.Contact.Contact.Address" relationSchema="Contact" persistenceEnforce="true" />
<cf:property name="Groups" set="true" cascadeSave="After" cascadeDelete="Before" typeName="{0}.Contact.Contact.GroupCollection" persistenceEnforce="true" />
<cf:property name="DocumentUrl" typeName="guid" persistenceEnforce="true" />
<cf:property name="Structure" typeName="{0}.Contact.Structure.Structure" persistenceEnforce="true">
<cf:attribute name="Newtonsoft.Json.JsonIgnore" class="" />
</cf:property>
<cf:property name="StructureContactId" key="true" persistenceEnforce="true" />
<cf:property name="AdditionalDate" usePersistenceDefaultValue="true" typeName="date" persistenceEnforce="true" />
<cf:property name="DocumentName" persistenceEnforce="true" />
<cf:method name="LoadBoardMembers" body="LOAD (int[] functions) WHERE Function IN (#functions)" />
</cf:entity>
<cf:entity name="StructureEnterprise" baseTypeName="Example.Models.Contact.Structure.Structure" namespace="Example.Models.Contact.Structure" categoryPath="/Example.Models" storeName="ExampleStoreName">
<cf:property name="VATNum" xmlns:cf="http://www.softfluent.com/codefluent/2005/1" persistenceEnforce="true" collectionKey="false" />
</cf:entity>
<cf:entity name="StructureOtherAssociation" baseTypeName="Example.Models.Contact.Structure.Structure" namespace="Example.Models.Contact.Structure" categoryPath="/Example.Models" storeName="ExampleStoreName">
<cf:property name="ReceiptNum" xmlns:cf="http://www.softfluent.com/codefluent/2005/1" persistenceEnforce="true" collectionKey="false" />
<cf:property name="SkillTerritoryType" typeName="{0}.Contact.Structure.enumStructureSkillTerritoryType" persistenceEnforce="true" />
</cf:entity>
<cf:entity name="Address" defaultUsePersistenceDefaultValue="false" namespace="Example.Models.Contact.Contact" categoryPath="/RH" persistenceName="Address" storeName="ExampleStoreName">
<cf:property name="AddressId" key="true" persistenceEnforce="true" />
<cf:property name="Line1" persistenceEnforce="true" />
<cf:property name="Line2" persistenceEnforce="true" />
<cf:property name="IsActive" typeName="bool" persistenceEnforce="true" />
<cf:property name="Landline" persistenceEnforce="true" />
<cf:property name="Email" persistenceEnforce="true">
<cf:rule typeName="EmailValidate" />
</cf:property>
<cf:property name="Fax" persistenceEnforce="true" />
<cf:property name="Website" persistenceEnforce="true" />
<cf:property name="Mobile" persistenceEnforce="true" />
<cf:property name="DirectLine" persistenceEnforce="true" />
<cf:property name="ProfessionalLine" persistenceEnforce="true" />
<cf:property name="TownId" typeName="guid" persistenceEnforce="true" />
<cf:property name="TownLabel" persistenceEnforce="true" />
<cf:property name="CountryId" typeName="guid" persistenceEnforce="true" />
<cf:property name="PostalCode" persistenceEnforce="true" />
<cf:rule typeName="OnBeforeSave" methodName="BeforeSave" />
</cf:entity>
<cf:entity name="Contact" defaultUsePersistenceDefaultValue="false" namespace="Example.Models.Contact.Contact" categoryPath="/RH" persistenceName="Contact" storeName="ExampleStoreName">
<cf:property name="Name" persistenceEnforce="true" />
<cf:property name="Firstname" persistenceEnforce="true" />
<cf:property name="Civility" defaultValue="CivilityContact.Mr" typeName="{0}.Global.Civility" persistenceEnforce="true" />
<cf:property name="DateOfBirth" serializationNullable="true" modelNullable="true" typeName="date" persistenceEnforce="true" />
<cf:property name="Comment" persistenceEnforce="true">
<cf:rule typeName="StringValidate" maxLength="2000" />
</cf:property>
<cf:property name="ContactId" key="true" persistenceEnforce="true" />
<cf:property name="StructureContacts" set="true" cascadeSave="After" cascadeDelete="Before" typeName="{0}.Contact.Structure.StructureContactCollection" relationPropertyName="Contact" relationSchema="Contact" persistenceEnforce="true" />
<cf:property name="Address" cascadeSave="After" cascadeDelete="Before" typeName="{0}.Contact.Contact.Address" relationSchema="Contact" persistenceEnforce="true" />
<cf:property name="TownOfBirthLabel" persistenceEnforce="true" />
<cf:property name="TownIdOfBirth" typeName="guid" persistenceEnforce="true" />
<cf:property name="CountryIdOfBirth" typeName="guid" persistenceEnforce="true" />
<cf:property name="DepartmentIdOfBirth" typeName="guid" persistenceEnforce="true" />
<cf:property name="PostalCode" persistenceEnforce="true" />
<cf:property name="Groups" set="true" cascadeSave="After" cascadeDelete="Before" typeName="{0}.Contact.Contact.GroupCollection" persistenceEnforce="true" />
<cf:property name="CartePecheMemberId" persistenceEnforce="true" />
<cf:method name="AdvancedSearchContactsTownLabel" body="SEARCH (string firstname, string name, int[] groups, string postalCode, string townLabel, string mail, string[] townId, guid structureId, guid countryId) WHERE Firstname LIKE '%%'+#firstname+'%%' AND Name LIKE '%%'+#name+'%%' AND (Address.PostalCode LIKE '%%'+#postalCode+'%%' OR StructureContacts.Address.PostalCode LIKE '%%'+#postalCode+'%%') AND ( Address.TownLabel LIKE '%%'+#townLabel+'%%' OR StructureContacts.Address.TownLabel LIKE '%%'+#townLabel+'%%' OR Address.TownId IN (#townId) OR StructureContacts.Address.TownId IN(#townId)) AND (StructureContacts.Groups.Value IN (#groups) OR Groups.Value IN (#groups)) AND StructureContacts.Structure.StructureId = #structureId and (Address.Email LIKE '%%'+#mail+'%%' OR StructureContacts.Address.Email LIKE '%%'+#mail+'%%') AND (Address.CountryId = #countryId OR StructureContacts.Address.CountryId = #countryId) order by Name, Firstname" />
<cf:method name="SimpleSearchContacts" body="SEARCH(string keywords) WHERE Firstname LIKE '%%'+#keywords+'%%' OR Name LIKE '%%'+#keywords+'%%' OR Address.Email LIKE '%%'+#keywords+'%%' OR StructureContacts.Address.Email LIKE '%%'+#keywords+'%%' order by Name, Firstname" />
<cf:rule typeName="OnBeforeSave" methodName="BeforeSave" />
<cf:method name="SearchContactByName" body="SEARCH(string nameOrFirstName, int[] functions) WHERE (Name LIKE '%%'+#nameOrFirstName+'%%' OR Firstname LIKE '%%'+#nameOrFirstName+'%%') AND StructureContacts.Function IN (#functions)" />
<cf:method name="LoadContactsByDateFunction" body="LOAD (date currentDate, int[] functions) WHERE StructureContacts.Function IN (#functions) AND ( StructureContacts.StartDateFunction <= #currentDate OR NOT StructureContacts.StartDateFunction EXISTS )
AND ( StructureContacts.EndDateFunction > #currentDate OR NOT StructureContacts.EndDateFunction EXISTS )" />
<cf:method name="DeleteById" body="DELETE(guid id) WHERE ContactId = #id" />
<cf:method name="AdvancedSearchContactTownId" body="SEARCH (string firstname, string name, int[] groups, string postalCode, string townLabel, string mail, string[] townId, guid structureId, guid countryId) WHERE Firstname LIKE '%%'+#firstname+'%%' AND Name LIKE '%%'+#name+'%%' AND (Address.PostalCode LIKE '%%'+#postalCode+'%%' OR StructureContacts.Address.PostalCode LIKE '%%'+#postalCode+'%%') AND (Address.TownId IN (#townId) OR StructureContacts.Address.TownId IN(#townId) ) AND (StructureContacts.Groups.Value IN (#groups) OR Groups.Value IN (#groups)) AND StructureContacts.Structure.StructureId = #structureId and (Address.Email LIKE '%%'+#mail+'%%' OR StructureContacts.Address.Email LIKE '%%'+#mail+'%%') AND (Address.CountryId = #countryId OR StructureContacts.Address.CountryId = #countryId) order by Name, Firstname" />
</cf:entity>
<cf:entity name="Group" defaultUsePersistenceDefaultValue="false" namespace="Example.Models.Contact.Contact" categoryPath="/RH" storeName="ExampleStoreName">
<cf:property name="GroupId" key="true" persistenceEnforce="true" />
<cf:property name="Value" defaultValue="enumGroupType.DGS" typeName="int" persistenceEnforce="true" />
</cf:entity>
<cf:entity name="StructureSupplier" baseTypeName="Example.Models.Contact.Structure.Structure" namespace="Example.Models.Contact.Structure" categoryPath="/Example.Models" storeName="ExampleStoreName">
<cf:property name="CartePecheDepositaireId" persistenceEnforce="true" />
<cf:property name="NumberExt" persistenceEnforce="true" />
<cf:property name="DocumentUrl" persistenceEnforce="true" />
<cf:property name="DocumentName" persistenceEnforce="true" />
<cf:property name="IsKeyAccount" typeName="bool" persistenceEnforce="true" />
<cf:property name="ConventionDate" typeName="date" persistenceEnforce="true" />
</cf:entity>
<cf:enumeration name="enumEstablishmentType" namespace="Example.Models.Contact.Structure" categoryPath="/Example.Models">
<cf:enumerationValue name="HEAD_OFFICE" />
<cf:enumerationValue name="SECONDARY" />
<cf:enumerationValue name="DELEGATION" />
<cf:enumerationValue name="ASSOCIATION" />
</cf:enumeration>
<cf:enumeration name="enumFunctionType" namespace="Example.Models.Contact.Structure" categoryPath="/Example.Models">
<cf:enumerationValue name="DIRECTOR" />
<cf:enumerationValue name="EMPLOYEE" />
<cf:enumerationValue name="ELECTED" />
<cf:enumerationValue name="VOLUNTEER" />
<cf:enumerationValue name="INDIVIDUAL" />
<cf:enumerationValue name="PRESIDENT" />
<cf:enumerationValue name="VICE_PRESIDENT" />
<cf:enumerationValue name="TREASURER" />
<cf:enumerationValue name="SECRETARY" />
<cf:enumerationValue name="BOARD_MEMBER" />
<cf:enumerationValue name="LEGAL_MEMBER" />
<cf:enumerationValue name="BY_LAW_MEMBER" />
<cf:enumerationValue name="ACCOUNT_INSPECTOR" />
<cf:enumerationValue name="RESELLER" />
<cf:enumerationValue name="OWNER" />
<cf:enumerationValue name="CONTRACTOR" />
<cf:enumerationValue name="GENERAL_CONTRACTOR" />
<cf:enumerationValue name="CONTRACTING_AUTHORITY" />
<cf:enumerationValue name="LESSOR" />
<cf:enumerationValue name="OTHER" />
<cf:enumerationValue name="MEMBER" />
</cf:enumeration>
<cf:enumeration name="enumStructureSkillTerritoryType" namespace="Example.Models.Contact.Structure" categoryPath="/Example.Models">
<cf:enumerationValue name="LOCAL" />
<cf:enumerationValue name="DEPARTEMENTAL" />
<cf:enumerationValue name="REGIONAL" />
<cf:enumerationValue name="NATIONAL" />
</cf:enumeration>
<cf:entity name="StructurePublicOrganization" baseTypeName="Example.Models.Contact.Structure.Structure" namespace="Example.Models.Contact.Structure" categoryPath="/Example.Models" storeName="ExampleStoreName">
<cf:property name="ReceiptNum" persistenceEnforce="true" />
</cf:entity>
</cf:project>
The generated SQL (note the extra AND in townLabel parameter)
CREATE PROCEDURE [Contact].[Contact_AdvancedSearchContactsTownLabel]
(
#firstname [nvarchar] (256) = NULL,
#name [nvarchar] (256) = NULL,
#groups [Contact].[cf_type_Contact_AdvancedSearchContactsTownLabel_2] READONLY,
#postalCode [nvarchar] (256) = NULL,
#townLabel [nvarchar] (256) = NULL,
#mail [nvarchar] (256) = NULL,
#townId [nvarchar] (max) = NULL,
#structureId [uniqueidentifier] = NULL,
#countryId [uniqueidentifier] = NULL,
#_orderBy0 [nvarchar] (64) = NULL,
#_orderByDirection0 [bit] = 0
)
AS
SET NOCOUNT ON
DECLARE #_c_groups int; SELECT #_c_groups= COUNT(*) FROM #groups
DECLARE #sql nvarchar(max), #paramlist nvarchar(max)
SELECT #sql=
'SELECT DISTINCT [Contact].[Contact].[Contact_Name], [Contact].[Contact].[Contact_Firstname], [Contact].[Contact].[Contact_Civility], [Contact].[Contact].[Contact_DateOfBirth], [Contact].[Contact].[Contact_Comment], [Contact].[Contact].[Contact_ContactId], [Contact].[Contact].[Contact_Address_AddressId], [Contact].[Contact].[Contact_TownOfBirthLabel], [Contact].[Contact].[Contact_TownIdOfBirth], [Contact].[Contact].[Contact_CountryIdOfBirth], [Contact].[Contact].[Contact_DepartmentIdOfBirth], [Contact].[Contact].[Contact_PostalCode], [Contact].[Contact].[Contact_CartePecheMemberId], [Contact].[Contact].[_trackLastWriteTime], [Contact].[Contact].[_trackCreationTime], [Contact].[Contact].[_trackLastWriteUser], [Contact].[Contact].[_trackCreationUser], [Contact].[Contact].[_rowVersion]
FROM [Contact].[Contact]
LEFT OUTER JOIN [Contact].[Address] ON ([Contact].[Contact].[Contact_Address_AddressId] = [Contact].[Address].[Address_AddressId])
LEFT OUTER JOIN [Contact].[StructureContact] ON ([Contact].[Contact].[Contact_ContactId] = [Contact].[StructureContact].[StructureContact_Contact_ContactId])
LEFT OUTER JOIN [Contact].[Address] [Address$1] ON ([Contact].[StructureContact].[StructureContact_Address_AddressId] = [Address$1].[Address_AddressId])
LEFT OUTER JOIN [Contact].[StructureContact_Groups_Group] ON ([Contact].[StructureContact].[StructureContact_StructureContactId] = [Contact].[StructureContact_Groups_Group].[StructureContact_StructureContactId])
LEFT OUTER JOIN [Contact].[Group] ON ([Contact].[StructureContact_Groups_Group].[Group_GroupId] = [Contact].[Group].[Group_GroupId])
LEFT OUTER JOIN [Contact].[Structure] ON ([Contact].[StructureContact].[StructureContact_Structure_StructureId] = [Contact].[Structure].[Structure_StructureId])
LEFT OUTER JOIN [Contact].[Address] [Address$2] ON ([Contact].[Contact].[Contact_Address_AddressId] = [Address$2].[Address_AddressId])
LEFT OUTER JOIN [Contact].[Contact_Groups_Group] ON ([Contact].[Contact].[Contact_ContactId] = [Contact].[Contact_Groups_Group].[Contact_ContactId])
LEFT OUTER JOIN [Contact].[Group] [Group$1] ON ([Contact].[Contact_Groups_Group].[Group_GroupId] = [Group$1].[Group_GroupId])
LEFT OUTER JOIN [Contact].[Address] [Address$3] ON ([Contact].[Contact].[Contact_Address_AddressId] = [Address$3].[Address_AddressId])
WHERE (((1 = 1) AND ((1 = 1) AND ((1 = 1) AND ((1 = 1) AND ((1 = 1) AND ((1 = 1) AND ((1 = 1) AND (1 = 1)))))))) AND (1 = 1))'
SELECT #paramlist = '#firstname nvarchar (256),
#name nvarchar (256),
#groups [Contact].[cf_type_Contact_AdvancedSearchContactsTownLabel_2] READONLY,
#postalCode nvarchar (256),
#townLabel nvarchar (256),
#mail nvarchar (256),
#townId nvarchar (max),
#structureId uniqueidentifier,
#countryId uniqueidentifier,
#_orderBy0 nvarchar (64),
#_orderByDirection0 bit'
IF #firstname IS NOT NULL
SELECT #sql = #sql + ' AND (([Contact].[Contact].[Contact_Firstname] LIKE ((''%'' + #firstname) + ''%'')))'
IF #name IS NOT NULL
SELECT #sql = #sql + ' AND (([Contact].[Contact].[Contact_Name] LIKE ((''%'' + #name) + ''%'')))'
IF #_c_groups > 0
SELECT #sql = #sql + ' AND (([Contact].[Group].[Group_Value] IN ((SELECT * FROM #groups)) OR [Group$1].[Group_Value] IN ((SELECT * FROM #groups))))'
IF #postalCode IS NOT NULL
SELECT #sql = #sql + ' AND ((([Contact].[Address].[Address_PostalCode] LIKE ((''%'' + #postalCode) + ''%'')) OR ([Address$1].[Address_PostalCode] LIKE ((''%'' + #postalCode) + ''%''))))'
IF #townLabel IS NOT NULL
SELECT #sql = #sql + ' AND ((([Address$2].[Address_TownLabel] LIKE ((''%'' + #townLabel) + ''%'')) OR (([Contact].[Address].[Address_TownLabel] LIKE ((''%'' + #townLabel) + ''%'')) OR ([Contact].[Address].[Address_TownId] IN ((SELECT [Item] FROM [dbo].cf_SplitString(#townId, nchar(1)))) OR [Contact].[Address].[Address_TownId] IN ((SELECT [Item] FROM [dbo].cf_SplitString(#townId, nchar(1))))))) AND ([Contact].[Address].[Address_TownLabel] LIKE ((''%'' + #townLabel) + ''%'')))'
IF #mail IS NOT NULL
SELECT #sql = #sql + ' AND ((([Address$3].[Address_Email] LIKE ((''%'' + #mail) + ''%'')) OR ([Contact].[Address].[Address_Email] LIKE ((''%'' + #mail) + ''%''))))'
IF #townId IS NOT NULL
SELECT #sql = #sql + ' AND (([Contact].[Address].[Address_TownId] IN ((SELECT [Item] FROM [dbo].cf_SplitString(#townId, nchar(1)))) OR [Contact].[Address].[Address_TownId] IN ((SELECT [Item] FROM [dbo].cf_SplitString(#townId, nchar(1))))))'
IF #structureId IS NOT NULL
SELECT #sql = #sql + ' AND (([Contact].[Structure].[Structure_StructureId] = #structureId))'
IF #countryId IS NOT NULL
SELECT #sql = #sql + ' AND ((([Contact].[Address].[Address_CountryId] = #countryId) OR ([Contact].[Address].[Address_CountryId] = #countryId)))'
SELECT #sql = #sql + ' ORDER BY [Contact].[Contact].[Contact_Name] ASC,[Contact].[Contact].[Contact_Firstname] ASC'
EXEC sp_executesql #sql, #paramlist,
#firstname,
#name,
#groups,
#postalCode,
#townLabel,
#mail,
#townId,
#structureId,
#countryId,
#_orderBy0,
#_orderByDirection0
RETURN
GO
EDIT 16/09/2016-possible workarounds found.
I found two possible workarounds:
First is to have a simpler search method with the rest of parameters (no townId, no townLabel), and to re-filter in memory with a LINQ mechanism.
Second is to go use generated code in a RAW method and adapt it to make it work the way we want.
Search methods are very tricky to generate. In fact, the producer must split the body of the method by parameters. Then it creates IF #param IS NOT NULL statement and combine them using an operator (by default AND). If you want to change this operator, you can add the following xml attribute at the method:
<cf:method cfps:searchOperation="OR" ... />
If you don't have too many arguments (1 or 2) you can use a LOAD method with nullable parameters:
<cf:method name="LoadNullable" body="LOAD(string text1, string text2) WHERE Name = #text1 AND Name = #text2">
<cf:parameter nullable="True" name="text1" modelNullable="False" />
</cf:method>
The generated code looks like:
CREATE PROCEDURE [dbo].[Customer_LoadNullable]
(
#text1 [nvarchar] (256) = NULL,
#text2 [nvarchar] (256)
)
AS
SET NOCOUNT ON
IF(#text1 IS NULL)
BEGIN
SELECT [Customer].[Customer_Id], [Customer].[Customer_Name]
FROM [Customer]
WHERE ([Customer].[Customer_Name] = #text2)
END
ELSE
BEGIN
SELECT [Customer].[Customer_Id], [Customer].[Customer_Name]
FROM [Customer]
WHERE (([Customer].[Customer_Name] = #text1) AND ([Customer].[Customer_Name] = #text2))
END
This way you don't have any issue with the OR and AND, but this is not suitable for methods with many nullable parameters.

Adding an unsaved entity to a collection in a composition relationship gives an error when using persistenceIdentity

My model contains the following entities:
<cf:entity name="Order" cfom:bindingList="false">
<!--persistenceIdentity is true, because the corresponding column for this property must be auto incremented by the database.-->
<cf:property name="Id" typeName="ulong" key="true" persistenceIdentity="true" cfps:hint="CLUSTERED" />
<!-- composition relation -->
<cf:property name="Orderlines" typeName="{0}.OrderlineCollection" cascadeDelete="Before" cascadeSave="After" />
<cf:method name="Save">
<cf:rule typeName="transaction" transactionType="TransactionScope" timeout="00:60:00" scopeOption="Required" />
</cf:method>
</cf:entity>
<cf:entity name="Orderline" cfom:bindingList="false">
<!--persistenceIdentity is true, because the corresponding column for this property must be auto incremented by the database.-->
<cf:property name="Id" typeName="ulong" key="true" persistenceIdentity="true" cfps:hint="CLUSTERED" />
<!-- Relation back to indicate an 1-to-n composition relationship. -->
<cf:property name="Order" typeName="{0}.Order" />
</cf:entity>
I have the following code:
Order order = new Order();
Orderline orderline = new Orderline();
order.Orderlines.Add(orderline); // ERROR
order.Save(); // Save the order and its orderlines
I get an error when adding the entity to the collection, because the Id property contains the value 0. How can I solve this? I do not want to save the entity before adding it to the collection.
You have to tell CodeFluent that it must use a list instead of a dictionary for the orderlines by means of the setType attribute. After that CodeFluent will no longer use the Id property for the Add and Contains methods, but still checks for the value 0 in the Add method, so you also have to add an OnAfterCreate rule that initializes the Id property:
<cf:entity name="Orderline" cfom:bindingList="false" setType="List">
<!--persistenceIdentity is true, because the corresponding column for this property must be auto incremented by the database.-->
<cf:property name="Id" typeName="ulong" key="true" persistenceIdentity="true" cfps:hint="CLUSTERED" />
<cf:property name="Name" typeName="string" />
<!-- Relation back to indicate an 1-to-n composition relationship. -->
<cf:property name="Order" typeName="{0}.Order" />
<cf:rule typeName="OnAfterCreate" />
<cf:snippet name="OnAfterCreate" language="CSharp">
<!-- here or in a partial class -->
private void OnAfterCreate()
{
this._id = long.MaxValue;
}
</cf:snippet>
</cf:entity>
Now the code works. The orderlines are added and saved within the transaction that is created when saving the order. After saving the order, the orderlines have got their id values from the database:
Order order = new Order();
Orderline orderline = new Orderline();
orderline.Name = "First order";
order.Orderlines.Add(orderline);
orderline = new Orderline();
orderline.Name = "second order";
order.Orderlines.Add(orderline);
order.Save();

How to load entities of which a property of type PersistentList contains one or more values from array parameter?

My model contain the following enumeration and entity:
<cf:enumeration name="Language" usePersistenceDefaultValue="false">
<cf:enumerationValue name="EN" value="1" default="true" />
<cf:enumerationValue name="NL" value="2" />
<cf:enumerationValue name="DE" value="3" />
</cf:enumeration>
<cf:entity name="Person" >
<cf:property name="Id" key="true" />
<cf:property name="Languages" typeName="CodeFluent.Runtime.Utilities.PersistentList<Language>">
<cf:message class="_doc">The languages that the person speaks</cf:message>
</cf:property>
<cf:method name="LoadPersonThatSpeaksOneOrMoreLanguages" checkLevel="None" memberAttributes="Public" >
<cf:body language="tsql" text="load(Language[] languages) from Person where Languages in (#languages)" />
</cf:method>
</cf:entity>
The method LoadPersonThatSpeaksOneOrMoreLanguages should return all persons that speak one or more of the provided languages.
The generated stored procedure for this method seems not to be correct:
ALTER PROCEDURE [dbo].[Person_LoadPersonThatSpeaksOneOrMoreLanguages]
(
#languages [dbo].[cf_type_Person_LoadPersonThatSpeaksOneOrMoreLanguages_0] READONLY,
#_orderBy0 [nvarchar] (64) = NULL,
#_orderByDirection0 [bit] = 0
)
AS
SET NOCOUNT ON
DECLARE #_c_languages int; SELECT #_c_languages= COUNT(*) FROM #languages
SELECT DISTINCT [Person].[Person_Id], ...
FROM [Person]
WHERE [Person].[Person_Languages] IN (((SELECT * FROM #languages)))
Question 1:
How can I achieve the desired result?
Should I create a Language entity and specify an 1:n association between Person and Language? I prefer not to have a Language entity.
Or can I specify that the Languages property must be converted to the same type as the table-valued-parameter (cf_type_Person_LoadPersonThatSpeaksOneOrMoreLanguages_0)?
Question 2:
The produced PersonCollection class contains the method LoadPersonThatSpeaksOneOrMoreLanguages. The parameter of this method is of type Language[]. Instead of an array I want an IEnumerable<Language>. How can I specify this in my XML model?
Question 1
The PersistentList is designed to store a collection of simple values (int, string, enum, ...), but not to query them directly in SQL. Indeed the PersistentList is translated to a NVARCHAR column in the database and this column contains values like EN|NL (pipe separated values). The database engine does not know how to extract single values from the string. Maybe you can use the cf_SplitString function to create a table from the column value and do what you want with it, but it does not seem to be the simplest solution...
Depending of your need you can use a multi-valued enumeration:
<cf:enumeration name="Language" flags="true">
<cf:enumerationValue name="Unspecified" /> <!-- value=0 -->
<cf:enumerationValue name="EN" /> <!-- value=1 -->
<cf:enumerationValue name="NL" /> <!-- value=2 -->
<cf:enumerationValue name="FR" /> <!-- value=4 -->
</cf:enumeration>
You can use them with CFQL:
-- Load Persons that speak the specified language
LOAD(Languages) WHERE (Languages & #Languages) = #Languages
-- Load Persons that speak at least one of the specified language
LOAD(Languages) WHERE (Languages & #Languages) <> 0
Of course the latest possibilities is to create a Language entity and use Table Valued Parameters.
http://blog.codefluententities.com/2014/07/16/persistent-list/
http://www.softfluent.com/documentation/Enumerations_Overview.html
Question 2
From the official blog of CodeFluent Entities:
Take what I say with a grain of salt as you are dealing with a topic I have looked into but never implemented. With that said another approach would be to use a multi-valued enumeration (flag)
http://blog.codefluententities.com/2013/05/29/using-flags-enumeration-with-aspnet-mvc-and-codefluent-entities
Using this approach you would create a relationship between the entity and the enumeration instead of making the Languages property a persistent list.
The following mostly works. When using the Modeler to create instances I was not able to select certain combinations of languages. Don't know if this was due to my inexperience in working with and setting up flag enumeration or if there is a flaw in the modeler. But I did manage to create the method and that part appears to be working.
<cf:enumeration name="Language" multivalue="true" usePersistenceDefaultValue="false" namespace="Demo1" categoryPath="/Demo1">
<cf:enumerationValue name="Unspecified" default="true" />
<cf:enumerationValue name="EN" />
<cf:enumerationValue name="NL" />
<cf:enumerationValue name="DE" />
</cf:enumeration>
<cf:entity name="Person" namespace="Demo1">
<cf:property name="Id" key="true" />
<cf:property name="Languages" usePersistenceDefaultValue="false" typeName="{0}.Language" />
<cf:property name="FirstName" />
<cf:property name="LastName" />
<cf:instance>
<cf:instanceValue name="Id">d13447c6-a709-4c87-891d-e83674821915</cf:instanceValue>
<cf:instanceValue name="FirstName">Jon</cf:instanceValue>
<cf:instanceValue name="LastName">Smith</cf:instanceValue>
</cf:instance>
<cf:instance>
<cf:instanceValue name="Id">77e3730c-2cc3-457d-8bc0-d9a5e224b96a</cf:instanceValue>
<cf:instanceValue name="FirstName">Sam</cf:instanceValue>
<cf:instanceValue name="Languages">DE, SP</cf:instanceValue>
<cf:instanceValue name="LastName">Newman</cf:instanceValue>
</cf:instance>
<cf:method name="LoadPersonThatSpeaksOneOrMoreLanguages" body="LOAD() WHERE Languages > 0" />
As I said take what I'm saying with a grain of salt. I ended up using an entity instead of an enumeration but only because that was what I was more familar with and had a deadline.