EF4: ObjectContext inconsistent when inserting into a view with triggers - exception

I get an Invalid Operation Exception when inserting records in a View that uses “Instead of” triggers in SQL Server with ADO.NET Entity Framework 4.
The error message says:
{"The changes to the database were committed successfully, but an error occurred while updating the object context. The ObjectContext might be in an inconsistent state. Inner exception message: The key-value pairs that define an EntityKey cannot be null or empty. Parameter name: record"}
# at System.Data.Objects.ObjectContext.SaveChanges(SaveOptions options)
at System.Data.Objects.ObjectContext.SaveChanges()
In this simplified example I created two tables, Contacts and Employers, and one view Contacts_x_Employers which allows me to insert or retrieve rows into/from these two tables at once. The Tables only have a Name and an ID attributes and the view is based on a join of both:
CREATE VIEW [dbo].[Contacts_x_Employers]
AS
SELECT dbo.Contacts.ContactName, dbo.Employers.EmployerName
FROM dbo.Contacts INNER JOIN dbo.Employers
ON dbo.Contacts.EmployerID = dbo.Employers.EmployerID
And has this trigger:
Create TRIGGER C_x_E_Inserts
ON Contacts_x_Employers
INSTEAD of INSERT
AS
BEGIN
SET NOCOUNT ON;
insert into Employers (EmployerName)
select i.EmployerName
from inserted i
where not i.EmployerName in
(select EmployerName from Employers)
insert into Contacts (ContactName, EmployerID)
select i.ContactName, e.EmployerID
from inserted i inner join employers e
on i.EmployerName = e.EmployerName;
END
GO
The .NET Code follows:
using (var Context = new TriggersTestEntities())
{
Contacts_x_Employers CE1 = new Contacts_x_Employers();
CE1.ContactName = "J";
CE1.EmployerName = "T";
Contacts_x_Employers CE2 = new Contacts_x_Employers();
CE1.ContactName = "W";
CE1.EmployerName = "C";
Context.Contacts_x_Employers.AddObject(CE1);
Context.Contacts_x_Employers.AddObject(CE2);
Context.SaveChanges(); // line with error
}
 
SSDL and CSDL (the view nodes):
<EntityType Name="Contacts_x_Employers">
<Key>
<PropertyRef Name="ContactName" />
<PropertyRef Name="EmployerName" />
</Key>
<Property Name="ContactName" Type="varchar" Nullable="false" MaxLength="50" />
<Property Name="EmployerName" Type="varchar" Nullable="false" MaxLength="50" />
</EntityType>
<EntityType Name="Contacts_x_Employers">
<Key>
<PropertyRef Name="ContactName" />
<PropertyRef Name="EmployerName" />
</Key>
<Property Name="ContactName" Type="String" Nullable="false" MaxLength="50" Unicode="false" FixedLength="false" />
<Property Name="EmployerName" Type="String" Nullable="false" MaxLength="50" Unicode="false" FixedLength="false" />
</EntityType>
The Visual Studio solution and the SQL Scripts to re-create the whole application can be found in the TestViewTrggers.zip at ftp://JulioSantos.com/files/TriggerBug/.
I appreciate any assistance that can be provided. I already spent days working on this problem.

I stumbled on the same problem when I tried to insert a row in a view with "instead of insert" and "instead of update" triggers.
I think I found a solution: when visual studio's wizard drop your view in your model, it add a StoreGeneratedPattern="Identity" on some properties (probably the keys of your entity).
When generating requests on a regular table, this property tells entity framework to expect an ID in return, so it append a select scope_identity() at the end of the insert.
Now with updatable views the scope_identity is screwed because the insert happen in another scope and it returns null, so the insert fail.
If you remove this StoreGeneratedPattern="Identity" from the model, entity framework doesn't append select scope_identity() and the insert is working fine.
I hope this solve your problem and that it doesn't come too late.
Cheers
More details here : http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/9fe80b08-0b67-4163-9cb0-41dee5115148/

Related

FuzzyLookup in BIML

