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
Related
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
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.
I have a View which looks similar to this:
SELECT Id, Name
FROM Users
UNION ALL
SELECT NULL as [Id], NULL as [Name]
When I try to map to this view in Entity Framework, it just fails. I don't get an error, but the view does not exist in my data store. Why is this? Is there a way around it?
I know this is an older question already marked as answered, but I wanted to post an alternative to editing the edmx. I learned this one the hard way, after tons of Google searches and pulling my hair out for hours, trying different options...
For a view, EF attempts to to infer a primary key by identifying columns that are non-nullable
and non-binary (see Create an Entity Key When No Key Is Inferred).
With a view used to flatten related data for lookup purposes, this can result in many columns (or the wrong ones) being inferred as keys.
For a view with a UNION, it can cause the opposite problem, because there may be no true identity column that can be safely included as a key.
To force EF to identify columns as a key, you can use ISNULL() to ensure the value is non-nullable: ISNULL(column_name, replacement_value)
To force EF to not mark a column as a key, you can use NULLIF() to make it nullable: NULLIF(column_name, value_not_equal_to_parameter_1)
If you need to ensure a value is returned, but don't want to have it marked as a key, I believe COALESCE(column_name, replacement_value) will do the job of ISNULL without EF marking the column as a key
If there is truly no viable column available as a primary key (as in a UNION view), you can fake a non-nullable PK through the use of ISNULL() combined with ROW_NUMBER() in your SELECT statement: SELECT ISNULL(ROW_NUMBER() OVER (ORDER BY sort_criteria), 0) as 'ALIASNAME'
As an alternative, the edmx can absolutely be edited directly as Marcos Prieto suggested, but you run the risk of having those changes overwritten the next time you run "Update Model from Database".
Hope this helps anyone who encounters this in the future!
It is because Visual Studio cannot infers the Primary Key of your View.
You can see the error message within edmx file by open it with XML editor and see the SSDL section.
Here is error message that results from my Model(which I created some View like yours within my Database just to emulate) :
Errors Found During Generation:
warning 6013: The table/view 'PhoneBook.dbo.ContactCustomer' does not have
a primary key defined and no valid primary key could be inferred.
This table/view has been excluded. To use the entity,
you will need to review your schema,
add the correct keys, and uncomment it.
It is not true that Union is not supported in EF 4.
But I think the problem is that Visual Studio saw your view as the odd View.
You can doing some experiment by create another View and compares them (using update model from database menu within model designer).
And you can modify the Model by hand (manual typing the edmx file) to define the Primary Key to resolve this.
I had a view that worked perfectly. I modified it (changed the view on a union of 2 tables), updated the model from database and this problem appeared.
I fixed it in 3 steps:
Open the .edmx file with a XML editor.
Uncomment the view's EntityType XML (edmx:StorageModels > Schema) code and add the Key:
<EntityType Name="your_view">
<Key>
<PropertyRef Name="your_id" />
</Key>
<Property Name="your_id" Type="int" Nullable="false" />
<Property Name="other_field" Type="varchar" MaxLength="45" />
</EntityType>
Be sure that EF didn't erased the view in edmx:StorageModels > Schema > EntityContainer (if you have code repository, copy the code from there):
<EntitySet Name="your_view" EntityType="Your_Model.Store.your_view" store:Type="Views" store:Schema="your_schema" store:Name="your_view">
<DefiningQuery>SELECT
`your_view`.`your_id`,
`your_view`.`other_field`,
FROM `your_view` AS `your_view`
</DefiningQuery>
</EntitySet>
I know this is an old question but i faced this issue recently & after trying to do the above mentioned methods i simply created another view that selects from the Union view & i was able to map the new view by updating my entity model.
Error 3007: Problem in Mapping Fragments starting at lines 186, 205: Non-Primary-Key column(s) [WheelID] are being mapped in both fragments to different conceptual side properties - data inconsistency is possible because the corresponding conceptual side properties can be independently modified.
I found several places on the web describing this error, but I simply don't understand them. (confused smiley goes here)
One
Two
Three
Four
There is something pretty fundamental here, I must be missing. Can you explain it, so that I understand it? Maybe using my real life example below?
Foreign key 1:N Wheels.Id -> Slices.WheelId
I add them to entity framework, and WheelId is not visible in the Slices-entity.
Doing some workaround (deleting the relationship from the db before adding tables to EF - then re-creating it and updating EF) I managed to get the WheelId to stay in Slices, but then I get the error mentioned at the top.
Since Slices.WheelId is an FK, you cannot expose it in your client model, period. There are ways to get the value, though.
var wheelId = someSlice.Wheels.ID;
Update In EF 4 you can do this by using FK Associations instead of independent associations.
Try to remove foreign property column from Entity set using entity model design it will solve your problem
For example
We have two tables one is customer and other one is order, using entity model design we added association between customers and orders when we do this Ado.net entity framework i will add navigation properties to both below tables.
Like
Customer.Orders - Here order is list
Order.Customer
One - Many relation.
So we need to remove property from with name CustomerId[Foreign key column] from Order entity set.
For reference:
http://social.msdn.microsoft.com/forums/en-US/adodotnetentityframework/thread/2823634f-9dd1-4547-93b5-17bb8a882ac2/
I was able to overcome this problem by the following steps:
right click the designer window
Select 'update model from database'
Select Add AND make sure that the 'Include foreign key columns in the model' checkbox is selected.
click on Finish...
I had set foreign keys up in the database but framework still wasn't pulling them in correctly. So I tried to add the association myself.
However, when I did this I would get a mapping error. It took me A WHILE but I figured out. What I did was set up the association using the entity toolbox association tool and then you have to double click on the association (1 to many) line and set the primary and foreign key there. Hopefully, this to help others who might have the same problem. I couldn't find the answer anywhere.
I had this problem for quite a different reason, and the message was slightly different; it didn't say "data inconsistency is possible because the corresponding conceptual side properties can be independently modified."
I have a table involved in my model with a binary column where I store image data. I only want this data returned when I need it (performance is a feature), so I split the table using a method similar to this. Later on, I added a property to that table, then updated the model from the database. The wizard added the property to both entity types that refer to the table with the added property. I had to delete it from one of them to solve the error.
I've had this happen because Entity Framework Update wizard mismapped some keys (or did not update?). As a result, some columns were mistakenly labeled as keys, while actual key columns were treated as plain columns.
The solution was to manually open EDMX file, find the entities, and update the keys.
Couldn't get any of the answer to work with EF6. The problem seems to be the framework doesn't import the foreign keys correctly as Associations. My solution was removing foreign keys from the tables, and then manually adding the associations using Entity Framework model, using the following steps: Entity Framework - Add Navigation Property Manually
For LinQ to Entities queries in EF1, my workaround for not having access to the foreign key as a property is with the following code, which does not produce a join query to the associated table:
dbContext.Table1s.FirstOrDefault(c => (int?)c.Table2.Id == null)
i.e, the generated SQL is:
...WHERE ([Extent1].[Table2Id] IS NULL)...
Solution is to allow deleting Rule = Cascade on Sql association.
Same thing as to be done on .edmx model, adding element to
association:
<Association Name="FK_Wheels_Slices">
<End Role="Wheels" Type= "your tipe here" Multiplicity="1">
<OnDelete Action="Cascade" />
</End>
</Association>
I had a table already mapped in EF. I added two more tables which had foreign keys in the previously added table. I then got the 3007 error.
To fix the error I deleted all three tables from the EDMX file, and then re-added them all at once together (via "Update Model from Database..."), instead of in stages.
I checked my Error List window and noticed I had errors in the model. Fixed them and all is well
in my case I solved this error by tick (include foreign key columns in the model)
- update Model from database
- tick (include foreign key columns in the model)
- finish
I'm trying to filter entities based on their store types (either table or view).
I have 2 entities in my test project, one's source is a table and the other's source is a view.
<EntitySet Name="Test" EntityType="TestModel.Store.Test" store:Type="Tables" Schema="dbo" />
<EntitySet Name="TestView" EntityType="TestModel.Store.TestView" store:Type="Views" store:Schema="dbo" store:Name="TestView">
The code sample above is taken from model's edmx file's SSDL section.
I think the store:Type information in SSDL is what i need but i couldn't find a way to retrieve that value using entity-framework api.
Any help will be appreciated.
Well you can query the metadata in the SSDL by looking in the SSpace or the StoreItemCollection.
i.e.
var sspaceEntitySets = context.MetadataWorkspace
.GetItems<EntityContainer>(DataSpace.SSpace)
.First().BaseEntitySets.OfType<EntitySet>();
var entitySet = sspaceEntitySets.First();
var tableType = entitySet
.MetadataProperties["http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator:Type"]
.Value.ToString();
Unfortunately this isn't going to help you tie your classes to whether they come from a table or a view. Because the Entities (i.e. the ones you code against in CSpace) rather than the ones that describe the shapes of the table (i.e. SSpace ones) are in CSpace and in order to know whether an Entity comes from a view or table, you would need to be able to get from the CSpace EntitySet to SSpace EntitySet via the Mapping.
Unfortunately the EF doesn't expose public CSSPace (i.e. there is no way to use the API to read the MSL fragment of the EDMX).
So in order to do this you would have to manually reason over the MSL element, probably using LINQ to XML or something.
Hope this helps
Alex