Two entities mapped to the same rows - entity-framework

I'm getting the following error on my database first model in Entity Framework:
Error 3032: Problem in mapping fragments starting at lines 3434,
4312:EntityTypes Model.Docent, Model.Student are being mapped to the
same rows in table Attendee. Mapping conditions can be used to
distinguish the rows that these types are mapped to.
While I already added conditions to these models:
<EntityTypeMapping TypeName="IsTypeOf(Model.Student)">
<MappingFragment StoreEntitySet="Attendee">
<ScalarProperty Name="Id" ColumnName="atnId" />
<Condition ColumnName="atnTypeId" Value="1" />
</MappingFragment>
</EntityTypeMapping>
And
<EntityTypeMapping TypeName="IsTypeOf(Model.Docent)">
<MappingFragment StoreEntitySet="Attendee">
<ScalarProperty Name="AvailabilityApprovedByType" ColumnName="atnAvailabilityApprovedByAttId" />
<ScalarProperty Name="Id" ColumnName="atnId" />
<Condition ColumnName="atnTypeId" Value="2" />
</MappingFragment>
</EntityTypeMapping>
Their is a more complex hierarchy, possibly that's the problem. But I'm unsure how to proceed. This a the hierarchy:
Attendee (Abstract)
-> Facility (Type = 3)
-> AttendeeCollection (Abstract)
-> Team (Type = 4)
-> Group (Type = 5)
-> Person (Abstract)
-> Student (Type = 1)
-> Docent (Type = 2)

Well I figured out the problem. The Person entity had mapped scalar properties and associations. The associations where the problem. Because they could be of two types. I could not write in a condition for them because they could map to two properties. So I removed the scalar properties and the table mapping for the Person class altogether.
After that I implemented private versions of these scalar properties on the Docent en Student class. And exposed them via a partial implementation. Where I added them as abstract to the Person class.
I hope this is clear and helps somebody else. Possibly somebody else can write it down more legible.

Related

Create an Entity Type and an Association from the same table

Is it possible to have both an entity type and a many-many association backed by the same table? I’ve tried a couple different approaches and they all fail with different errors.
First I tried creating the model from the database then creating an association in the designer.
It gives me the proper Foos and Bars navigation properties, but it's not backed by any table and fails when I try to execute a query.
Model1.msl(3,4) : error 3027: No mapping specified for the following EntitySet/AssociationSet - BarFoo.
So I tried mapping the association to the FooBar table in the designer. Only FooREF and BarREF are part of the association, but EF expects the primary key to be mapped as well.
ERROR (3025): Problem in Mapping Fragment starting at line 32: Must specify mapping for all key properties (FooBar.ObjectID) of table FooBar.
Since I’m not able to map the ObjectID and RowVersion, I tried to exclude them by using a QueryView in the edmx.
<edmx:Mappings>
<Mapping Space="C-S" xmlns="http://schemas.microsoft.com/ado/2009/11/mapping/cs">
<EntityContainerMapping StorageEntityContainer="TestModelStoreContainer" CdmEntityContainer="TestEntities">
<EntitySetMapping Name="Bars">
<EntityTypeMapping TypeName="TestModel.Bar">
<MappingFragment StoreEntitySet="Bar">
<ScalarProperty Name="ObjectID" ColumnName="ObjectID" />
<ScalarProperty Name="Count" ColumnName="Count" />
<ScalarProperty Name="RowVersion" ColumnName="RowVersion" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
<EntitySetMapping Name="Foos">
<EntityTypeMapping TypeName="TestModel.Foo">
<MappingFragment StoreEntitySet="Foo">
<ScalarProperty Name="ObjectID" ColumnName="ObjectID" />
<ScalarProperty Name="Name" ColumnName="Name" />
<ScalarProperty Name="RowVersion" ColumnName="RowVersion" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
<EntitySetMapping Name="FooBars">
<EntityTypeMapping TypeName="TestModel.FooBar">
<MappingFragment StoreEntitySet="FooBar">
<ScalarProperty Name="ObjectID" ColumnName="ObjectID" />
<ScalarProperty Name="RowVersion" ColumnName="RowVersion" />
<ScalarProperty Name="BarREF" ColumnName="BarREF" />
<ScalarProperty Name="FooREF" ColumnName="FooREF" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
<AssociationSetMapping Name="BarFoo" TypeName="TestModel.BarFoo" >
<EndProperty Name="Foo">
<ScalarProperty Name="ObjectID" ColumnName="FooREF" />
</EndProperty>
<EndProperty Name="Bar">
<ScalarProperty Name="ObjectID" ColumnName="BarREF" />
</EndProperty>
<QueryView>
SELECT VALUE TestModel.BarFoo(CREATEREF(TestEntities.Foos, row(f.FooREF), TestModel.Foo), CREATEREF(TestEntities.Bars, row(f.BarREF), TestModel.Bar))
FROM TestModelStoreContainer.FooBar AS f
</QueryView>
</AssociationSetMapping>
</EntityContainerMapping>
</Mapping>
</edmx:Mappings>
But despite the QueryView element being documented, its addition causes the schema validation to fail.
MappingException: Schema specified is not valid. Errors:
<File Unknown>(39,8) : error 2025: XML Schema validation failed for mapping schema. Schema Error Information : The element 'AssociationSetMapping' in namespace 'http://schemas.microsoft.com/ado/2009/11/mapping/cs' has invalid child element 'QueryView' in namespace 'http://schemas.microsoft.com/ado/2009/11/mapping/cs'. List of possible elements expected: 'Condition, ModificationFunctionMapping' in namespace 'http://schemas.microsoft.com/ado/2009/11/mapping/cs'..
Is there a strategy I'm missing here? In the end I'd like to be able to treat FooBar as an entity while also keeping the Foos and Bars navigation properties. Is this possible?

