I am using Entity Framework 4 with SqlServer CE as the database.
Because SqlServer CE does not support TransactionScope, so I have to resort to using BeginTransaction and RollbackTransaction method.
I have two GRUD functions to delete and create rows in the database.
What I want to achieve is something like this :
using (var context = new MyContext())
{
using (var tx = context.BeginTransaction())
{
// grud functions
deleteRows();
addRows();
// do db stuff here...
tx.Commit();
}
}
But I cannot find BeginTransaction in the context.
How can I go about it?
How can I go about it
I think, you're misunderstanding a concept of DbContext. It is a local cache of entities + change tracker. Whatever you do with entities, this changes are just tracked by context without affecting underlying data source.
This all happens until you call SaveChanges method. This method applies changes from change tracker to data source in transaction manner, so, all of the changes you've made, will be consistent.
If you will write your deleteRows and addRows in a way, that they won't call SaveChanges, and put SaveChanges call somewhere outside these methods, you'll get desired transaction:
using (var context = new MyContext())
{
// grud functions
deleteRows(context);
addRows(context);
context.SaveChanges();
}
private void deleteRows(MyContext context) {}
private void addRows(MyContext context) {}
In ObjectContext, BeginTransaction() is a method on Connection, not on the ObjectContext
However, DbContext exposes BeginTransaction directly to the underlying connection. So possibly you aren't using a DbContext?
Try
context.Connection.BeginTransaction()
Related
I create generic add method that makes search in database given id and then i make changes on table and so i save it to database but when i add it under transaction scope it didn't worked. Thrown Request timeout Exception.It works on .net framework but doesn't work on .net core.I tried Mock<> but it does give me an error that platform doesn't support that.
using (Y db = new Y())
{
using (var transaction = db.Database.BeginTransaction())
{
db.Table.Add(new Table());
db.SaveChanges();
public static void add<Y>(Func<T, bool> condition)
{
tableobject = Db.Set<Y>().where(condition).FirstOrDefault();
Db.Set<Y>().Add(tableobject);
Db.SaveChanges();
}
Transaction.Commit();
}
}
I make some changes on question for representation real situation.
I find the solution.
This is a method that takes parameters and create new instance MyDbContext.
I called this method under transaction and if you work with .net core you cant create two dbcontext instance.You can work only one dbcontext. And I passed my Dbcontext class to method and the problem solved.
I'm trying to understand how NHibernate works. To do so I've created a small test, given below. But the test is failing on the marked line and I don't understand why.
What am I misunderstanding?
To briefly explain the code chunk... I create an entity in the DB. Then I call Evict to remove the entity from session cache so that the next call for it would force a DB read. Then I do a DB read, but instead of getting back an entity instance read from DB, I get NULL, on the marked line.
using NHibernate;
using MyCorp.MyProject.Resources.MyEntity;
using MyCorp.MyProjectTests.Common.Fixture;
using Xunit;
namespace MyCorp.MyProjectTests.Common.DB
{
[Collection("Component")]
public class NHibernateTest
{
private readonly ISessionFactory dbSessionFactory;
public NHibernateTest(ComponentFixture componentFixture)
{
this.dbSessionFactory = componentFixture.DatabaseFixture.DBSessionFactory;
}
[Fact]
[Trait("Category", "Component")]
public void TestSessionCache()
{
const string QUERY = #"DELETE MyEntityModel mg WHERE mg.Id = :id";
const string TITLE = "NHibernate session test object";
using (ISession dbSession = this.dbSessionFactory.OpenSession())
{
// Create new entity and then remove it from session cache.
long id = (long) dbSession.Save(new MyEntityModel
{
Title = TITLE
});
dbSession.Evict(dbSession.Get<MyEntityModel>(id));
// Entity loaded from DB and stored into session cache.
Assert.Equal(TITLE, dbSession.Get<MyEntityModel>(id).Title); // ===== FAILS HERE =====
// Delete entity from DB, but don't evict from session cache yet.
dbSession.CreateQuery(QUERY).SetParameter("id", id).ExecuteUpdate();
// Entity still reachable through session cache.
Assert.Equal(TITLE, dbSession.Get<MyEntityModel>(id).Title);
// Evict deleted entity from session cache.
dbSession.Evict(dbSession.Get<MyEntityModel>(id));
// Entity not available in neither DB nor session cache.
Assert.Null(dbSession.Get<MyEntityModel>(id));
}
}
}
}
Save() is not equal to SQL INSERT.
Save() means: make the session aware of this object and have the session send it to the database at a suitable time. Depending on mappings and configuration, this can be before Save() returns, or not.
So you evict the object from the session before it gets persisted.
If you omit the call to Evict(), your test works because none of the other code actually depends on the item being in the database (the DELETE statement may indicate it found 0 rows to delete, but this is not a problem for the test).
To use automatic flush behaviour, you should always be inside a transaction, not just a session. In fact, for best reliability, you really should always be inside a transaction whenever you are inside a session (other patterns are possible, but tend to be more complicated to get right).
Here is the documentation on when flushing of changes to the database happens:
http://nhibernate.info/doc/nhibernate-reference/manipulatingdata.html#manipulatingdata-flushing
I need to update customer details. For this, I have to retrieve the entity from another session from repository and in the service, I am updating that entity. When I do this, I get an error message saying:
The operation is not valid for the current state of the enlistment.
But if I updated the entity without retrieving it from database, everything works fine.
This is how I am trying to update in Service.
Customer customer = customerService.getById(customer_payment.customer_id);
customer.deductBalance(customer_payment.discount_received);
customerService.updateCustomerDetails(customer);
This is my repository that updates the entity:
using (ISession session = SessionFactory.OpenSession)
{
using(ITransaction t = session.BeginTransaction())
{
session.SaveOrUpdate(customer);
t.Commit();
}
}
This is my function that returns the entity of the given ID:
Customer customer;
using (ISession session = SessionFactory.OpenSession)
{
customer = session.Get<Customer>(customer_id);
}
return customer;
How can I solve this problem? Thanks in advance.
Edit 1: This is what my OpenSession does:
Configuration configuration = new Configuration().Configure("hibernate.cfg.xml");
Assembly assembly = Assembly.GetCallingAssembly();
configuration.AddAssembly(assembly);
iSessionFactory = configuration.BuildSessionFactory();
CurrentSessionContext.Bind(iSessionFactory.OpenSession());
return iSessionFactory.OpenSession();
Is it a good approach to open a new session everytime or should I use Singleton pattern in SessionFactory?
Detach the customer from first ISession before updating it with other. You have to expose Evict method on repository OR have to expose ISession outside your repository.
Customer customer = customerService.getById(customer_payment.customer_id);
customerService.Evict(customer);
//OR
customerRepository.Evict(customer);
//OR
customerService.Session.Evict(customer);
//OR something similar...
customer.deductBalance(customer_payment.discount_received);
customerService.updateCustomerDetails(customer);
Refer following:
https://ayende.com/blog/4282/nhibernate-cross-session-operations
What does NHibernate Session.Evict do?
Can I detach an object from an NHibernate session?
Edit (for your Update)
Is it a good approach to open a new session everytime or should I use Singleton pattern in SessionFactory?
This is opinion based question actually; but it is recommended that your ISession should be short lived.
That said, you can create new session for each database action. But by doing this, you are missing many ORM features like Session Level Cache, Lazy Loading, Change Tracking (UoW) etc.
You can choose to move your UoW on Request level (i.e. ISession per Request) where you can avail the benefit of ORM features; but again there are other issues associated with it. Refer following: https://stackoverflow.com/a/48092471/5779732
Should I transform Entity (Persistent) objects to DTO objects?
I'm trying to figure out the best way to manage the DbContext. I've seen code samples that don't dispose and I've seen people say that that is a bad idea. Is it appropriate for me to do something like below? Also, should I put every transaction, including reads, in a new DbContext? This might be another question, but is the part about the EntityState necessary?
public abstract class GenericRepository<T> where T : EntityData
{
protected MyDbContext Context
{
get { return new MyDbContext(); }
}
public T Save(T obj)
{
T item;
using (var context = Context)
{
var set = context.Set<T>();
if (String.IsNullOrEmpty(obj.Id))
item = set.Add(obj);
else
{
item = set.Find(obj.Id);
item = obj;
}
// taken from another code sample
var entry = context.Entry(item);
if (entry.State == EntityState.Detached)
{
//Need to set modified so any detached entities are updated
// otherwise they won't be sent across to the db.
// Since it would've been outside the context, change tracking
//wouldn't have occurred anyways so we have no idea about its state - save it!
set.Attach(item);
context.Entry(item).State = EntityState.Modified;
}
context.SaveChanges();
}
return item;
}
}
EDIT
I also have an extended class that implements this function below. The context is not being wrapped in a using statement in this query, so I'm a little suspicious of my code.
public IQueryable<T> FindByAccountId(string accountId)
{
return from item in Context.Set<T>()
let user = UserRepository.FindByAccountId(accountId).FirstOrDefault()
where item.UserId == user.Id
select item;
}
Contexts should really be on a per request basis. The request comes in and a new context is created. This context is used for the remainder of the request then disposed of at the end of the request accordingly. This gives you the benefit of request long transactions, and as highlighted by HamidP, you also have the added benefit of cached entities; meaning that any entities loaded into the context can be loaded by retrieved without Entity Framework needing to query the database.
If you're using any kind of inversion of control container such as StructureMap then you can easily create HTTP request bound contexts by a configuration such as:
this.For<DbContext>().HybridHttpOrThreadLocalScoped().Use<DbContext>();
You're then able to inject your DbContext (or a derivative of it) into your repository and leave your IOC container of choice to dispose of the context at the end of the request. If you were to inject the same context into another repository then you'd receive the same instance of the context.
I hope this helps!
No, it should not
Best approach here is to assign a context just for a request. you should attach a context to an incoming request and dispose your context when request is finished. In this approach you save the overhead of creating a context for every transaction and also benefit from caching mechanism of context because each context has it's inside cache and a request may access the data it had access recently.
Creating a context for each transaction is not as bad as having a long life context!! Don't ever do that, long life contexts result in many concurrency issue and the cache becomes stale and memory consumption grows high and higher and you should maintain your application in future by miracles.
I have a WCF which calls EF functions.
When I invoke the method from the client to insert a user nothing happens.
This is the method I am invoking :
public void insertData(Users pUser)
{
using (var context = new AMTEntitiesContainer())
{
var User = context.Users.Add(pUser);
context.SaveChanges();
}
}
Is there anything wrong in this code?
The only thing that I see wrong is var User is not needed. Simply use context.Users.Add(pUser); and save the changes.
The other possibility is that AMTEntitiesContainer is not a proper DbContext. Without seeing your AMTEntitiesContainer class it would be hard to figure that out though.