some time ago I managed to add a custom interceptor to a private fork of EF 6.1 that gets called just before and after a lazy-loading DB-Hit is performed, like this:
public interface IDbLazyLoadInterceptor : IDbInterceptor
{
void LazyLoadablePropertyGetterCalled<TItem>(DbLazyLoadInterceptionContext<TItem> interceptionContext) where TItem : class;
void PropertyIsGettingLazyLoaded<TItem>(DbLazyLoadInterceptionContext<TItem> interceptionContext) where TItem : class;
void PropertyHasBeenLazyLoaded<TItem>(DbLazyLoadInterceptionContext<TItem> interceptionContext) where TItem : class;
}
Now I have a new requirement. I need to create an interceptor that gets called by EF just before and just after the materialization of entities is/has been performed. Problem: I want to be able to correlate this to DB Hits (stuff getting called to IDbCommandInterceptor).
I need help about where to start.
I thought I could add interceptor-dispatch-calls into the Shaper, e.g. into its public bool MoveNext() and public async Task<bool> MoveNextAsync(CancellationToken cancellationToken) methods, before and after it calls _shaper.StartMaterializingElement(); and _shaper.StopMaterializingElement();. I guess this could work, right?
But how can I correlate this to the Db Command fed to the IDbCommandInterceptor? Is there some way to get the Command (query?) run by the Reader used by the Shaper?
And: Am I missing further spots I'd need to add interceptor-calls to?
Thanks, cheers,
Tim
Related
When repository.save(t) is called from my service, which is in turn called from my controller, all works just fine, and the object is inserted into the database table; But, when the service is called from my test class, Hibernate returns the created object but does not really flush the transaction into the database. I have tried using #Transactinal and #Commit in my test class and also on my #Test methods, but no difference in the result. I have also tried other solutions which involve using org.springframework.test.context.transaction.TestTransaction class, but any method call on this class throws an exception.
this is my super class for test:
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
public abstract class QaApplicationTest {
protected abstract void initializeTest() throws Exception;
protected abstract void cleanupTestEffects() throws Exception;
}
And this is my concrete test class:
public class RequestControllerTest extends QaApplicationTest {
#Autowired
private SiteService siteService;
#Autowired
private RequestService requestService;
#Test
#Transactional
public void givenObject_whenInsertToDB_thenCreated() throws Exception{
Site siteObject = siteService.save(siteObject); //Here I need a commit.
Request request = new Request(site.getId());
Request savedRequest = requestService.save(request); //Here database returns "Parent Key Not Found" error.
Assertions.assertTrue(savedRequest.getId()>0);
}
}
I know the #Transactional on test methods are used to roll back all the changes made inside the method, however, In my case, the changes are not even committed in the first place. And I have used #org.springframework.transaction.annotation.Transactional which is the correct annotation. I don't know which part I am doing wrong! Any idea?
My colleague found the issue; we had used a third-party library (Camunda) that had enabled batch-insert on Hibernate. So by disabling the batch operation the issue was resolved and the insert is actually taking place now. Not sure, why we faced this only in Spring Test and not in the main application though. if anyone has a comment, we appreciate it.
I am working on a .net core project where the requirement is to maintain an SQLite DB and an SQL Server DB simultaneously. I created two DbContext files SqlServerContext and SqliteContext and separate migration folders for them. These files are derived from a WorkerContext file that's derived from DbContext. The migration is working properly, as tables are created in both databases. But I could not make simultaneous data operation work.
This is the IKeyboardMouseActivityRepository. There are separate parts for using SqliteContext and SqlServerContext. I have to comment out one part when using the other. So I can do data entry in one DB at a time now.
public interface IKeyboardMouseActivityRepository :
IRepository<KeyboardMouseActivity, Guid, SqlServerContext>
// IRepository<KeyboardMouseActivity, Guid, SqliteContext>
{
}
public class KeyboardMouseActivityRepository :
IKeyboardMouseActivityRepository,
Repository<KeyboardMouseActivity, Guid, SqlServerContext>
// Repository<KeyboardMouseActivity, Guid, SqliteContext>
{
public KeyboardMouseActivityRepository(SqlServerContext dbContext)
: base(dbContext)
{
}
// public KeyboardMouseActivityRepository(SqliteContext dbContext)
// : base(dbContext)
// {
// }
}
This is the main Repository class.
public abstract class Repository<TEntity, TKey, TContext>
: IRepository<TEntity, TKey, TContext>
where TEntity : class, IEntity<TKey>
where TContext : DbContext
{
protected TContext _dbContext;
protected DbSet<TEntity> _dbSet;
public Repository(TContext context)
{
_dbContext = context;
_dbSet = _dbContext.Set<TEntity>();
}
// other methods such as Add, Remove etc.
}
My understanding is that since the context parameter is specified in KeyboardMouseActivityRepository, it only works for that specified context. How can I modify it so it works for both DbContext files and I can do data operation in both DB at the same time?
The repository you have defined is typed per-DbContext. If you want to have a repository that can update two known DbContext implementations then you can back off the Generic approach for the DbContexts and implement the repository to accept one of each in the constructor:
public abstract class Repository<TEntity, TKey>
: IRepository<TEntity, TKey>
where TEntity : class, IEntity<TKey>
{
protected SqlAppDbContext _sqlContext;
protected SqlLiteAppDbContext _sqlLiteContext;
protected DbSet<TEntity> _sqlDbSet;
protected DbSet<TEntity> _sqlLiteDbSet;
public Repository(SqlAppDbContext sqlContext, SqlLiteAppDbContext sqlLiteContext)
{
_sqlContext = sqlContext ?? throw new ArgumentNullException("sqlContext");
_sqlLiteContext = sqlLiteContext ?? throw new ArgumentNullException("sqlLiteContext");
_sqlDbSet = _sqlContext.Set<TEntity>();
_sqlLiteDbSet = _sqlLiteContext.Set<TEntity>();
}
// other methods such as Add, Remove etc.
}
Note that you will want to investigate and implement something like TransactionScope to help ensure that operations done via the repository are mutually committed or rolled back. For instance if you have code that attempts to update data in both DbSets and SaveChanges, if one succeeds and the other fails for any reason, usually the expectation would be they both roll back. Reads I expect would prioritize one DbSet over the other, but expect if you were to want to support something like a fail-over or situational load from one server or the other you will run into issues if it is at all possible that entities fetched from one DbContext are ever married up with entities fetched from the other. (entities loaded by _sqlContext cannot be associated with entities loaded by _sqlLiteContext) When updating entities and associating them via navigation properties you will be loading everything twice or playing a very dangerously error prone game of detaching and reattaching entities betewen DbContexts.
I would advise against using a Generic Repository pattern /w EF. This will paint you into various corners that will limit many of the capabilities that EF can provide for optimizing queries, working with projections, and performing operations like pagination, filtering, sorting, etc. efficiently without a lot of extra code or introducing pretty complex code into the repository.
Overall I wish you luck with the project, however a requirement and design like this will be a nest of hungry dragons for your time and sanity. :)
I have a java EE project using JPA (transaction-type="JTA"), hibernate as provider. I write my beans to handle the CRUD things. The program running in JBOSS 7 AS.
I have an EntityManagerDAO :
#Stateful
public class EntityManagerDao implements Serializable {
#PersistenceContext(unitName = "dtdJpa")
private EntityManager entityManager;
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public Object updateObject(Object object) {
object = entityManager.merge(object);
return object;
}
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public void createObject(Object object) {
entityManager.persist(object);
}
public void refresh(Object object) {
entityManager.refresh(object);
}
public <T> T find(Class<T> clazz, Long id) {
return entityManager.find(clazz, id);
}
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public void deleteObject(Object object) {
entityManager.remove(object);
}
}
but when I invoke deleteObject, this exception comes out.
java.lang.IllegalArgumentException: Removing a detached instance com.test.User#5
How is this caused and how can I solve it?
EntityManager#remove() works only on entities which are managed in the current transaction/context. In your case, you're retrieving the entity in an earlier transaction, storing it in the HTTP session and then attempting to remove it in a different transaction/context. This just won't work.
You need to check if the entity is managed by EntityManager#contains() and if not, then make it managed it EntityManager#merge().
Basically, the delete() method of your business service class should look like this:
em.remove(em.contains(entity) ? entity : em.merge(entity));
In my case, I got the same error, when I tried to delete an object
using,
session.delete(obj)
without creating any transaction before that.
And the problem is solved by creating the transaction first(session.beginTransaction() and then deleting the object.
I hope my answer will help someone :)
Sometimes its simply because you are missing the #Transaction annotation for add, remove, update operations.
I faced the same problem. The detached entity should be re-attached. As #BalusC mentioned, using EntityManager.merge() should be used to attach the detached entity. EntityManager.merge() generates SQL Query which fetches the current state of the entity, on which EntityManager.remove() has to be performed. But in my case it didn't worked.
Try EntityManager.remove(EntityManager.find(Class<T>,arg)) instead. It worked for me.
In my experience, if I query an object from the DB then closed the entity manager then do a DB delete, the problem happens. Or if I copy that loaded object to another instance then do a delete, this problem also happens.
In my opinion there are 2 things to keep note:
The object must be in the same session that was created by the Entity Manager
And the object mustn't be transferred to another object while the Entity Manager's session is still opened.
Cheers
Using autofac as my IoC framework.
I'd like to be able to set up my DbContext instance in my application's startup.
In my ASP.NET MVC 3 project, I register DbContext instance in Global.asax (PerLifetimeScope). But when I fire up my site on multiple browsers (or multiple tabs) at once, sometimes I get Object reference not set to an instance of an object. or New transaction is not allowed because there are other threads running in the session when I try to save changes back to database. Also I get
ExecuteReader requires an open and available Connection. The connection's current state: Broken. sometimes when I want to read data from database.
the errors seem to pop up randomly and I suspect it has something to do with my context's lifetime scope. here's my DbContext's overriden SaveChange method.
public class MyContext : DbContext
{
public override int SaveChanges()
{
var result = base.SaveChanges(); // Exception here
}
}
Here's how I register my context:
builder.Register(c => new MyContext("SomeConnectionString"))
.InstancePerLifetimeScope();
If I just have one open tab of my site in the browser everything works ok.
Also, It's worth mentioning I have CRUD operations with db every 5-10 seconds in my website by calling a controller method using Ajax.
StackTrace for New transaction is not allowed because there are other threads running in the session:
at System.Data.EntityClient.EntityConnection.BeginDbTransaction(IsolationLevel isolationLevel)
at System.Data.EntityClient.EntityConnection.BeginTransaction()
at System.Data.Objects.ObjectContext.SaveChanges(SaveOptions options)
at System.Data.Entity.Internal.InternalContext.SaveChanges()
at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
at System.Data.Entity.DbContext.SaveChanges()
at MyProject.Data.MyContext.SaveChanges() in D:\Test.cs
StackTrace for Object reference not set to an instance of an object.:
at System.Data.Objects.ObjectStateManager.DetectConflicts(IList`1 entries)
at System.Data.Objects.ObjectStateManager.DetectChanges()
at System.Data.Entity.Internal.InternalContext.DetectChanges(Boolean force)
at System.Data.Entity.Internal.InternalContext.GetStateEntries(Func`2 predicate)
at System.Data.Entity.Internal.InternalContext.GetStateEntries()
at System.Data.Entity.Infrastructure.DbChangeTracker.Entries()
at System.Data.Entity.DbContext.GetValidationErrors()
at System.Data.Entity.Internal.InternalContext.SaveChanges()
at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
at System.Data.Entity.DbContext.SaveChanges()
at MyProject.Data.MyContext.SaveChanges() in D:\Test.cs at
Registration of MyContext looks ok. Is it possible that some other service that takes a MyContext is registered as a singleton and being shared across threads?
I had the same issue, sporadic errors related to the DbContext while using Autofac to resolve the DbContext.
{System.Data.EntityCommandExecutionException: An error occurred while executing the command definition. See the inner exception for details.
etc.
{System.NullReferenceException: Object reference not set to an instance of an object.
at System.Data.Objects.ObjectStateManager.DetectConflicts(IList`1 entries)
etc.
I found a class resembling the following in my code. The dependency resolution was occurring within a static method inside of the singleton. The object being resolved had a dependency on the DbContext. I haven't had any additional issues after I found a way to restructure this class so that it was no longer a singleton.
Perhaps you have a similar situation? Another thing to try might be to make your DbContext InstancePerHttpRequest. That could help identify whether this is the issue.
public class Singleton
{
private static Singleton _instance = new Singleton();
private Singleton()
{
}
public static void DoSomething<TSource>(TSource source) where TSource : ISource
{
var items = DependencyResolver.Current.Resolve<IEnumerable<IDbContextConsumer<TSource>>>();
foreach (var item in items)
{
item.Execute(source);
}
}
}
Albahari writes in "c# 4.0 in a nutshell":
>
Although DataContext/ObjectContext implement IDisposable, you can (in general)
get away without disposing instances. Disposing forces the context’s connection
to dispose—but this is usually unnecessary because L2S and EF close connections
automatically whenever you finish retrieving results from a query
<<
This feels wrong and FxCop also complains if you are not diposing something that is IDisposable.
I have the following repository code:
public abstract class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{ ...
public void Add(TEntity entity)
{
using (var dbContext = this.UnityContainer.Resolve<DbContext>())
{
dbContext.Set<TEntity>().Add(entity);
dbContext.SaveChanges();
}
}
...
public virtual IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> expression)
{
using (var dbContext = this.UnityContainer.Resolve<DbContext>())
{
return dbContext.Set<TEntity>().Where(expression).ToList().AsEnumerable();
}
}
...
Note: I do not return IQueryable - lazy loading should not play a role.
Resolve DbContext is configured as PerResolveLifetimeManager.
Is this approach OK or do I need to reconsider this based on Albaharis description?
You should always call dispose if class exposes it. The statement claims that EF and L2S close connection whenever they finish operation - as I know the statement is correct but in the same time ADO.NET team also closes connection in Dispose method so perhaps there are situations when connection is not closed.
I'm working on EF 4.0 ObjectContext (yeah, I know...). I ended up looking at the code in DotPeek, and the dispose just nulls the reference to the connection and a few other things in the ObjectContext class.
When a connection is created (also found through DotPeek) it returns the existing instance. If the connection string is changed, it'll update the connection string for all instances.
That was my take on it at least. Need to look deeper but at first glance, it seems that you can get away with it.