CodeFluent tries to update a non-nullable foreign key of type bigint with NULL when deleting related entity - codefluent

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.

Related

codefluent custom stored procedure

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.

Cannot use value 0 of an enumeration while defining instances of an entity

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/

use _trackXXX columns in the business object model

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.

Entity Framework designer custom property does not appear in DDL generation

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>

EF 4.0 POCO magic doesn't work-- No changes detected

I can't seem to update my database from disconnected poco objects. In this example, I fetch an ApplicationUser object, update varchar(100) field SSOID, but no changes take effect. Even when I refetch the object in the save method, nothing gets sent to the db.
If I try to call ApplyCurrentValues, it throws
An object with a key that matches the key of the supplied object could not be found in the ObjectStateManager. Verify that the key values of the supplied object match the key values of the object to which changes must be applied.
public void Save(ApplicationUser au)
{
ApplicationUser original = context.ApplicationUsers.FirstorDefault(a => a.UserID == au.UserID);
context.ContextOptions.ProxyCreationEnabled = false; //changing this has no effect
if(original == null)
{
context.AddObject("ApplicationUsers",au); //this works!
}
else
{
///let's manually set some properties on the object I just fetched.
original.SSOID = au.SSOID;
ObjectStateEntry entry = null;
context.DetectChanges();
context.ObjectStateManager.TryGetObjectStateEntry(original.EntityKey, out entry);
//entry is still null!
context.ApplicationUsers.ApplyCurrentValues(original); //BOOM!!
}
context.SaveChanges();
return;
}
I tried everything I can think of, even making ApplicationUser implement System.Data.Objects.DataClasses.IEntityWithKey, which supposedly isn't necessary.
Here's the mapping:
<EntitySetMapping Name="ApplicationUsers">
<EntityTypeMapping TypeName="MyCompanyName.ApplicationUser">
<MappingFragment StoreEntitySet="ApplicationUser">
<ScalarProperty Name="UserID" ColumnName="UserID" />
<ScalarProperty Name="SystemID" ColumnName="SystemID" />
<ScalarProperty Name="Username" ColumnName="Username" />
<!--snip-->
<ScalarProperty Name="CreatedOn" ColumnName="CreatedOn" />
<ScalarProperty Name="CreatedBy" ColumnName="CreatedBy" />
<ScalarProperty Name="SSOID" ColumnName="SSOID" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
Are you using the straight/simple POCO T4 template, or the self-tracking entities template??
The straight POCO have no support for any change tracking whatsoever - if you want that, you need the self-tracking entities.
See resources:
Using Self Tracking Entities to retrieve and update
Working with Self-Tracking Entities
Update: I think you are quite close to the right way of doing things here. You re-load the original state of the object (I would probably add a check on a timestamp or something to make sure the object in store hasn't been changed in the meantime).
Once you've done that, I believe you just need to detect yourself what changes have happened / where differences exist between au and original; update the original accordingly, and then just simply call context.SaveChanges().
As far as I understand it, the .DetectChanges() can't work, since you're using straight POCO classes without any change tracking, e.g. your au object doesn't have any way of knowing what's been changed - you basically need to do a property-by-property comparison yourself.