Exception when using static dbcontext with entitydatasource with onContextCreating - entity-framework

I want to share a single dbcontext across a session. I understand there are a number of issues with using shared, cached Dbcontexts.
So I create a global dbcontext and save it in a static class variable.
I am able create select, create new entities, do inserts and updates via the dbcontext in code behind calls without a problem
I want to use the same dbcontext in my entitydatasource controls within my webforms, for concurrency reasons. I do this by using the OnContextCreating and ONContextdisposing Events. This works fine for reading data but when I try and do an update via a entitydatasource (from a dataview, gridview etc) I get the following exception
AcceptChanges cannot continue because the object's key values conflict with
another object in the ObjectStateManager. Make sure that the key values are unique
before calling AcceptChanges.
I have reduced the system producing the error to a ef5.0 datamodel, using a single table and a single webform page with a single enitydatasource. I have reproduced the code below
public partial class _Default : Page
{
static QuantumDataEntities dbcontext = null;
protected void Page_Load(object sender, EventArgs e)
{
}
protected void OnContextCreating(object sender, EntityDataSourceContextCreatingEventArgs e)
{
if (dbcontext == null)
dbcontext = new QuantumDataEntities();
e.Context = (dbcontext as IObjectContextAdapter).ObjectContext;
}
protected void OnContextDisposing(object sender, EntityDataSourceContextDisposingEventArgs e)
{
e.Cancel = true;
}
}
As you can see it is very simple. Whenever the Entitydatasource needs an objectcontext to read,update etc, it calls OnContextCreating. I am creating the dbcontext on the first OnContextCreating call, caching in a static class variable and reusing for future OnContextCreating calls. The OnContextCreating event is call a few times, first for page load, next for the post back when the edit button is pressed and finally (I think) for the actually dbsave.
If I get ride of the caching and just create a new dbcontext for each call it works fine. i.e
replace
if (dbcontext == null)
dbcontext = new QuantumDataEntities();
e.Context = (dbcontext as IObjectContextAdapter).ObjectContext;
with
dbcontext = new QuantumDataEntities();
e.Context = (dbcontext as IObjectContextAdapter).ObjectContext;
There is some other weirdness with this error. If I set up a OnUpdating event, that is supposed to get called before any updates are performed by the entitydataosource, it never gets called.
I have included the stack trace for the exception and the end of this text.
I would have like to test this with EF 6.0 Alpha 3, but the designers have moved the object context
into another assembly making it non compatible with entitydatasouce
This is a major roadblock to further development of our site. The only choice we have now is to move to a non shared dbcontext, which will cause a extreme performance issues (i.e loading the same 20K data set mutliple times on a page load - yes we can re-architect, we it will take weeks/months, which we dont have right now)
Questions ?
What is going on ?
Am I doing something wrong ? ( as opposed to poor architecture)
exception stack trace
[InvalidOperationException: AcceptChanges cannot continue because the object's key values conflict with another object in the ObjectStateManager. Make sure that the key values are unique before calling AcceptChanges.]
System.Data.Objects.ObjectStateManager.FixupKey(EntityEntry entry) +2518309
System.Data.Objects.EntityEntry.AcceptChanges() +159
System.Data.Objects.ObjectContext.AcceptAllChanges() +356
System.Web.UI.WebControls.EntityDataSourceView.ExecuteUpdate(IDictionary keys, IDictionary values, IDictionary oldValues) +376
System.Web.UI.DataSourceView.Update(IDictionary keys, IDictionary values, IDictionary oldValues, DataSourceViewOperationCallback callback) +87
System.Web.UI.WebControls.DetailsView.HandleUpdate(String commandArg, Boolean causesValidation) +1091
System.Web.UI.WebControls.DetailsView.HandleEvent(EventArgs e, Boolean causesValidation, String validationGroup) +425
System.Web.UI.WebControls.DetailsView.OnBubbleEvent(Object source, EventArgs e) +89
System.Web.UI.Control.RaiseBubbleEvent(Object source, EventArgs args) +37
System.Web.UI.WebControls.DetailsViewRow.OnBubbleEvent(Object source, EventArgs e) +80
System.Web.UI.Control.RaiseBubbleEvent(Object source, EventArgs args) +37
System.Web.UI.WebControls.LinkButton.OnCommand(CommandEventArgs e) +121
System.Web.UI.WebControls.LinkButton.RaisePostBackEvent(String eventArgument) +156
System.Web.UI.WebControls.LinkButton.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String eventArgument) +10
System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument) +13
System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData) +9642898
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1724

