Can not save an entity with the value 0 for a non-nullable numeric property - codefluent

My model contains an entity Order with a non-nullable property Amount of type decimal:
cf:entity name="Order">
<cf:property name="Id" />
<cf:property name="Amount" typeName="decimal" defaultValue="0" nullable="false" />
</cf:entity>
I can not save an instance of this entity with the value 0 for the property Amount, because when calling "Order.Save()" I get the error "Procedure or function 'Order_Save' expects parameter '#Amount', which was not supplied." from SQL-Server.
Everything goes fine if I give the parameter the default value 0 in the stored procedure:
ALTER PROCEDURE [dbo].[Order_Save]
(
#Amount [decimal] (28, 13) = 0,
...
How can I instruct CodeFluent to generate a stored procedure with the default value 0 for the Amount parameter? Or do you know another solution?
Kind regards

You have to set usePersistenceDefaultValue="false":
<cf:entity name="Order">
<cf:property name="Id" />
<cf:property name="Amount" typeName="decimal" nullable="false" usePersistenceDefaultValue="false" />
</cf:entity>
https://www.softfluent.com/documentation/Properties_DefaultValues.html

The root cause of this is related to Object-relational impedance mismatch that CodeFluent Entities tries to fix.
On the database side you can define a nullable column for an integral type (this is not the case here, but the implications are the same)
On the .NET side, you define an integral type that cannot be null.
To fix the null impedance mismatch between those worlds, CodeFluent Entities defines a 'default value' concept on the .NET side. This follows the Sentinel value pattern.
The .NET side 'default value' for this property will be the .NET value that will be equivalent to null on the database side.
By default, this value is 0 for a number (and -1 for an identity number).
So, when you send a 0 to the database, it's equivalent to sending a null.
In the other way, if you store a null in the database, you'll get a 0 in the .NET property.
Note that unlike most ORMs - CodeFluent Entities is not an ORM but it does contain Object to Relational mapping technology - you will not get an error or an exception because you map nullable column to non nullable .NET types, it'll work like a charm, thanks to this default value concept. It's very practical because in the end, it allows us to map non nullable .NET types to nullable database columns. You don't have to use int? to declare a nullable int column (but you can if you want, CodeFluent Entities supports it).
In your case, since the column is not nullable, you logically get an error if you send a .NET 0.
So, you can choose to not use the 'default value' concept (like in meziantou's answer), or also you can define another default value (like defaultValue="-1" for example, or -2 for an identity column), but you'll still get an error if you send this new default value from .NET.

Related

Is there an equivalent to updateSchema="false" that applies to individual entities?

Setting updateSchema="false" in the configuration section of the XML file for the SQL producer will prevent Codefluent from altering the schema (i.e. field names, field type, indexes, etc) of the SQL table while still allowing for the creating of stored procedures and methods against the table. This is discussed in the blog article https://blog.codefluententities.com/2011/10/31/interoperate-with-an-existing-database-using-codefluent-entities/
Question
Is there anything built in that would achieve the same effect but apply only apply to the underlying tables of specific entities?
You can disable table diffs generation for a specific entity using an attribute:
<cf:entity name="Customer" cfps:produceTableDiff="false">
<cf:property name="Id" key="true" />
</cf:entity>

Serialize Emit Default Values

How can I change the EmitDefaultValue in my entities from model?
BOM Producer, even when the dataMember is set to true, is omitting the serialization of null values thus sending incomplete objects to the client application.
[System.Runtime.Serialization.DataMemberAttribute(EmitDefaultValue=false, Order=111)]
You can set the value of EmitDefaultValue at project level or property level:
<cf:project cfsm:emitDefaultValue="true"
xmlns:cfsm="http://www.softfluent.com/codefluent/producers.servicemodel/2007/1">
<cf:property cfsm:emitDefaultValue="true"
xmlns:cfsm="http://www.softfluent.com/codefluent/producers.servicemodel/2007/1">

Entity Framework - entity using a view giving duplicate data

I have the following view (SQL Server 2012 if it matters):
SELECT
EntityId
,EntityType
,StateId
FROM
SomeTable
INNER JOIN SomeOtherTable
When I generate an entity for this view (EF 6 - database first) it looks like this in the EDMX file:
<EntityType Name="VW_MyView">
<Key>
<PropertyRef Name="EntityId" />
<PropertyRef Name="EntityType" />
</Key>
<Property Name="EntityId" Type="Int32" Nullable="false" />
<Property Name="EntityType" Type="String" Nullable="false" MaxLength="2" FixedLength="false" Unicode="false" />
<Property Name="StateId" Type="Int32" />
</EntityType>
As you can see, the model generator created an entity key on the first two columns. The problem is, the first two columns do not guarantee uniqueness.
So for example I could have data like this in the view:
EntityId EntityType StateId
-------- ---------- -------
1234 CR 1
1234 CR 2
1234 CR 3
When I query the data using linq such as:
using (ContextA context = new ContextA())
{
var zList = context.VW_MyView.Where(f => f.EntityId == 1234
&& f.EntityType == "CR").ToList();
}
I get a list of three items, but like this (notice stateid duplicated):
EntityId EntityType StateId
-------- ---------- -------
1234 CR 1 <-- dupe
1234 CR 1 <-- dupe
1234 CR 1 <-- dupe
I migrated this exact same code from EF 4 (object context templates) to EF 6 (dbcontext templates), and before the migration it did not perform like this.
I know I can manually add an EntityKey to the StateId column, and it will work properly, but I have over 100 views in my model and I don't want to go through each one to check.
Why has this behavior changed, and is there a setting I can enable (globally) to correct this?
EDIT:
So based on the answers, I have been able to gather three ways to prevent this issue.
Add all primary key values from each consisting table into the view
Use nullif() tricks in the view to force columns to be non-nullable, and those be added by EF to the key
Manually add the Entity Key in the model myself
But this doesn't explain really why this happens, and how it could possibly be desired behavior? The EF linq query is simply returning entirely incorrect data, without any exceptions or warnings. I can't imagine this is correct.
I have the same "issue" in EF4 (with an .edmx file using the ObjectContext database-first approach) - not sure why it worked for you.
For Entity Framework, if it doesn't have a specified primary key (like on a table), it will fall back to using all non-nullable columns of that object (here: your view) as its compound PK.
These non-nullable columns are now the key for the table/view, and thus, only one value of that key can exist.
In order to resolve this, you need to either include more columns in your view to make the auto-detected key really unique (by including e.g. the primary key of all underlying base tables), or you need to manually set the key properly to something that works for you.
Another solution I found is by setting entity's MergeOption to NoTracking.
using (ContextA context = new ContextA())
{
context.VW_MyView.MergeOption = System.Data.Objects.MergeOption.NoTracking;
//Rest code goes here...
}
Solution found in this thread

ORM to create single entity from more than one database tables

Well tested running system have already defined entity called 'User'.
Now I need to add a new property to User entity (ex: Age)
To do this in the safe way, I do not like to do any changes with the existing data base table, because that is very risky in my case. I need a way to rebuild the User entity with the minimum code changes.
So my proposal is:
Create a new table (user_age), with two columns (user_id, age)
Modify the user entity to add property 'age' and its getter-setters
So my entity (User) properties, will be saved to two different tables (user and user_age)
Loading the user is also similarly.
Is this possible to do with hibernate....??
If not, Any other safer way to do this with Hibernate...?
what are the available ORMs that provide this kind of feature (nhibernate, entityframwork,etc... or any other ORM)...?
Yes, there are various approaches:
[1] See JPA Secondary Tables. This allows you to map an Entity to two or more tables.
Section 2.2.7: http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/#d0e2235
[2] Create another Entity, say UserInfo, mapped to this new table. Create a one-to-one mapping from User to UserInfo.
Yes. You can do that.
I've used for a similar problem a joined-subclass.
Base:
<class name="User" table="Users">
<id name="Code" type="System.Guid">
<column name="Code" />
<generator class="guid.comb" />
</id>
...
</class>
Subclass:
<joined-subclass name="UserExt" extends=User" table="UsersExt">
<key column="Code" />
<property name="Age">
<column name="Age" not-null="true" />
</property>
</joined-subclass>
A good reference here.
NHibernate's join mapping is for exactly this case.
See Ayende's blog and the documentation for more information. From the documentation:
Using the <join> element, it is possible to map properties of one class to several tables, when there's a 1-to-1 relationship between the tables.
From my searches, it looks like it is also possible to do this with Entity Framework: Simon J Ince - Mapping two Tables to one Entity in the Entity Framework . I think this article is about Entity Framework v1, and things could have changed by now, but it appears that there is an important limitation in Entity Framework's version of this mapping:
... it requires a record in each table to exist as the generated SQL uses an INNER JOIN. It makes sense if you're using a new model, but I guess this is more tricky if you're mapping to an existing schema and data.
With NHibernate, you can set the optional attribute on the join mapping to tell it to use outer joins instead of inner joins.
optional (optional - defaults to false): If enabled, NHibernate will insert a row only if the properties defined by this join are non-null and will always use an outer join to retrieve the properties.

Using DB-Default values for unmapped bit columns in entity framework 4 (database first)

I am trying to map two concrete entity types and an abstract base type to the same database table.
The table contains a bit column that does not accept null. Column has a default value: ((0)).
Only one of the two concrete entity types (i.e. concrete type 1) needs to use the column's value (for the other (i.e. concrete type 2) it is always false).
I tried adding a property mapped to that column only to the entity type which requires it
and a
When I call SaveChanges I get an UpdateException, with the following message on its inner-most-exception:
"The column cannot contain null values. [ Column name = MY_BIT_COLUMN,Table name = MY_TABLE ]"
I already edited the SSDL section of the EDMX and changed:
<Property Name="MY_BIT_COLUMN" Type="bit" Nullable="false" />
to:
<Property Name="MY_BIT_COLUMN" Type="bit" Nullable="false" DefaultValue="false" />
(Without this change mapping failed - will not run)
Is there any way to get around this without adding a property mapped to this column to the second concrete entity type or moving it to the base type?
Adding property as protected to concrete type 2 does work, but I would prefer a more elegant workaround.
If your workaround doesn't work (I'm little bit surprised but it is to late for me to test it now) then the only other workaround is changing your inheritance from TPH to TPT or TPC. The problem obviously is that TPH requires all columns in derived types to be nullable.
Other workaround is making your column member of parent entity and in derived entity use custom constructor which will always set it to false.
The last workaround is making your column nullable and enforce the validation in your business logic for the first entity.