Is there any reason to use ObjectContext transaction handling with DbContext's SaveChanges? - entity-framework

I have a code like this:
public abstract class DataContextBase
{
public DbContext DbContext { get; protected internal set; }
public ObjectContext ObjectContext { get; protected internal set; }
protected DbTransaction transaction;
protected void SetContext(DbContext db, ObjectContext oc)
{
DbContext = db;
ObjectContext = oc;
}
public void BeginTransaction()
{
if (ObjectContext.Connection.State != System.Data.ConnectionState.Open)
{
ObjectContext.Connection.Open();
}
transaction = ObjectContext.Connection.BeginTransaction();
}
public void CommitTransaction()
{
try
{
transaction.Commit();
}
finally
{
transaction = null;
ObjectContext.Connection.Close();
}
}
public void RollbackTransaction()
{
try
{
transaction.Rollback();
}
finally
{
transaction = null;
ObjectContext.Connection.Close();
}
}
public void Save()
{
DbContext.SaveChanges();
}
}
It is from a sample application, and I use this as a base class of my application's main data context. I'm using Entity Framework 5, and I have just read that when I call the DbContext's SaveChanges method, it always runs in a database transaction and it will throw an exception when the transaction have to be rollbacked and in this case the changes are not saved into the database.
But in the sample application, almost every service method begins with a DataContextBase.BeginTransaction call and ends with a DataContextBase.CommitTransaction call (in an exceptional case it ends with DataContextBase.RollbackTransaction) even though that DataContextBase.Save is called (which calls DbContext.SaveChanges()).
It looks like there is an extra transaction wrapping the built in transaction of the DbContext.SaveChanges call.
Could there be any situation which needs this extra transaction?
NOTE: The DataContextBase's ObjectContext is come from the DbContext with a trick:
((IObjectContextAdapter)this).ObjectContext; // inside the DbContext class

Having an extra transaction is redundant because ObjectContext/DbContext implements Unit of Work. If you have other means of communicating with the database and they also need to be part of the transaction the use TransactionScope.
Connection management is also done by EF and you do not have to

Related

Spring Transactions - How to access to entity saved in parent transaction

consider following model:
#Service
public class TripServiceImpl implements TripService {
#Autowired
private EventService eventService;
#Autowired
private CalendarService calendarService;
#Transactional
public void processTrip(TripDto dto) {
EventDto event = eventService.findByTripId(dto).orElseGet(() -> eventService.createByTrip(dto));
dto.getMembers().forEach(memberCode -> {
try {
calendarService.createReminder(event.getId(), memberCode);
} catch (Exception ex) {}
});
// rest logic
}
}
This is how createReminder method looks like:
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void createReminder(Long eventId, String memberCode) {
Optiona<Event> eventRepository.findById(eventId); // this returns Optional.empty()
...
}
In our trip service we find or create event for given trip if no exists, and add reminders to calendars of members from that trip. Method for creating reminders we need as new transaction to ensure, if that method failed with exception, don't rollback all changes. This transaction propagation unfortunately can't find entity which was saved in parent transactional method. Can you tell me how to fix it? Thank you.

How do I create a separate entity manager for bulk operations in a JTA environment?

In JPA, when doing bulk operations such as this
update LogEntry e set e.customer = null where e.customer.id = :cid
It is recommended to use a separate entity manager to avoid breaking synchronization, according to this: UPDATE SET Queries in JPA/JPQL
For example, the EntityManager may not be aware that a cached entity object in its persistence context has been modified by an UPDATE query. Therefore, it is a good practice to use a separate EntityManager for UPDATE queries.
How do I create a separate entity manager in a JTA environment such as Wildfly using hibernate? Do I need to create a separate persistence unit for bulk operations?
EDIT: Given I dont need a separate PU for bulk operations, is this a sufficient way of solving it using a new transaction?
#Transactional
public class JpaCustomerRepository implements CustomerRepository {
#Inject
private EntityManager em;
...
#Override
public Customer remove(long id) {
CustomerEntity entity = em.find(CustomerEntity.class, id);
if (entity != null) {
updateLogEntriesToNull(entity);
em.remove(entity);
return entity;
} else {
return null;
}
}
#Transactional(value=TxType.REQUIRES_NEW)
public void updateLogEntriesToNull(CustomerEntity entity) {
em.createNamedQuery(LogEntry.updateCustomerToNull)
.setParameter("cid", entity.getId())
.executeUpdate();
}
...
}
Where LogEntry.updateCustomerToNull is the bulk query.
Answer: This does not work because the interceptor is not invoked when called from inside the same class.
EDIT2: Following the suggestions from Andrei, this should work:
#Transactional
public class JpaCustomerRepository implements CustomerRepository {
public static class BulkUpdater {
#Inject
private EntityManager em;
#Transactional(value=TxType.REQUIRES_NEW)
public void updateLogEntriesToNull(CustomerEntity entity) {
em.createNamedQuery(LogEntry.updateCustomerToNull)
.setParameter("cid", entity.getId())
.executeUpdate();
}
}
#Inject
private EntityManager em;
#Inject
private BulkUpdater bulkUpdater;
...
#Override
public Customer remove(long id) {
CustomerEntity entity = em.find(CustomerEntity.class, id);
if (entity != null) {
bulkUpdater.updateLogEntriesToNull(entity);
em.remove(entity);
return entity;
} else {
return null;
}
}
...
}
Testing confirms that the interceptor gets called twice.
The recommendation is valid only if you also do other stuff with the EntityManager (when there is a risk of manipulating/reading the same entities as the BULK UPDATE). The easiest solution: make sure that this BULK UPDATE is executed in a separate service, within a new transaction. No need to create a separate PU (persistence unit) for bulk operations.

DbContext is Disposed Exception using StructureMap