Related

Persist object without try-catch block in method

I'm currently working on a project and I want to see what damage it can do if I don't embrace my code with try-catch block when persisting object into database. Here is my code down below that i use as test.
public class NewEventServiceBean implements NewEventService {
#PersistenceContext(name = "example")
EntityManager manager;
#Resource
private UserTransaction userTransaction;
#Override
public void createNewEvent(String title, String content) throws Exception
{
userTransaction.begin();
Event event = new Event();
Content cont = new Content();
cont.setContent(content);
event.setTitle(title);
event.setCont(cont);
manager.persist(event);
manager.persist(cont);
userTransaction.commit();
}
In the database i have this Event table that has a foreign key to Content table.
And my question is if It's possible that Event object is persisted in to the database even if I cause something wrong when persisting the content class. Or what is the disadvantages of not embracing the code with try catch and rollback?
I've tried to cause an error when persisting the content object, but the Event is not persisted into the datbase even if everything is correct in that class.

Entity framework Code first returns detached objects when called using IRepository pattern

Trying to fetch an object using IRepository pattern with Entity Framework Code First. Usually when calling the repository, it will return an object which is attached to the context, and changes will be traced. My problem now is that the repository seems to only return detached objects.
When adding one object to another, my savechange will treat both the objects connected to eachother as inserted instead of inserting one and connect the other as a foreign object, since the vessel for some reason gets handled as detached. I know that it can be solved by attaching the vessel-object, that is not what im looking for, since it should be attached when queryed for in the first place.
IRepository
T FirstOrDefault(Expression<Func<T, bool>> predicate);
EFRepository (Implementing IRepository)
public T FirstOrDefault(Expression<Func<T, bool>> predicate)
{
return Context.Set<T>().FirstOrDefault(predicate);
}
Calling it
private readonly IRepository<VesselModel> _vesselRepository;
private readonly IRepository<InspectionModel> _inspectionRepository;
public Constr(IRepository<VesselModel> vesselRepository, IRepository<InspectionModel> inspectionRepository){
_vesselRepository = vesselRepository;
_inspectionRepository = inspectionRepository;
}
public void RunConnection(int vesselId){
var vessel = _vesselRepository.FirstOrDefault(x=> x.OrigId == vesselId);
var inspection = new Inspection{Vessel = vessel};
_inspectionRepository.Add(inspection);
_inspectionRepository.SaveChanges();
}

DbContext not initializing with SQL Server Compact in ASP.Net MVC

I have created a simple project using ASP.Net MVC template in Visual Studion 2013 Express for Web. It does not use any authentication. Then I installed EntityFramework (v6.0.1), EntityFramework.SqlServerCompact packages.
My DbContext class is very simple:
public class EditTestContext : DbContext
{
public EditTestContext() : base("EditTestContext")
{
}
public EditTestContext(string connectionString) : base(connectionString)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.SetInitializer(
new DropCreateDatabaseIfModelChanges<EditTestContext>());
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Configurations.Add(new EditTestConfig());
}
}
The actual context object is created in the Unit of Work class:
public class EditTestUoW:IEditTestUoW,IDisposable
{
private DbContext dbContext;
public EditTestUoW()
{
CreateDbContext();
}
private void CreateDbContext()
{
dbContext = new EditTestContext();//NEW DBCONTEXT OBJECT IS CREATED
dbContext.Configuration.LazyLoadingEnabled = false;
dbContext.Configuration.ProxyCreationEnabled = false;
dbContext.Configuration.ValidateOnSaveEnabled = false;
}
public IRepository<EditTestModel> EditTestRepo
{
get
{
return new EFRepository<EditTestModel>(dbContext);
}
}
}
The connection string being used is:
<add name="EditTestContext" connectionString="Data Source=
|DataDirectory|EditTestDb.sdf;Max Database Size=256;
Max Buffer Size=1024;File Mode=Shared Read;
Persist Security Info=False;" providerName="System.Data.SqlServerCe.4.0" />
Now when I try to access any thing using this Context like:
var rep=UoW.EditTestRepo;
var list=rep.GetAll().ToList();
I am getting following exception on rep.GetAll() function:
System.InvalidOperationException: Sequence contains no matching element
On digging deeper, IQueryable from Repository class (DbSet<EditTest>) is throwing following exception:
The context cannot be used while the model is being created. This exception may
be thrown if the context is used inside the OnModelCreating method or if the same
context instance is accessed by multiple threads concurrently. Note that instance
members of DbContext and related classes are not guaranteed to be thread safe.
I thought it might have been caused by ninject, but it is still there even after I removed it.
What I am doing wrong here or something (some assembly reference etc.) is missing?
Well after some other search on the issue, I got this MSDN forum link. As was suggested by Rowan, I tried to manually initialize the context using following statement in my EFRepository class:
dbContext.Database.Initialize(false);
The application failed way before it was hitting the GetAll() method. But this exposed the stack trace which gave me some direction:
[InvalidOperationException: Sequence contains no matching element]
System.Linq.Enumerable.Single(IEnumerable`1 source, Func`2 predicate) +2614017
System.Data.Entity.Utilities.DbProviderManifestExtensions.GetStoreTypeFromName
(DbProviderManifest providerManifest, String name) +146
.....Other Lines.....
System.Data.Entity.Internal.InternalContext.Initialize() +31
System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType
(Type entityType) +38
System.Data.Entity.Internal.Linq.InternalSet`1.Initialize() +138
System.Data.Entity.Internal.Linq.InternalSet`1.get_InternalContext() +38
System.Data.Entity.Infrastructure.DbQuery`1.System.Linq.IQueryable
.get_Provider() +99
System.Linq.Queryable.Any(IQueryable`1 source) +50
Then searching for DbProviderManifestExtensions.GetStoreTypeFromName revealed that this is the line where EF was trying to get column type. I had specified UNIQUEIDENTIFIER for my Id column:
Property(x=> x.Id).HasColumnType("UNIQUEIDENTIFIER")
Once I commented this, all was well.
Though there is a request on Codeplex to provide the proper error message in case the column type is not valid for database provider.