EF 6 query contains unmapped columns, breaking the query

I have an EDMX (Entity Framework 6.1.3) that I'm using to query two different databases. There are some minor differences between the databases but I only want the common columns. I generated the EDMX from Database A, and removed the columns that were not in Database B from the Diagram and regenerated the code.
If I query database B the query contains the columns I removed, although the final SELECT does not. This means that the query fails.
The table mapping shows the columns, but with nothing on the Value/Property side:
The exception is:
System.Data.Entity.Core.EntityCommandExecutionException : An error occurred while executing the command definition. See the inner exception for details.
----> System.Data.SqlClient.SqlException : Invalid column name 'ValidFromDate'.
Invalid column name 'ValidToDate'.
Invalid column name 'LastPulled'.
Invalid column name 'IsCurrent'.
The query that is being sent to the server is:
SELECT TOP (1)
[c].[FirstName] AS [FirstName],
[c].[LastName] AS [LastName],
[c].[HomePhone] AS [HomePhone],
[c].[WorkPhone] AS [WorkPhone],
[c].[MobilePhone] AS [MobilePhone],
[c].[Email] AS [Email],
[c].[Fax] AS [Fax]
FROM (SELECT
[Person].[FirstName] AS [FirstName],
[Person].[LastName] AS [LastName],
[Person].[HomePhone] AS [HomePhone],
[Person].[WorkPhone] AS [WorkPhone],
[Person].[MobilePhone] AS [MobilePhone],
[Person].[Email] AS [Email],
[Person].[Fax] AS [Fax],
[Person].[ValidFromDate] AS [ValidFromDate],
[Person].[ValidToDate] AS [ValidToDate],
[Person].[LastPulled] AS [LastPulled],
[Person].[IsCurrent] AS [IsCurrent]
FROM [dbo].[Person] AS [Person]) AS [c]
As you can see there is an inner-query which contains the additional columns.
At this point I'm kind of stumped as to why this is happening. How do I remove these columns from both sides of the mapping, or otherwise stop EF from putting unwanted columns in ANY part of the query?
When you use the EDMX designer and simply delete a column from an entity - this does not fully remove the column from the EDMX. Assuming you truly want it gone, you can open the EDMX file with a text editor and remove it by hand. To make sure that your manual changes trigger a rebuild of your auto-generated classes, edit it in Visual Studio and you shouldn't have an issue.
Right-click the EDMX in solution explorer
Open With...
XML (Text) Editor
I would expect if you were to open the EDMX, you would find something that looks like:
<EntityType Name="Person">
<Property Name="FirstName" Type="varchar" />
<Property Name="LastName" Type="varchar" />
<Property Name="HomePhone" Type="varchar" />
<Property Name="WorkPhone" Type="varchar" />
<Property Name="MobilePhone" Type="varchar" />
<Property Name="Email" Type="varchar" />
<Property Name="Fax" Type="varchar" />
<Property Name="ValidFromDate" Type="datetime" />
<Property Name="ValidToDate" Type="datetime" />
<Property Name="LastPulled" Type="datetime" />
<Property Name="IsCurrent" Type="short" />
</EntityType>
And you would just remove the bottom 4 columns, save and close, and rebuild the project.
EDIT: In case anyone in the future references this answer, if you do not first delete the column in the designer (like the OP did in this case), there will be two other instances of the column(s) in the XML that you would also need to remove in order for it to compile.
Thanks to #Borophyll for pointing me in roughly the right direction. Although their answer was not the solution to my issue, it did allow me to see the actual issue.
in the EDMX file there is also a entry that looks like this:
<EntitySet Name="Person" EntityType="Self.Person" store:Type="Tables" store:Schema="dbo">
<DefiningQuery>SELECT
[Person].[FirstName] AS [FirstName],
[Person].[LastName] AS [LastName],
[Person].[HomePhone] AS [HomePhone],
[Person].[WorkPhone] AS [WorkPhone],
[Person].[MobilePhone] AS [MobilePhone],
[Person].[Email] AS [Email],
[Person].[Fax] AS [Fax],
[Person].[ValidFromDate] AS [ValidFromDate],
[Person].[ValidToDate] AS [ValidToDate],
[Person].[LastPulled] AS [LastPulled],
[Person].[IsCurrent] AS [IsCurrent]
FROM [dbo].[Person] AS [Person]</DefiningQuery>
</EntitySet>
And that's where the weird sub-query was coming. I think the reason is that the table has no primary key as it is part of a staging database.
I just deleted from the SELECT statement the last four columns and everything worked.

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.

