Is there any way to view the persistence tracking fields (_trackLastWriteTime) as properties in my BOM.
I've seen a similar question in your website forums, but due to the updates you made to it, answers are lacking of "code" sections, so they are useless.
Thanks again!
Josep.
You can just declare the following properties to the entity:
<cf:property name="LastWriteTime" typeName="datetime" persistenceName="_trackLastWriteTime" readOnLoad="true" readOnSave="true" persistent="false"/>
<cf:property name="CreationTime" typeName="datetime" persistenceName="_trackCreationTime" readOnLoad="true" persistent="false" />
<cf:property name="CreationUser" persistenceName="_trackCreationUser" readOnLoad="true" persistent="false" />
<cf:property name="LastWriteUser" persistenceName="_trackLastWriteUser" readOnLoad="true" readOnSave="true" persistent="false" />
persistenceName should match the column name. This is the default column name but it could be different in your context. The property name is not important, only the persistence/column name.
persistent = false means the property is not persistent. This instructs the inference pipeline to not create a column corresponding to this property (because in fact it has already created that column).
readOnLoad = true and/or readOnSave = true means all layers will load and/or save this property, although it's been marked as non persistent.
Related
Is it possible to make a Property in SAPUI5 Smart Table mandatory so that a user can't leave a field empty while editing it?
I have found this document saying that it should be possible to set mandatory field control on Property in metadata.xml file with annotation like this:
<Property Name="NameLast" Type="Edm.String" Nullable="false" MaxLength="40" sap:label="Last name" sap:field-control="7" />
But with this setting I am getting following error in console:
2017-04-14 11:37:36.691429 MockServer: Resource not found for the segment '7'
2017-04-14 11:37:36.707985 The following problem occurred: HTTP request failed404,Not Found,{"error":{"code":404,"message":{"lang":"en","value":"Resource not found for the segment '7'"}}} -
EDIT:
Later I found out that sap:field-control should not contain number, but a path expression to another property which contains the number. However this also doesn't work:
<Property Name="NameLast" Type="Edm.String" Nullable="false" MaxLength="40" sap:label="Last name" sap:field-control="Name_fc" />
<Property Name="Name_fc" Type="Edm.Byte" />
Value of Name_fc property is '7'. I don't see any console error now, however I can still leave the input (NameLast) field empty without any validation and send it to OData service, which is not what I expect.
Here is a link to an example from sap where they use required fields. I have no idea though how they made it.
https://sapui5.netweaver.ondemand.com/sdk/explored.html#/sample/sap.ui.comp.sample.smartfield/code/SmartField.view.xml
For OData v2 the sap:field-control annotation on the Property can be
used to specify whether the field is mandatory.
<Property Name="Customer" ... sap:field-control="mandatory"/>
<Property Name="CompanyCode" ... sap:field-control="mandatory"/>
https://sapui5.hana.ondemand.com/#docs/api/symbols/sap.ui.comp.smartfield.SmartField.html
The mandatory property of the entity has to be nullable="false". That's it.
Have a look at the smart field example from your link:
<Property Name="Name" Type="Edm.String" Nullable="false"
MaxLength="30" sap:label="Name" sap:creatable="false"
sap:quickinfo="Property annotation DataFieldWithUrl"
sap:updatable="true" sap:sortable="false" />
Btw. thanks for sharing your smart table example!
From my understanding there are 2 options:
1) Define a specific property as Mandatory --> Nullable="false"
2) Link a property to another property in the entity, the "field-control".
This field control can contain numbers and "7" means mandatory.
The linking from option 2 can be done in the MPC_EXT class (redefine the define method).
The actual value in the "field-control" property is set in the get_entity / get_en
However I am also having an issue with the smarttable. The mandatory fields do not light up red when empty.
Do it in brute force way in the Object Page extension controller.
var oField = this.getView().byId(<FieldId>)
oField.getDataProperty().property.nullable = "false" or "true".
"false" and "true" must be a string.
To convert a boolean to string use <boolean>.toString();
In my model I'm using bigint (ulong) as the type for entity keys.
The value 0 must be used for empty keys.
Columns for foreign keys must not be nullable, because in my methods I only want to check for the value 0 and not for null.
Everything works fine, except for the deletion of related entities that are referenced by other entities.
Here is my model:
<cf:entity name="Customer" 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" />
<cf:property name="Name" typeName="string" />
</cf:entity>
<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" />
<!--persistenceNullable is false, because the column for the foreign key must not be nullable.-->
<cf:property name="Customer" typeName="{0}.Customer" persistenceNullable="false" />
</cf:entity>
Here is my code:
Customer customer = new Customer();
customer.Save();
Order order = new Order();
order.Customer = customer;
order.Save();
customer.Delete();
The last statement gives the following error:
Cannot insert the value NULL into column 'Order_Customer_Id', table 'CodeFluentTest.dbo.Order'; column does not allow nulls. UPDATE fails.
This is because the Customer_Delete stored procedure contains the following update statement:
UPDATE [Order] SET [Order].[Order_Customer_Id] = NULL
WHERE ([Order].[Order_Customer_Id] = #Customer_Id)
Of course this will not work, because the Order_Customer_Id column is not nullable.
How can I instruct CodeFluent to put the value 0 instead of NULL into the Order_Customer_Id column?
CodeFluent does not really support object keys that are not nullable, because the implied semantics would be somehow strange. When you delete an object, from an OO perspective, the object is now null, it's identifier does not exist any more, it's not set to a specific 0 or other value. You will probably run into other problems with tweaking the model like this.
That being said, one way to change this behavior is to use the "persistenceUnlink" attribute on the property involved, directly in the XML file. Unfortunately the graphical modeler does not support this (ancient) attribute and will override it everytime you modify the model and save it back using the GUI.
So, what you can do is use a custom aspect to automatically apply this attribute on properties where you want it. Here is a sample code for such an aspect (note the aspect runs on startup because it's really based on XML, not on the in-memory model contrary to most aspects):
<cf:project xmlns:cf="http://www.softfluent.com/codefluent/2005/1" defaultNamespace="unlink">
<cf:pattern name="Unlink Aspect" namespaceUri="http://www.example.com/unlink" preferredPrefix="ul" step="Start">
<cf:message class="_doc">
Sample aspect that removes auto unlink in delete procedures
</cf:message>
<cf:descriptor name="unlink" targets="Property" defaultValue="true" displayName="Unlink" typeName="boolean" description="Determines if this property will be unlinked during delete" category="Unlink Aspect" />
</cf:pattern>
<?code #namespace name="System" ?>
<?code #namespace name="System.Xml" ?>
<?code #namespace name="CodeFluent.Model" ?>
<?code
// use a special utility method to get all elements
// with the given attribute in a given namespace URI
var properties = Project.Package.RootModelPart.SelectElements("unlink", "http://www.example.com/unlink", false);
foreach(var property in properties)
{
// here we set a special attribute not supported by the GUI designer in Visual Studio
property.SetAttribute("persistenceUnlink", "false");
}
?>
</cf:project>
What you must do is:
save this code as a file, for example "unlink.xml" somewhere around your project files.
in the graphical modeler, right-click on the "Aspects" node of the CodeFluent project, and choose "Add existing aspect", browse to your unlink.xml file (it should display the aspect metadata), and press ok.
go back to the design surface in the graphical modeler, click on the property you want to remove persistence link, go to the Visual Studio property grid, choose the blue "aspects and producers properties" tab, and set "Unlink" to False that should now be displayed (default is true).
rebuild, and the stored procedure code should not contain links for this relation any more.
I have a custom stored procedure with in parameters that return fields of different tables how I can map this custom stored to an entity? I only want to use like a read only values for a report I don't want to save or something like that I try to add the extra fields to the most similar entity but when I execute the method in code the extra fields are null
Solution 1: Using a view
A view allows to aggregate data from different entities.
<Article>
<Id />
<Name />
<Lines typeName="LineCollection" />
<cf:method name="LoadArticlesByCommand" body="load(string commandName) from ArticleByCommand where CommandName = #commandName" />
<cf:view name="ArticleByCommand" autoLightweight="true">
<ArticleName expression="Name"/>
<ArticleQty expression="Lines.Quantity" />
<CommandName expression="Lines.Command.Name" />
</cf:view>
</Article>
<Command>
<Id />
<Name />
<Lines typeName="LineCollection" />
</Command>
<Line setType="List">
<Article typeName="Article" key="true" />
<Command typeName="Command" key="true" />
<Quantity typeName="int" />
</Line>
http://blog.codefluententities.com/2014/04/22/views-auto-lightweight-and-the-modeler/
https://www.softfluent.com/documentation/Views_PersistentViews.html
Solution 2: Using a lightweight entity
Instead of creating a view, you can can create a lightweight entity that contains only the properties used by the stored procedure.
<cf:entity name="Person" lightweight="true">
<cf:property name="FirstName" typeName="string" />
<cf:property name="lastName" typeName="string" />
<cf:method name="ComputeBalance"
body="load () raw"
rawBody="SELECT 'John' AS FirstName, 'Doe' AS LastName" />
</cf:entity>
Solution 3: Custom mapping
For more specific values or types, a custom method can be provided to map the database values to .NET types. This custom method will be called with a DataReader as parameter, meaning that a developer could do whatever he wants.
<cf:entity name="Sample">
<cf:method name="LoadPair" body="raw" rawBody="SELECT 1234,5678"
returnTypeName="CodeFluent.Runtime.Utilities.Pair<System.Int32,System.Int32>"
cfom:methodName="On{0}" />
<cf:snippet>
private static CodeFluent.Runtime.Utilities.Pair<int,int> OnLoadPair(System.Data.IDataReader reader)
{
return new Pair<int, int>(reader.GetInt32(0), reader.GetInt32(1));
}
</cf:snippet>
</cf:entity>
You can also use OnAfterReadRecord or OnBeforeReadRecord rules
If it is not essential that you map the results of the custom stored procedure to an entity than another option is to use the built in support for DataSets.
http://blog.codefluententities.com/2011/06/22/dataset-support-in-codefluent-entities/
<cf:method name="LoadAllCities" body="raw" returnTypeName="System.Data.DataSet">
SELECT $Address::City$ FROM $Address$
</cf:method>
.
DataSet ds = Address.LoadAllCities();
foreach (DataTable table in ds.Tables)
{
foreach (DataRow row in table.Rows)
{
Console.WriteLine("City: " + row[0]);
}
}
Upon re-reading you're question I am providing another answer.
In response to the part where you said "I try to add the extra fields to the most similar entity but when I execute the method in code the extra fields are null". The following steps should be able to solve that problem.
Execute one of the automatically created stored procedure in SQL Management Studio.
Execute the stored procedure you manually created.
Verify that the fieldnames returned by both stored procedures match.
I think the above will solve your immediate problem but I still don't like the solution. The reason is that you said you picked the most similar entity. I think that is going to cause problems in the future especially if the stored procedure is not being mapped to all of the entities properties.
I would recommend either lightweight entity, view or DataSet.
While using CF Entities, I'm facing an issue defining instances of an entity that uses an enumeration value.
I can add the instances of the entitytwor, but during build or save an error would show up arguing:
CF0134: Instance value for key property 'EnumType' of entity 'Namespace.Entity' cannot be equal to the key property default value '0' of type 'System.Int32' ...
XML would present associated enumeration value for the property as expected.
It looks like the problem only occurs when first enumeration value is being used (associated value 0). Other lines won't be a problem.
Is there any particular things to do while defining enumeration values or use of them ?
Thanks for your answer
CodeFluent Entities defines the concept of "default value" for properties of any type, including value types and enums. The default value allows to store null in a database without having to use .NET Nullable type.
<cf:enumeration name="EmailStatus">
<cf:enumerationValue name="Valid" /> <!-- will become NULL in the Database -->
<cf:enumerationValue name="Invalid" />
</cf:enumeration>
Although the default values behavior is enabled by default you can also change it:
at the Property level by setting its usePersistenceDefaultValue attribute to false:
<cf:property typeName="EmailStatus" usePersistenceDefaultValue="false" />
at the Enumeration level by setting its usePersistenceDefaultValue attribute to false:
<cf:enumeration usePersistenceDefaultValue="false" />
at the Project level by setting its defaultUsePersistenceDefaultValue attribute to false:
<cf:project defaultUsePersistenceDefaultValue="false" />
http://blog.codefluententities.com/2012/11/15/property-default-values-and-enumerations/
I have added a custom property to the Properties dialog of the Entity Framework 5 designer through
http://msdn.microsoft.com/en-us/library/microsoft.data.entity.design.extensibility.ientitydesignerextendedproperty(v=vs.103).aspx
This works well, the property appears in the Properties dialog and it is saved in the EDMX file.
Now I'd like to use that property in the DDL generation process. I have edited the T4 template file SSDLToSQL10.tt (found at C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\Extensions\Microsoft\Entity Framework Tools\DBGen).
However, the custom property doesn't seem to appear anywhere in the metadata tree. The website (in German)
http://www.databinding.net/en/blog/post/2010/12/13/entity-framework-4-erweiterte-eigenschaften-in-einer-t4-vorlage-verwenden.html
tells me that the extended property should appear in the EntityType.MetadataProperties collection, but this collection contains only the following members:
KeyMembers Members Name NamespaceName Abstract BaseType DataSpace MetadataProperties
None of those is my custom property.
Am I missing something? How can I access the IEntityDesignerExtendedProperty's value in the T4 code generation template?
EDIT: Here is the EDMX part with the custom property:
<edmx:ConceptualModels>
<Schema ...>
....
<EntityType Name="Entity1">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Type="Guid" Name="Id" Nullable="false" annotation:StoreGeneratedPattern="None" />
<Property Type="String" Name="Name" Nullable="false" />
<a:MyNewProperty xmlns:a="http://schemas.tempuri.com/MyNewProperty">True</a:MyNewProperty>
</EntityType>
I guess I have to map that custom property from CSDL to SSDL somehow.
You added the property to the CSDL (conceptual layer) while the DDL is created using the SSDL (store layer). You should be able to access the conceptual model in the SSDLToSQL10.tt but I don't think it is really what you are after. In general your property is not something the EF runtime can really use - I believe it will be just treated as an extension and ignored. If you want to add a property that is supposed to be used by the EF runtime the property must be declared in the CSDL (conceptual layer) and SSDL (store layer) and mapped correctly in the MSL (mapping layer) - with the latter being probably the most difficult.
Unless I am missing what you are trying to achieve you are probably using a wrong extension point. The IEntityDesignerExtendedProperty allows defining custom properties that shows in the property and the model browser windows in the designer but are ignored at runtime. For me it looks like you would like to add a property automatically to your model. For that I would try using the IModelTransformationExtension where you should be given the entire edmx which you will be able to modify at will (i.e. CSDL, SSDL, MSL and add elements (properties) in correct EF xml namespaces). I would try using OnBeforeModelSaved since I believe the model will be saved automatically before trying to generate the database.
I was able to achieve what I want using the edmx:CopyToSSDL=true attribute:
<edmx:ConceptualModels>
<Schema ...>
....
<EntityType Name="Entity1">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Type="Guid" Name="Id" Nullable="false" annotation:StoreGeneratedPattern="None" />
<Property Type="String" Name="Name" Nullable="false" />
<a:MyNewProperty edmx:CopyToSSDL="true"
xmlns:a="http://schemas.tempuri.com/MyNewProperty">
True
</a:MyNewProperty>
</EntityType>
This way, the transformator that generates the SSDL from the CSDL copies the annotation over to the SSDL, so I'm able to access it in the T4 template that generates the DDL SQL file.
If someone is going to use this in Entity Framework 5, please not that there is a bug (http://entityframework.codeplex.com/workitem/702), and you can workaround by using the old EDMX XML namespace:
<a:MyNewProperty edmxv2:CopyToSSDL="true"
xmlns:a="http://schemas.tempuri.com/MyNewProperty"
xmlns:edmxv2="http://schemas.microsoft.com/ado/2008/10/edmx">
True
</a:MyNewProperty>