Why transaction can't commit in a self-invoked ejb method with #REQUIRES_NEW Annotation

First I want to explain my self-invoked ejb method in this situation. I have a stateful session bean with a method which starts a new transaction (Annotated by #REQUIRES_NEW). To invoke this method inside the bean itself and make the annotation effective, I use SessionContext#getBusinessObject() to achieve the effect of #EJB (#EJB here causes stackoverflow?!). My code is shown below:
#Stateful
#Local
public class TransactionTest implements ITransactionTest {
#PersistenceContext(unitName="Table",Type=PersistenceContextType.EXTENDED)
private EntityManager manager;
#Resource
SessionContext sc;
ITransactionTest me;
#PostConstruct
public void init(){
me = this.sc.getBusinessObject(ITransactionTest.class);
}
public void generateRecord(int i) throws RuntimeException{
Record record = new Record();
record.setId(i+"");
record.status(1);
manager.persist(record);
manager.flush(); //If not flush, result is correct. Why?
me.updateRecord(i);
}
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void updateRecord(int i) throws RuntimeException{
try {
Record record = manager.find(Record.class, i+"");
record.setStatus(2);
manager.flush();
} catch(Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
}
While,generateRecord() runs properly. The console shows it executes 'insert' and 'update' HQL without any exception (I use Hibernate as JPA provider). However, the 'update' result doesn't appear in the database. Why? Does updateRecord() commit correctly?
Also, I try it in two altenative ways: First is invoking generateRecord() (it will no longer invoke updateRecord()) and updateRecord() consecutively in another bean. It can give me the right result.
The second is removing the first flush(). Then both 'insert' and 'update' HQL will be executed at the second flush(). This method can also produce right result.
My program is running under JBOSS 6.1.0-Final and database is Oracle.
Best Regards,
Kajelas

How do I get changes to save when selecting with a non-EF DTO type?

I'm using the following code to populate a DevExpress XtraGrid with data from a DB first model. Calling SaveChanges has no effect, and I assume this is because there are no cached objects that match the objects I select for the grid.
Is there any way I can tell EF to use the PK, Id, to apply new values to cached objects during saved changes? Calling SaveChanges works if I use the whole Employee object for the grid.
private void EmployeeFormLoad(object sender, EventArgs e)
{
empsGridView.OptionsBehavior.Editable = true;
var emps = context.Employees.Select(emp => new EmployeeDescriptor
{
Id = emp.Id,
FirstName = emp.FullNames,
LastName = emp.Surname
});
employeeDescriptorBindingSource.DataSource = emps.ToList();
}
private void button1_Click(object sender, EventArgs e)
{
employeeDescriptorBindingSource.EndEdit();
context.SaveChanges();
}
Anonymous types are Immutable in C#. In VB, you can make them mutable using the "Key" keyword, although I'm not sure if EF honors that for updates. If you want your model to be editable, change the conceptual model to only include the fields you need and ensure that the unused table columns are all nullable.