I'm trying to do the following in BIML:
I'm at a bit of a loss on how to do this in BIML. Here is what I've tried:
<FuzzyLookup
Name="Fuzzy Lookup"
ConnectionName="WO7"
Exhaustive="true"
AutoPassThroughInputColumns="true"
>
<ExternalReferenceTableInput Table="map.AgencyWO7" />
<Inputs>
<Column SourceColumn="AgencyName" TargetColumn="AgencyName" />
</Inputs>
<Outputs>
<Column SourceColumn="AgencyId" TargetColumn="AgencyIdWO7" />
<Column SourceColumn="AgencyName" TargetColumn="AgencyNameWO7" />
</Outputs>
The result is the following error:
(-1,-1) : Error 5 : The input column for the
Fuzzy Lookup Fuzzy Lookup references external column that cannot be found in the reference table. Verify that the
input mapping references a valid column in the reference table.
Property TargetColumn. EmitSsis. There were errors during compilation.
See compiler output for more information.
I think you are maybe missing a reference to the previous transform which is effectively the joining arrow, had you been using SSDT.
Also the format I use to set passthrough = true is on a per column basis.
<FuzzyLookup Name="Fuzzy Lookup" MatchIndexName="" ConnectionName="WO7">
<InputPath OutputPathName="[Previous Transform Name].Output" />
<ExternalReferenceTableInput Table="map.AgencyWO7" />
<Inputs>
<Column MinSimilarity="85" MatchTypeExact="true" PassThrough="true" SourceColumn="AgencyName" TargetColumn="AgencyName" />
</Inputs>
<Outputs>
<Column SourceColumn="AgencyId" TargetColumn="AgencyIdWO7" />
<Column SourceColumn="AgencyName" TargetColumn="AgencyNameWO7" />
</Outputs>
</FuzzyLookup>
Try the above code, and if all else fails you can design the fuzzy look up in SSDT and then import it into biml using Mist/BimlStudio which is pretty reliable.
https://varigence.com/Mist
Cheers

Hibernate JPA+mysql: can not disable caching in createNativeQuery

I am using Hibernate/JPA with mySQL, and because of legacy reasons createNativeQuery at one point. The application works with different servers using the same database, so it should do no caching at all but always show the most recent result. I simulate other servers by changing a value manually in a database-editor, but after a change it always gives old results.
As far as I know I should disable any 2nd level caching (not very important because I do not use any ORM-objects), clear() any 1st level caching, and disable mysql query caching (is already done on database level). Where do I fail, or what do I forget? It drives me crazy.
init(): start of the servlet
entityFactory = Persistence.createEntityManagerFactory("persistence-id");
getEntityManager(): start of each request
destroyEntityManager(); // just in case
entityFactory.getCache().evictAll();
entityManager = entityFactory.createEntityManager();
entityManager.setProperty("javax.persistence.cache.storeMode",
CacheStoreMode.BYPASS);
entityManager.clear(); // just in case
destroyEntityManager(): end of each request
if (entityManager != null) {
if (entityManager.getTransaction().isActive()) {
entityManager.flush();
entityManager.getTransaction().commit();
}
entityManager.clear();
if (entityManager.isOpen()) {
entityManager.close();
}
entityManager = null;
}
destroy(): end of servlet
destroyEntityManager();
if (entityFactory != null) {
entityFactory.close();
}
persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
<persistence-unit name="WallMountBackOffice-PU">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>...</class>
<class>...</class>
<properties>
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" />
<property name="hibernate.connection.url" value="jdbc:mysql://localhost/ourschema" />
<property name="hibernate.connection.username" value="root" />
<property name="hibernate.connection.password" value="" />
<property name="hibernate.connection.pool_size" value="10" />
<property name="hibernate.connection.autocommit" value="false" />
<property name="hibernate.connection.release_mode" value="on_close" />
<property name="dialect" value="org.hibernate.dialect.MySQLDialect" />
<property name="hibernate.cache.use_second_level_cache"
value="false" />
<property name="hibernate.cache.use_query_cache" value="false" />
<property name="javax.persistence.sharedCache.mode" value="NONE" />
<property name="org.hibernate.cacheable" value="false" />
</properties>
</persistence-unit>
The code which does the 'select ...':
...
Query jpaQuery = entityManager.createQuery(query);
entityManager.getTransaction().begin();
jpaQuery.executeUpdate();
entityManager.getTransaction().commit();
In first line, there is a mistake, it must be "BYPASS" not "REFRESH", as following:
query.setHint("javax.persistence.cache.retrieveMode", "BYPASS");
And using the JPA enums instead of string literals is recommended, so it will be:
query.setHint(QueryHints.CACHE_RETRIEVE_MODE, CacheRetrieveMode.BYPASS);
query.setHint(QueryHints.CACHE_STORE_MODE, CacheStoreMode.REFRESH);
You can use setHint() storeMode or retrieveMode method. If you are trying to retrieve the record, use retrieveMode with BYPASS.
For Hibernate
query.setHint("javax.persistence.cache.storeMode", "REFRESH");
query.setHint("javax.persistence.cache.retrieveMode", "REFRESH");
For EclipseLink.
query.setHint("javax.persistence.cache.storeMode", "REFRESH");
query.setHint("javax.persistence.cache.retrieveMode", "REFRESH");
JPA 2.0 Specification
public enum CacheRetrieveMode {
/**
* Read entity data from the cache: this is
* the default behavior.
*/
USE,
/**
* Bypass the cache: get data directly from
* the database.
*/
BYPASS
}
public enum CacheStoreMode {
/**
* Insert/update entity data into cache when read
* from database and when committed into database:
* this is the default behavior. Does not force refresh
* of already cached items when reading from database.
*/
USE,
/**
* Don't insert into cache.
*/
BYPASS,
/**
* Insert/update entity data into cache when read
* from database and when committed into database:
* Forces refresh of cache for items read from database.
*/
REFRESH
}