I am using StructureMap as my IOC Container. I created a seed method that runs on start-up, to be sure the database has relevant data. When this runs, i get the error
The operation cannot be completed because the DbContext has been disposed.
The seed class in question is
public class SeedDatabase : IRunAtStartup
{
private DbContext _context;
private UserManager<ApplicationUser> _userManager;
public SeedDatabase(DbContext context, UserManager<ApplicationUser> userManager)
{
_context = context;
_userManager = userManager;
}
public async void Execute()
{
if (!_context.Set<ApplicationUser>().Any())
{
//Seed Admin User
await _userManager.CreateAsync(new ApplicationUser
{
UserName = "Admin",
Company = "Test Company Ltd",
EmailAddress = "email#emailaddress.com"
}, _userManager.PasswordHasher.HashPassword("Password"));
_context.SaveChanges();
}
}
}
The error occurs on hitting .SaveChanges()
This method just runs once at startup and it accepts a UserManager and DbContext in its conctructor which are provided by means of the IOC Container.
I have tried handling the DbContext per HttpRequest, which was my preference, but also as a singleton. However, the situation did not change.
Here is some of the setup for IOC Container
//convention for DbContext
public class DbContextConvention : IRegistrationConvention
{
public void Process(Type type, Registry registry)
{
//be sure a new context is used for each request
if (type.CanBeCastTo(typeof(DbContext)) && !type.IsAbstract)
{
registry.For(type).LifecycleIs(new UniquePerRequestLifecycle());
}
}
}
Any ideas why I keeps disposing it before it has even been used?
The problem is probably caused by the asynchronous nature of your Execute method. This method will return almost immediately and will continue on a background thread. You are probably disposing the DbContext after Execute returned, but at that moment in time the background operation hasn't finished. Instead, return a Task and wait for that task to finish before cleaning up, or make the Execute method synchronous.

jpa #postpersist #postupdate only after transaction commit

I'm inserting/updating 3 tables while using manual transaction. I want to insert into an history table right after the transaction was committed. that means that there were 3 em.persist actions (for each table), and only after committing them I would like to call a method.
This is my code:
#Resource
private EJBContext context;
public void save(Object obj)
{
try
{
transaction = context.getUserTransaction();
transaction.begin();
em.persist(obj);
sb2.save(obj); //persist in sb2
sb3.save(obj); //persist in sb2
transaction.commit();
}
catch (Exception exp)
{
transaction.rollback();
}
}
Is there any kind of post commit method?
How can I call a method after the commit, and not after the persist?
You could use TransactionSynchronizationRegistry to register a callback to be executed after the transaction completes:
public class MyListener {
// if you can't inject it with #Resource, you'll have to fetch it from JNDI
#Resource
private javax.transaction.TransactionSynchronizationRegistry registry;
#PostUpdate
public void postUpdate(final Object entity) {
registry.registerInterposedSynchronization(new Synchronization() {
public void beforeCompletion() {}
public void afterCompletion(int status) {
if (status == javax.transaction.Status.STATUS_COMMITTED) {
// Do something with your entity
}
}
});
}
}
Then annotate your entities with #EntityListeners(MyListener.class) (or add it in your orm.xml).
If you are in a JPA 2.2 + CDI + JTA environment, you could leverage CDI events which can be observed at a specified transaction phase.
#Retention(RetentionPolicy.RUNTIME)
#Qualifier
public #interface EntityEvent {}
public class MyListener {
#Inject
#EntityEvent
private Event<Object> event;
#PostUpdate
public void postUpdate(Object entity) {
event.fire(entity);
}
}
public class MyObserver {
public void afterCommit(#Observes(during = TransactionPhase.AFTER_SUCCESS) #EntityEvent Object entity) {
// do something...
}
}
#EntityListeners(MyListener.class)
public class MyEntity {}
JPA does not provide such events, but EclipseLink provides extended events through the SessionEventListener API.
http://eclipse.org/eclipselink/documentation/2.5/jpa/extensions/p_session_event_listener.htm
You could also consider the container JTA events.

Code first DbContext usage

I have been trying to work with Entity Framework's Code First. I wrote the below line of code
DbContext _context = new DbContext(ConfigurationManager.ConnectionStrings["con"].ConnectionString);
However on execution, the connection remains closed. Is there something wrong with this code??
I have created a generic repository class using the DBContext shown below
public class GenericRepository<T> where T:class
{
public DbContext _context = new DbContext(ConfigurationManager.ConnectionStrings["con"].ConnectionString);
private DbSet<T> _dbset;
public DbSet<T> Dbset
{
set { _dbset = _context.Set<T>(); }
get { return _dbset; }
}
public IQueryable<T> GetAll()
{
return Dbset;
}
}
and I then call this class on the page load event, where Teacher is an entity class which maps to a table in the database
protected void Page_Load(object sender, EventArgs e)
{
GenericRepository<Teacher> studentrepository = new GenericRepository<Teacher>();
rptSchoolData.DataSource = studentrepository.GetAll().ToList();
rptSchoolData.DataBind();
}
but the connection remains closed and there is also an InvalidOperation Exception in the ServerVersion of the context object.
Am I missing something??
This property
public DbSet<T> Dbset
{
set { _dbset = _context.Set<T>(); }
get { return _dbset; }
}
has a heavy smell to it. A setter that does nothing with value is an anti pattern big time. Do you expect to set the DbSet after creating a GenericRepository?
I don't understand that your code even works because you never initialize _dbset, it should throw a null object reference exception.
The _dbset and DbSet shouldn't be there in the first place. GetAll should return _context.Set<T>(). EF should open and close the connection all by itself. Maybe the fact that you don't initialize the DbSet causes a connection never to open, causing problems in other pieces of code not revealed here.