Table per hierarchy layout problems in EF 4.1 with multiple nullable discriminators

I have a table with an int PK, one NOT NULL field, and two NULL string fields.
When I go and set up a TPH-style design in EF, I set it up this way:
The top level type only has the PK and the NOT NULL field.
The first level checks the first nullable field as a discriminator. The not null resulting type is abstract. I map the field accordingly.
I do this again for the second field, again mapping where not null. I set nullable = false on the fields I map.
<EntitySetMapping Name="Items">
<EntityTypeMapping TypeName="IsTypeOf(Model1.Item)">
<MappingFragment StoreEntitySet="Items">
<ScalarProperty Name="ID" ColumnName="ID" />
<ScalarProperty Name="OtherID" ColumnName="OtherID" />
</MappingFragment>
</EntityTypeMapping>
<EntityTypeMapping TypeName="IsTypeOf(Model1.BothNullItem)">
<MappingFragment StoreEntitySet="Items">
<ScalarProperty Name="ID" ColumnName="ID" />
<Condition ColumnName="FirstNullField" IsNull="true" />
<Condition ColumnName="NullField2" IsNull="true" />
</MappingFragment>
</EntityTypeMapping>
<EntityTypeMapping TypeName="IsTypeOf(Model1.FirstFieldNull)">
<MappingFragment StoreEntitySet="Items">
<ScalarProperty Name="ID" ColumnName="ID" />
<ScalarProperty Name=FirstNullField" ColumnName="FirstNullField" />
<Condition ColumnName="FirstNullField" IsNull="false" />
</MappingFragment>
</EntityTypeMapping>
<EntityTypeMapping TypeName="IsTypeOf(Model1.NotNullSubItem1)">
<MappingFragment StoreEntitySet="Items">
<ScalarProperty Name="ID" ColumnName="ID" />
<Condition ColumnName="NullField2" IsNull="true" />
</MappingFragment>
</EntityTypeMapping>
<EntityTypeMapping TypeName="IsTypeOf(Model1.NotNullSubItem2)">
<MappingFragment StoreEntitySet="Items">
<ScalarProperty Name="ID" ColumnName="ID" />
<ScalarProperty Name="NullField2" ColumnName="NullField2" />
<Condition ColumnName="NullField2" IsNull="false" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
I'm getting "two entities with different keys are mapped to the same row."
I am thinking it's because maybe the first item's not null attribute is not getting inherited by the children.
I've created a third type to try to take care of the case of NullField1 being null and NullField2 not being null (which will not happen in my DB, I have a constraint), but even if I added it it doesn't work.
Explicitly adding the not null condition on either of the 2 sub children requires me to map the column, which still doesn't work even if I map it to some extraneous property.
None of the combinations of conditions for "BothNullItem" works either.
Any ideas?
Welp, I ended up giving up on trying to get discriminator columns to propagate through inheritance so I created 3 views, one top level, one for null field1 and not null field one, and then on not null field one, I make field2 the discriminator column in EF. This sounds like a crappy answer to my problem so I'd like to hear from someone in the know what the deal is. Buehler?
UPDATE: FIXED! Going off of the horizontal partitioning concept in one of the articles I found on msdn (http://msdn.microsoft.com/en-us/library/cc716779.aspx)I went in and manually added the conditions in the msl. It looks like you can't have this work automatically from the designer. For those of you wondering, open the edmx in the xml editer and go down and look at the conditions elements of your mappings. Add in extra conditions where needed (apparently conditions don't inherit from parent entities so you have to manually add them to the children) and compile!

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.