Using Hibernate with SQL Server 2008

I'm using SQL Server 2008 with Hibernate 3.0 at data access layer of my application. The problem is that I'm unable to read from database.
Here is my Hibernate config;
<property name="connection.driver_class"> com.microsoft.sqlserver.jdbc.SQLServerDriver</property>
<property name="connection.url">jdbc:sqlserver://localhost;databaseName=test;integratedSecurity=true;</property>
<property name="connection.username">not required</property>
<property name="connection.password"></property>
<property name="dialect">org.hibernate.dialect.SQLServer2008Dialect</property
<property name="hibernate.hbm2ddl.auto">update</property>
<mapping resource="user.hbm.xml"/>
Here is user.hbm.xml
<class name="ammar.User" table="user" schema="dbo" catalog="test">
<id name="userId" type="int" column="userId" >
<generator class="assigned"/>
</id>
<property name="userName">
<column name="userName" />
</property>
</class>
And here is code I'm using to get data from database.
SessionFactory sF = new Configuration().configure().buildSessionFactory();
Session session = sF.openSession();
User user = (User) session.get(User.class, 1);
if(user!=null)
System.out.println(user.getUserName());
else
System.out.println("Not Found");
SQLGrammarException as well as SQLServerException occurs when I run this.
Urgent reply is needed.
Solved. Actually 'user' a is reserved word in SQL Server 2008. Anyhow, Thanks to #ManuPK.

Hibernate mapping doesn't allow null timestamp

I am developing a web based application using spring-hibernate combination with Mysql as a database. I have observed 1 problem with hibernate mapping that it doesn't allow me to set null, when type is timestamp. Here is code snippet for better understanding
Here my agenda is - I want to allow user to enter null value for endTime, not for stTime.
schedule.htm.xml
<id
name="id"
type="long"
column="test_run_schedule_id"
>
<generator class="increment" />
</id>
<property
name="testName"
type="java.lang.String"
column="test_run_name"
not-null="true"
length="45"
/>
<property
name="operation"
type="java.lang.String"
column="operation_included"
not-null="true"
length="500"
/>
<property
name="stTime"
type="java.util.Date"
column="start_time"
not-null="true"
length="19"
/>
<property
name="endTime"
type="java.util.Date"
column="end_time"
not-null="false"
length="19"
/>
When I looked into Mysql database, it showing me Columns start_time [timestamp, NOTNULL], as well as end_time [timestamp, NOTNULL] instead of end_time[timestamp, NULL].
So when I insert value, end_time is always taken as CURRENT_TIMESTAMP by default.
How do I create a NULLABLE end_time column?

Linq to SQL Foreign Keys & Collections

I'm a still trying to wrap myself around LINQ to SQL and foreign key relationships.
I have a table called "Parent" and another called "Children". I have a OneToMany relationship, where a Parent can have multiple Children. My DBML looks something like:
<Table Name="" Member="Parents">
<Type Name="Parent">
<Column Member="ParentID" Type="System.String" IsPrimaryKey="true" CanBeNull="false" />
<Column Member="ChildID" Type="System.String" CanBeNull="false" />
<Association Name="Parent_Child" Member="Childs" ThisKey="ParentID" OtherKey="ParentID" Type="Child" />
</Type>
</Table>
<Table Name="" Member="Childs">
<Type Name="Child">
<Column Member="ChildID" Type="System.String" IsPrimaryKey="true" CanBeNull="false" />
<Column Member="ParentID" Type="System.String" CanBeNull="false" />
<Association Name="Parent_Child" Member="Parent" ThisKey="ParentID" OtherKey="ParentID" Type="Parent" IsForeignKey="true" />
</Type>
</Table>
In my code, I would like do to something like:
// parent has already been loaded from the datacontext
parent.Childs = <some collection of children>
db.SubmitChanges();
But when I do that I get the following error:
A member defining the identity of the object cannot be changed.
Consider adding a new object with new identity and deleting the existing one instead.
Can anyone tell me how to properly accomplish this?
this is actually the error of datacontext,i think u have multiple instance of datacontext..
well u can not add any entity in datacontext witch is already fetched through another instance of datacontext...
even u can so it by setting datacontext.objecttrackingenabled to false and then u can add it to another data context then it will work for sure....