Hi I am tryin to use Transactions in my application which coded in MVC.net
I have my dbcontext in a layer and I reach it from a business layer. When I run the code its not giving me any errors but its also not making the transaction at all.
This is my context
public class DB : IDisposable
{
public DB()
{
}
private RootDYSContext _ctx = null;
public RootDYSContext ctx
{
get
{
if (_ctx == null)
_ctx = new RootDYSContext();
return _ctx;
}
set
{
_ctx = value;
}
}
public bool Commit()
{
try
{
ctx.SaveChanges();
}
catch (Exception exp)
{
Mesaj = exp.Message;
return false;
}
return true;
}
}
And this is how I am tryin to do transaction which based on here "https://learn.microsoft.com/en-us/ef/core/saving/transactions"
public bool CreateKullanici(VMKullanici
KullaniciData,List<VMRol>RolDataList,VMRol AnaRolData)
{
string mesaj = "";
int kullanici_id;
using (RootDBHelper.DB db = new RootDBHelper.DB())
{
using (var ctx = new RootDBLayer.RootDYSContext())
{
using (DbContextTransaction dbContextTransaction =
ctx.Database.BeginTransaction())
{
try
{
kullanici_id = SaveKullanici(KullaniciData, out
mesaj);
foreach(VMRol rol in RolDataList)
{
VMKullaniciRol KullaniciRolInsertData = new
VMKullaniciRol();
KullaniciRolInsertData.kullanici_id =
kullanici_id;
KullaniciRolInsertData.rol_id = rol.rol_id;
if (rol.rol_id == AnaRolData.rol_id)
KullaniciRolInsertData.ana_rol_mu =
(int)RConstants.EvetHayir.Evet;
else
KullaniciRolInsertData.ana_rol_mu =
(int)RConstants.EvetHayir.Hayir;
bKullaniciRol.SaveKullaniciRol(KullaniciRolInsertData, out mesaj);
}
/*Edit:I think this line made confusion I put this to make sure rollback
dbContextTransaction.Rollback(); */
dbContextTransaction.Commit();
return true;
}
catch
{
dbContextTransaction.Rollback();
return false;
}
}
}
}
}
I expect when I run this I shouldnt get any record at all.I seriously stucked because I dont get any error or any hint at all and this method keeps saving data regardless of rollback.
Thank you in advance.
Edit: I do my save changes part on the insert methods
Adding also code examples for the methods
public int SaveKullanici(VMKullanici KullaniciData, out string mesaj)
{
using (RootDBHelper.DB db = new RootDBHelper.DB())
{
mesaj = "";
PKullanici pKullanici = new PKullanici();
if (KullaniciData.kullanici_id == default(int))
pKullanici.InsertKullanici(db.ctx, KullaniciData);
else
pKullanici.UpdateKullanici(db.ctx, KullaniciData);
if (db.Commit())
{
return KullaniciData.kullanici_id;
}
else
{
mesaj = db.Mesaj;
return -1;
}
}
}
public void InsertKullanici(RootDYSContext ctx, VMKullanici KullaniciData)
{
ctx.TKullanici.Add(KullaniciData.TKullanici);
}
I think your problem is that you do commit and then you want to rollback but you should one thing commit or rollback.
In addition I cant see in your code where you actually doing transaction.commit. Your code has commit function but commit != savechanges.
Idea of transactions (there are different type but) is that you save data to database do some validation or operations and then you say well it looks ok commit or there is an error do rollback.
Also note if you are not do not commit or do rollback then your identity field will get incremented no matter what
Related
I would like to update data using C# and Entity Framework Core.
But the result of Swagger is different from the result of executing the API in axios.
The variable "todoList" is stored with data in Swagger and returned null in axios.
Why a value of "todoList" is null?
Please tell me about it.
Thank you!
This is a Code.
public bool updateCompFlg(string operatorId, string id)
{
// Call Update!
// "_repository is an Interface."
if (_repository.Update(operatorId, id))
{
return true;
}
else
{
return false;
}
}
This is the code that actually updates the data.
public bool Update(string operatorId, string id)
{
// ★The results here are different.
var todoList = _context.TODOLIST.Where(u => u.seqNo.Equals(id)).FirstOfDefault();
todoList.operatorId = operatorId;
todoList.resultFlg = "1";
_context.Update(todoList);
try
{
_context.SaveChange();
return true;
}
catch (SystemException ex)
{
throw ex;
}
}
I am working with Xamarin forms and I have implemented SQLite database using Sqlite-net-pcl nuget package. The problem is that the database is initially saving a row but is unable to update or delete any row. Here is the code in which I save and delete the database row with a Key.
public Task<int> SaveItemAsync(SQLiteRowData item)
{
if (item.ID != 0)
{
return database.UpdateAsync(item);
}
else
{
return database.InsertAsync(item);
}
}
public Task<int> DeleteItemAsync(SQLiteRowData item)
{
return database.DeleteAsync(item);
}
public Task<int> DeleteItemAsyncWithKey(string key)
{
Task<SQLite.SQLiteRowData> lSQLiteRowTaskData =
App.sqliteConnectionPath.GetItemForKeyAsync(key);
SQLite.SQLiteRowData lSQLiteRowData = lSQLiteRowTaskData.Result;
return database.DeleteAsync(lSQLiteRowData);
}
public async Task<bool> DeleteUserPreferencesAsync()
{
Boolean lReturnValue = false;
try
{
Task<SQLiteRowData> lSQLiteRowDataTask = App.sqliteConnectionPath.GetItemForKeyAsync("UserPreferences"); SQLiteRowData lSQLiteRowData = lSQLiteRowDataTask.Result;
if (lSQLiteRowData != null) {
await DeleteItemAsync(lSQLiteRowData);
lReturnValue = true;
}
}
catch (Exception ex)
{
EMAUtilities.printLogs(ex.Message);
}
return lReturnValue;
}
// Used for saving userpreference
SQLiteRowData lSQLiteRowData = new SQLiteRowData("UserPreferences", lValue);
Task<int> x = SaveItemAsync(lSQLiteRowData);
Can anyone point out the mistake I have done in this, Thanks in advance
I'm trying to make a master-detail Web Form working with Entity Framework and performing insert and update on the same page. I'm new at EF, so I must be making a lot of mistakes here. Can you help me pointing me what's the best practices to perform insert/update on EF? What am I doing wrong here?
In this code, the "New" mode works well, but the "Edit" mode gets this error: "An entity object cannot be referenced by multiple instances of IEntityChangeTracker".
OrdersEntities ordersEntities = new OrdersEntities();
private Order myOrder
{
get { return (Order)Session["myOrder"]; }
set { Session["myOrder"] = value; }
}
public DataTable dtOrderDetails
{
get { return (DataTable)ViewState["dtOrderDetails"]; }
set { ViewState["dtOrderDetails"] = value; }
}
private string Mode
{
get { return (string)ViewState["mode"]; }
set { ViewState["_modo"] = value; }
}
private void btnSaveOrder_Click(object sender, EventArgs e)
{
if (dtOrderDetails.Rows.Count > 0)
{
using (ordersEntities)
{
using (var contextTransaction = ordersEntities.Database.BeginTransaction())
{
try
{
if (Mode == "New")
{
Order newOrder = new Order();
OrderDetails newOrderDetails;
int maxOrderNumber = ordersEntities.Order.Select(o => o.OrderNumber).DefaultIfEmpty(0).Max();
maxOrderNumber++;
newOrder.OrderNumber = maxOrderNumber;
newOrder.Date = DateTime.ParseExact(txtOrderDate.Text, "dd/MM/yyyy", CultureInfo.InvariantCulture);
newOrder.CustomerID = Convert.ToInt32(ddlCustomer.SelectedValue);
newOrder.Status = 1;
ordersEntities.Orders.Add(newOrder);
foreach (DataRow dt in dtOrderDetails.Rows)
{
newOrderDetails = new OrderDetails();
newOrderDetails.OrderNumer = maxOrderNumber;
newOrderDetails.ProductId = Convert.ToInt32(dt["ProductId"]);
newOrderDetails.Quantity = Convert.ToInt32(dt["Quantity"]);
ordersEntities.OrderDetails.Add(newOrderDetails);
}
ordersEntities.SaveChanges();
contextTransaction.Commit();
myOrder = newOrder;
}
if (Mode == "Edit")
{
Order editedOrder = myOrder;
OrderDetails editedOrderDetails;
editedOrder.Date = DateTime.ParseExact(txtOrderDate.Text, "dd/MM/yyyy", CultureInfo.InvariantCulture);
editedOrder.CustomerID = Convert.ToInt32(ddlCustomer.SelectedValue);
ordersEntities.Order.Attach(editedOrder);
ordersEntities.Entry(editedOrder).State = System.Data.Entity.EntityState.Modified;
editedOrder.OrderDetails.Clear();
foreach (DataRow dt in dtOrderDetails.Rows)
{
editedOrderDetails = new OrderDetails();
editedOrderDetails.OrderNumer = editedOrder.OrderNumber;
editedOrderDetails.ProductId = Convert.ToInt32(dt["ProductId"]);
editedOrderDetails.Quantity = Convert.ToInt32(dt["Quantity"]);
ordersEntities.OrderDetails.Add(editedOrderDetails);
}
ordersEntities.SaveChanges();
contextTransaction.Commit();
}
}
catch (Exception ex)
{
contextTransaction.Rollback();
}
}
}
}
}
Here is how you should approach it.
It would be best if you abstract the DbContext away, with this simple interface:
public interface IDataRepository : IDisposable
{
IDbSet<Order> Orders { get; set; }
void Save();
}
Of course, your implementation of IDataRepository is based on EntityFramework. Note that you will need to have a dataRepositoryConnection connection string in your web.config file:
public class EfDataRepository : DbContext, IDataRepository
{
public EfDataRepository() : base("dataRepositoryConnection")
{
}
public IDbSet<Order> Orders { get; set; }
public void Save()
{
this.SaveChanges();
}
}
In my experience, you also need a 'factory', which gives you a new instance of the data repository. This allows you to be the 'owner' of the instance, and you can safely dispose it. Note that the interaction with the DataContext should be minimal - you do your Unity of Work and get rid of it. Don't reuse! You will see it as an example below.
public class DataRepositoryFactory<T> where T : IDataRepository
{
private Type dataRepositoryImplementationType;
public DataRepositoryFactory(T dataRepositoryImplementation)
{
if (dataRepositoryImplementation == null)
{
throw new ArgumentException("dataRepositoryImplementation");
}
this.dataRepositoryImplementationType = dataRepositoryImplementation.GetType();
}
public T Create()
{
return (T)Activator.CreateInstance(this.dataRepositoryImplementationType);
}
}
In your controller (if it were MVC app), or Page backend (forms), it would be best if you use Microsoft Unity to get an instance of DataRepositoryFactory. For now, a manual construction would suffice too.
IDataRepository dataRepository = new EfDataRepository();
var dataRepositoryFactory = new DataRepositoryFactory<IDataRepository>(dataRepository);
Also, you don't need all this Transaction/Commit stuff you have put. It should be transparent for you. EF supports it implicitly, you don't have to be explicit about it.
// See, now you are the 'owner' of the dataRepository
using (var dataRepository = this.dataRepositoryFactory.Create())
{
if (Mode == "New")
{
Order newOrder = new Order();
// This doesn't make sense. Either generate a random order number (e.g. a Guid), or just use the Order.Id as an order number, although I don't recommend it.
int maxOrderNumber = dataRepository.Orders.Select(o => o.OrderNumber).DefaultIfEmpty(0).Max();
maxOrderNumber++;
newOrder.OrderNumber = maxOrderNumber;
newOrder.Date = DateTime.ParseExact(txtOrderDate.Text, "dd/MM/yyyy", CultureInfo.InvariantCulture);
newOrder.CustomerID = Convert.ToInt32(ddlCustomer.SelectedValue);
newOrder.Status = 1;
dataRepository.Orders.Add(newOrder);
foreach (DataRow dt in dtOrderDetails.Rows)
{
OrderDetails newOrderDetails = new OrderDetails();
newOrderDetails.OrderNumer = maxOrderNumber;
newOrderDetails.ProductId = Convert.ToInt32(dt["ProductId"]);
newOrderDetails.Quantity = Convert.ToInt32(dt["Quantity"]);
newOrder.OrderDetails.Add(newOrderDetails);
}
myOrder = newOrder;
}
if (Mode == "Edit")
{
Order editedOrder = dataRepository.Orders.FirstOrDefault(o => o.Id == myOrder.Id);
editedOrder.Date = DateTime.ParseExact(txtOrderDate.Text, "dd/MM/yyyy", CultureInfo.InvariantCulture);
editedOrder.CustomerID = Convert.ToInt32(ddlCustomer.SelectedValue);
editedOrder.OrderDetails.Clear();
foreach (DataRow dt in dtOrderDetails.Rows)
{
OrderDetails editedOrderDetails = new OrderDetails();
editedOrderDetails.OrderNumer = editedOrder.OrderNumber;
editedOrderDetails.ProductId = Convert.ToInt32(dt["ProductId"]);
editedOrderDetails.Quantity = Convert.ToInt32(dt["Quantity"]);
editedOrder.OrderDetails.Add(editedOrderDetails);
}
}
dataRepository.Save();
}
Also, I am pretty sure you have setup the relation between Order and OrderDetails classes incorrectly, in your EF code-first approach.
This is just wrong:
OrderDetails newOrderDetails = new OrderDetails();
newOrderDetails.OrderNumer = maxOrderNumber;
If you post them here, I can fix them for you.
static void Main(string[] args)
{
bool bMethod1 = Metthod1();
bool bMethod2 = Metthod2();
bool bMethod3 = Metthod3();
if (!bMethod1 || !bMethod2 || !bMethod3)
{
//RollBack
}
}
I have a situation, where I want to rollback the work done by methods if any of the method returns false. I'm not doing any database related activity in my code, So is there any way to rollback/undo changes in C# 4.0 or above.
I tried to use TransactionScope, but its not doing rollback.
Other way I have thought of implementing my own rollback method which will manually undo all the changes(like if the file is copied, I will check the destination file and delete it using code). So is there any other workaround to solve this?
I have tried this so far.
static void Main(string[] args)
{
using (TransactionScope scope = new TransactionScope())
{
bool bMethod1 = Metthod1();
bool bMethod2 = Metthod2();
bool bMethod3 = Metthod3();
if (!bMethod1 || !bMethod2 || !bMethod3)
{
//RollBack
Transaction.Current.Rollback();
}
if (Transaction.Current.TransactionInformation.Status == TransactionStatus.Committed)
{
scope.Complete();
}
}
}
I think you better imlement Command Pattern yourself.
Your 'transaction' if you can say so, basicaly is list of commands. Those commands can be executed only all at once. It is not so complex to implement.
Here is what I used when I needed this behavior:
public struct Command
{
public Action Upgrade;
public Action Downgrade;
}
public static void InvokeSafe(params Command[] commands)
{
var completed = new Stack<Command>();
try
{
foreach (var cmd in commands)
{
cmd.Upgrade();
completed.Push(cmd);
}
}
catch (Exception up)
{
try
{
foreach (var cmd in completed)
{
cmd.Downgrade();
}
}
catch (Exception down)
{
throw new AggregateException(up, down);
}
throw;
}
}
Then you call it like this:
int value = 3;
int modifier = 4;
InvokeSafe(new Command
{
Upgrade = () => { value += modifier; },
Downgrade = () => { value -= modifier; }
},
new Command
{
Upgrade = () => { value *= modifier; },
Downgrade = () => { value /= modifier; }
});
I'm using a SQLite database in a Windows 8 Store Application.
Furthermore I use the SQLite wrapper sqlite-net to do operations on the database.
I will explain this in detail so that you understand my situation.
There are Model classes that contain the business logic, for example the class Location. Then there are DatabaseModels from which the Database Tables are generated, for example LocationDb.
I don't want to implement the basic database methods like Insert(Model model), Update(Model model), Delete(int id), GetAllRecords() and GetRecord(int id) in every Model class. Instead I want to implement these methods in a base class of all models. This base class of all models is called ModelBase; the base class of all database Models is DbModelBase.
There is no problem implementing the Insert method as follows for all models:
public int Insert(ModelBase model)
{
int id;
using (var db = new SQLite.SQLiteConnection(MainApplication.DBPath))
{
db.Insert(model.GetDbModelFromModel());
id = (int)SQLite3.LastInsertRowid(db.Handle);
}
return id;
}
But I don't know how to implement the other ones using sqlite-net.
I need to find a specific data record in a specific table. I have a model object which contains the id. But how to make one method work with all model classes without explicitly calling the specific table?
The following code works for a single specific database table...
using (var db = new SQLite.SQLiteConnection(MainApplication.DBPath))
{
var queryResult = db.Table<LocationDb>().Where(l => l.Id == model.Id);
if (queryResult.Count() == 1)
{
db.Update(new TeacherDb(model));
}
}
... but I can not write
var queryResult = db.Table<typeof(databaseModel)>().Where(t => t.Id == model.Id);
Write a generic function for that.
What about this solution:
public class BaseModel<T> where T : Type
{
protected T Id;
/// <summary>
/// Adds a new record to data base. It doesn't check for record existance.
/// </summary>
/// <returns></returns>
protected BaseModel<T> Add()
{
try
{
var entities = Variable.CurrentEntities;
var dbSet = entities.Set<BaseModel<T>>();
var result = dbSet.Add(this);
entities.SaveChanges();
return result;
}
catch (Exception exception)
{
LogException.HandleException(exception);
return null;
}
}
protected bool Update()
{
try
{
var entities = Variable.CurrentEntities;
var dbSet = entities.Set<BaseModel<T>>();
var original = dbSet.Find(this);
entities.Entry(original).CurrentValues.SetValues(this);
entities.SaveChanges();
return true;
}
catch (Exception exception)
{
LogException.HandleException(exception);
return false;
}
}
protected BaseModel<T> Delete()
{
try
{
var entities = Variable.CurrentEntities;
var dbSet = entities.Set<BaseModel<T>>();
var result = dbSet.Remove(this);
entities.SaveChanges();
return result;
}
catch (Exception exception)
{
LogException.HandleException(exception);
return null;
}
}
protected bool Upsert()
{
try
{
var entities = Variable.CurrentEntities;
var dbSet = entities.Set<BaseModel<T>>();
dbSet.AddOrUpdate(this);
return true;
}
catch (Exception exception)
{
LogException.HandleException(exception);
return false;
}
}
protected BaseModel<T> Save()
{
try
{
var entities = Variable.CurrentEntities;
var dbSet = entities.Set<BaseModel<T>>();
var original = dbSet.Find(Id);
if (original == null)
{
original = dbSet.Add(this);
}
else
{
entities.Entry(original).CurrentValues.SetValues(this);
}
entities.SaveChanges();
return original;
}
catch (Exception exception)
{
LogException.HandleException(exception);
return null;
}
}
}