Web API 2.2 OData V4 not recognizing primary key - entity-framework

In Web API 2.2 WebApiConfig.cs:
ODataModelBuilder builder=new ODataConventionModelBuilder();
builder.EntitySet<Customer>("Customers")
Customer entity is generated by the entity data model wizard. "EF Designer from database"
When i try to load the entity it returns error "Customer entity has no key"
Even though the CSDL file specifies the primary key and it works with Web API actions
<Key>
<PropertyRef Name="CustomerID">
</Key>
...
The workaround has been to manually add the [Key] annotation attribute in the model.
QUESTION: Is this a bug? How can I resolve this issue, without having to manually edit anything to specify the primary key for each entity?

In Your CSDL example you are not closing the PropertyRef tag with "/>". This XML syntax error could make to ignore the Customer Key.
<Key>
<PropertyRef Name="CustomerID" />
</Key>
...

Related

Entity Framework EDMX Diagram table missing properties after update

I'm maintaining an asp.Net MVC 3 web application using Entity Framework 4.1 Database first approach. I have had to add two new properties to one of the tables in the database.
I then right clicked on the EDMX diagram and choose 'Update Model from Database' within the context menu, like so:
This works without any problems, but the two new fields aren't added to the expected table. However, when I open the EDMX file in XML format, I can see the two new fields listed, like so:
<EntityType Name="Shift">
<Key>
<PropertyRef Name="shiftID" />
</Key>
<Property Name="shiftID" Type="int" Nullable="false" StoreGeneratedPattern="Identity" />
//Two new fields below
<Property Name="shiftTitleGradeID" Type="int" />
<Property Name="shiftTitleSubID" Type="int" />
</EntityType>
Can anyone advise me on how to get the two new fields into my EDMX diagram and not just the XML file?
Thanks in advance.
Got it fixed. I had to delete all references to the new properties in the XML file. Then recreate the update model from database procedure, this time ensuring the two checkboxes where ticked, Pluralize or singularize generated object names, and, Include foreign key columns in the model (I thought I had these ticked the first time, but I didn't).
This fixed my problem.

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.

NHibernate duplicate insert in many-to-many and composite-element

I have in an NHibernate 2 application an Product entity which has an many-to-many relationship to an Location. The Location entity does not have any navigation properties or mappings back to the product. It is mapped like this:
<bag name="Locations" table="ProductLocation" cascade="none">
<key column="ProductId" />
<many-to-many column="LocationId" class="Location"/>
</bag>
The product also has an composite-element, a Component with a concentration mapped via the ProductComponent class. The class has no navigation property or mapping back to the product.
<bag name="ProductComponents" table="ProductComponent" access="nosetter.camelcase">
<key column="ProductId" />
<composite-element class="ProductComponent">
<property name="Concentration"/>
<many-to-one name="Component" column="ComponentId" access="nosetter.camelcase"/>
</composite-element>
</bag>
This all works fine when just inserting one product at a time. It however fails when batch inserting multiple products.
While the products itself get inserted fine, each product does get an own unique Id, the elements in the many-to-many (Locations) and composite-element (ProductComponent) doesn't get inserted well. This is because NHibernate multiple times executes the insert to the ProductLocation table with the same ProductId.
This causes an duplicate record in the link table. How can this be prevented?
You'll have to define one site of the relationship to be the owner so that only one side does the insert. This can be achieved with Inverse set to true on the other side.
Find a more detailed explanation here

Error 3023 using Entity Framework

Using the Entity Framework, I've modeled a fairly simple database schema with an ever-so-slightly more complex class hierarchy. In two places, I'm using single table inheritance with a single NVARCHAR(20) NOT NULL discriminator column. In one of those two places, it works great, no issues. But in the other place, with an almost identical pattern, I get the following error:
Error 3023: Problem in Mapping Fragments starting at lines 371, 375, 379, 382: Column MediaStream.MediaStreamTypeID has no default value and is not nullable. A column value is required to store entity data.
An Entity with Key (PK) will not round-trip when:
((PK does NOT play Role 'MediaStream' in AssociationSet 'FK_MediaStream_SessionID' OR PK is NOT in 'MediaStream' EntitySet OR Entity is type [SlideLinc.Model].MediaStream) AND (PK plays Role 'MediaStream' in AssociationSet 'FK_MediaStream_SessionID' OR PK is NOT in 'MediaStream' EntitySet OR Entity is type [SlideLinc.Model].MediaStream) AND (PK plays Role 'MediaStream' in AssociationSet 'FK_MediaStream_SessionID' OR PK is in 'MediaStream' EntitySet))
Here's the table definition (not including various indexes, foreign keys, etc.):
CREATE TABLE [dbo].MediaStream(
[MediaStreamID] UNIQUEIDENTIFIER NOT NULL,
[SessionID] UNIQUEIDENTIFIER NOT NULL,
[RtmpUri] nvarchar(250) NOT NULL,
[MediaStreamTypeID] nvarchar(20) NOT NULL,
CONSTRAINT PK_MediaStream PRIMARY KEY CLUSTERED
(
[MediaStreamID] ASC
)
I'm using the MediaStreamtypeID column as the discriminator: if it's set to "video", a VideoMediaStream class should be created, and if it's set to "audio", an AudioMediaStream class should be created.
The relevant portions of the EDMX file look like this:
<EntitySetMapping Name="MediaStream">
<EntityTypeMapping TypeName="IsTypeOf(SlideLinc.Model.MediaStream)">
<MappingFragment StoreEntitySet="MediaStream">
<ScalarProperty Name="RtmpUri" ColumnName="RtmpUri" />
<ScalarProperty Name="MediaStreamID" ColumnName="MediaStreamID" /></MappingFragment></EntityTypeMapping>
<EntityTypeMapping TypeName="IsTypeOf(SlideLinc.Model.VideoMediaStream)">
<MappingFragment StoreEntitySet="MediaStream" >
<ScalarProperty Name="MediaStreamID" ColumnName="MediaStreamID" />
<Condition ColumnName="MediaStreamTypeID" Value="video" /></MappingFragment></EntityTypeMapping>
<EntityTypeMapping TypeName="IsTypeOf(SlideLinc.Model.AudioMediaStream)">
<MappingFragment StoreEntitySet="MediaStream" >
<ScalarProperty Name="MediaStreamID" ColumnName="MediaStreamID" />
<Condition ColumnName="MediaStreamTypeID" Value="audio" /></MappingFragment></EntityTypeMapping></EntitySetMapping>
<AssociationSetMapping Name="FK_MediaStream_SessionID" TypeName="SlideLinc.Model.FK_MediaStream_SessionID" StoreEntitySet="MediaStream">
<EndProperty Name="MediaStream">
<ScalarProperty Name="MediaStreamID" ColumnName="MediaStreamID" /></EndProperty>
<EndProperty Name="Session">
<ScalarProperty Name="SessionID" ColumnName="SessionID" /></EndProperty></AssociationSetMapping>
So there are multiple things about this error that I don't get:
(1) Why does exactly this same approach work for my other class hierarchy, but not this one? I thought it might be the Entity Designer getting confused, so I deleted this portion of my hierarchy (in the XML), and recreated it, but I'm still getting it. I could try recreating the whole damn thing, but hell, that's a lot of work, and if I'm gonna have to be doing this very often, that doesn't leave a great taste in my mouth about the entity framework.
(2) What is it complaining about in the first place? I don't get how MediaStreamTypeID (which isn't a member of the primary key) has anything to do with the primary key at all, or why the fact that it can't be null is a problem (especially given that this same setup works elsewhere in my model!).
Any thoughts or suggestions?
I had a similar problem, and was able to solve it by setting the "Abstract" property for the base class to "True" and by removing the discriminator column from the base class in the model (either in the *.edmx file or in the designer view within Visual Studio).
I got the exactly same error but maybe with different reason from yours.
In the code below, I mis-copied some codes so 2 of inherited classes (LeveledItem and Team) are of the same "Type".
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.SetInitializer(new ScrumDbContextInitializer());
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<LeveledItem>()
.Map<LeveledItem>(m => m.Requires("Type").HasValue(typeof(LeveledItem).Name))
.Map<Team>(m => m.Requires("Type").HasValue(typeof(LeveledItem).Name))
.Map<Story>(m => m.Requires("Type").HasValue(typeof(Story).Name))
.Map<Task>(m => m.Requires("Type").HasValue(typeof(Task).Name)) .Map<Sprint>(m => m.Requires("Type").HasValue(typeof(Sprint).Name));
After the second one was changed to "typof(Team).Name", the error was fixed.
I had a similar problem to this, just out of interest, does deleting the .EDMX file and recreating it from scratch solve your problem?
What caused the problem:
Created a set of tables with some 0..* mappings
Generated the Entity Framework class, customised a whole bunch of Navigation properties
Went back to the DB and changed the cardinality of some of the 0..* to 1..* relationships - ie: set some of the FKs to !nullable
Updated the Entity Framework
Recompiled and BOOM I got a compilation error similar to yours
There seems to be an issue where the "Update Model from Database" command doesn't update the changed relationships correctly.
The solution was to open the EDMX file, look for the <Association Name="FK_XXX_XXX"> elements that were generated the first time and change the Multiplicity attribute on the relevant End point from Multiplicity="0..1" to Multiplicity="1"
I encountered this error when I inadvertently mapped 2 classes to the same table via the [Table] attribute (same effect via modelBuilder ToTable())