How can I remove the underscore in the name of a column for a foreign key? - codefluent

My model contains the entities Order and Orderline. The Orderline has a property of type Order:
<cf:project
persistencePropertyNameFormat="{1}"
persistenceTableIdentifierSeparatorChar=""
persistenceRelationIdentifierSeparatorChar="" ...>
<cf:entity name="Orderline">
<cf:property name="Id"/>
<cf:property name="Order" typeName="{0}.Order" />
</cf:entity>
CodeFluent has created a database containing a table named Orderline with a column named Order_Id.
How can I remove the underscore in the name of this column?
I tried to specify an empty string for the persistenceTableIdentifierSeparatorChar and persistenceRelationIdentifierSeparatorChar attributes, but that didn't help.

Related

How to produce a nullable property in the lightweight entity for a database view?

My model contains the following entity:
<cf:entity name="SomeEntity">
<cf:property name="Id" key="true" />
<cf:property name="SomeDate" typeName="date?" />
<cf:view name="SomeEntitySummary" autoLightweight="true" checkLevel="none">
<cf:viewProperty name="Id" />
<cf:viewProperty name="SomeDate" nullable="true" typeName="date?" />
</cf:view>
</cf:entity>
The produced code for the SomeEntity class contains a SomeDate property of type
Nullable<DateTime>
However, the produced code for the SomeEntitySummary class contains a SomeDate property of type DateTime and is not nullable.
How can I produce a nullable property in the lightweight entity that is produced for the database view?
CodeFluent Entities infer views from the properties of the entity. So, if the property is nullable, the property of the view should also be nullable. However, it seems there is a small issue, so you must declare the property as modelNullable explicitly (the modeler generates the right xml).
The following model does generate a DateTime? property in the SomeView class:
<cf:entity name="Customer">
<cf:property name="Id" key="true" />
<cf:property name="Name" />
<cf:property name="SomeDate" modelNullable="true" typeName="date" />
<cf:view autoLightweight="true" name="SomeView">
<cf:viewProperty name="Id" />
<cf:viewProperty name="SomeDate" />
</cf:view>
</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>

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.

Entity Framework with stored procedure using defining query

I wrote a defining query
<EntitySet Name="EntityFramework" EntityType="SEOAnalysisModel.Store.EntityFramework">
<DefiningQuery>
SELECT Keyword, ResultHead ,Year from SeoAnalysis where Year = 2005
</DefiningQuery>
</EntitySet>
And entity type for custom entity
<EntityType Name="EntityFramework">
<Key>
<PropertyRef Name="Year" />
</Key>
<Property Name="Year" Nullable="false" Type="int" />
<Property Name="Keyword" Nullable="false" MaxLength="1000" Type="varchar" />
<Property Name="ResultHead" Nullable="false" MaxLength="2000" Type="varchar" />
</EntityType>
But when I call a stored procedure, it returns only 1 value of the column
// Stored procedure
public void SelectValue() {
using (MyConnection ctx = new MyConnection()) {
foreach (var p in ctx.EntityFramework(2005)) {
Response.Write(p.Keyword);
}
}
}
And this column value is repetitive.
Now how can I get all column value?
Actually I am getting same keyword repetitive.
If I have keyword like Apple, then this keyword is repeating until loop is running.
You have marked Year as key for the entity. Key must be unique among all records in your defining query otherwise it is not a key. If the key is not unique, EF will do exactly what you got - it will materialize only the first record into entity and use it for all other records as well. EF uses key to identify the entity - if you get two records with the same key value, EF believes that it is the same entity!