I have a problem with some simple code, I'm refactoring some existing code from LINQ to SQL to the Entity Framework. I'm testing my saves and deletes, and the delete is really bugging me:
[TestMethod]
public void TestSaveDelete()
{
ObjectFactory.Initialize(x =>
{
x.For<IArticleCommentRepository>().Use<ArticleCommentRepository>();
});
PLArticleComment plac = new PLArticleComment();
plac.Created = DateTime.Now;
plac.Email = "myemail";
plac.Name = "myName";
plac.Text = "myText";
plac.Title = "myTitle";
IArticleCommentRepository acrep = ObjectFactory.GetInstance<IArticleCommentRepository>();
try
{
PortalLandEntities ple = new PortalLandEntities();
int count = ple.PLArticleComment.Count();
acrep.Save(plac);
Assert.AreEqual(ple.PLArticleComment.Count(), count + 1);
//PLArticleComment newPlac = ple.PLArticleComment.First(m => m.Id == plac.Id);
//ple.Attach(newPlac);
acrep.Delete(plac);
Assert.AreEqual(ple.PLArticleComment.Count(), count + 1);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Every time i try to run this code, I get an exception in the delete statement, telling me that its not contained within the current ObjectStateManager.Please note that both my Save and delete looks like this:
public void Delete(PLCore.Model.PLArticleComment comment)
{
using (PortalLandEntities ple = Connection.GetEntityConnection())
{
ple.DeleteObject(comment);
ple.SaveChanges();
}
}
public void Save(PLCore.Model.PLArticleComment comment)
{
using (PortalLandEntities ple = Connection.GetEntityConnection())
{
ple.AddToPLArticleComment(comment);
ple.SaveChanges();
}
}
and the connection thingy:
public class Connection
{
public static PortalLandEntities GetEntityConnection()
{
return new PortalLandEntities();
}
}
Any ideas on what i could do to make it work?
You cannot load an entity from one ObjectContext (in your case, an ObjectContext is an instance of PortalLandEntities) and then delete it from another ObjectContext, unless you detach it from the first and attach it to the second. Your life will be much, much simpler if you use only one ObjectContext at a time. If you cannot do that, you must manually Detach and then Attach first, all the while keeping track of which entities are connected to which ObjectContext.
How to use DI with your Connection : make it non-static.
public class Connection
{
private PortalLandEntities _entities;
public PortalLandEntities GetEntityConnection()
{
return _entities;
}
public Connection(PortalLandEntities entities)
{
this._entities = entities;
}
}
Then use a DI container per request. Most people do this via a controller factory.
Related
I am using Entity Framework code-first and it's behaving very strangely when I use transactions.
In the code below you will find a simple example. As you can see - even though transactions are created - they are never committed. Despite NOT having a commit - my first transaction is being saved to the database in about 50% of unit test runs.
Sometimes data only saved in DB in 10% of runs. I have no reasonable explanation and loosing my mind trying to solve it..
Code:
[TestMethod]
public void TestConcurrentTransactions2()
{
int invoiceId = 1560;
var context1 = new ApplicationDbContext();
var transaction1 = context1.Database.BeginTransaction();
var invoice1 = new OracleDatabaseService.Models.Invoice()
{
InvoiceId = (++invoiceId).ToString(),
ClientId = "2",
ExternalSystemId = "0"
};
context1.Invoices.Add(invoice1);
context1.SaveChanges();
var context2 = new ApplicationDbContext();
var transaction2 = context2.Database.BeginTransaction();
var invoice2 = new OracleDatabaseService.Models.Invoice()
{
InvoiceId = (++invoiceId).ToString(),
ClientId = "2",
ExternalSystemId = "0"
};
context2.Invoices.Add(invoice2);
context2.SaveChanges();
//transaction2.Commit();
}
I'm using EF 6.2.0 and an Oracle-managed data access provider.
This behavior seems to be very situational and the only way to reproduce it is by re-running the test case over and over again. I seem to be able to repro the issue more often if I go and change invoiceId while the previous test hasn't finished executing. In this case - the test will still finish executing successfully but a record in DB will appear. Though in most cases, this doesn't really matter and data will be pushed to DB randomly.
So my questions are:
Is this an OK behavior that transaction is being committed automatically?
How do I force EF to only commit transactions when I need to?
Is there an alternative mechanism to handle transactions in EF?
UPDATE 23.08.2018
So, here is a code which almost certainly will repro the issue at least once per run:
[TestMethod]
public void Test1()
{
int invoiceId = 3350;
Parallel.For(0, 30, i =>
{
var context1 = new ApplicationDbContext();
var transaction1 = context1.Database.BeginTransaction();
var invoice1 = new OracleDatabaseService.Models.Invoice()
{
InvoiceId = (invoiceId + i).ToString(),
ClientId = "2",
ExternalSystemId = "0"
};
context1.Invoices.Add(invoice1);
context1.SaveChanges();
//transaction1.Commit();
});
}
Here is a fix attempt that seems to be working, though I don't fit into a service code pattern very well. I need to be able to come back to a service later and either roll-back or commit a transaction.
[TestMethod]
public void Test2()
{
int invoiceId = 3350;
Parallel.For(0, 30, i =>
{
using (var context = new ApplicationDbContext())
{
var transaction = context.Database.BeginTransaction();
var invoice = new OracleDatabaseService.Models.Invoice()
{
InvoiceId = (invoiceId + i).ToString(),
ClientId = "3",
ExternalSystemId = "0"
};
context.Invoices.Add(invoice);
context.SaveChanges();
//transaction.Commit();
}
});
}
This is my attempt to implement a service that uses DBContext. As you will see in the code - a descructor will check whether there is a context or transaction present and dispose of them. Seems to be working OK. Though, what will happen if any of the parallel processes fail? Is the descructor called then? I need somebody to review the code, possibly.
public class DbService
{
ApplicationDbContext _context;
DbContextTransaction _transaction;
public DbService()
{
_context = new ApplicationDbContext();
_transaction = _context.Database.BeginTransaction();
}
public void InsertInvoice(int invoiceId)
{
var invoice1 = new OracleDatabaseService.Models.Invoice()
{
InvoiceId = (invoiceId).ToString(),
ClientId = "3",
ExternalSystemId = "0"
};
_context.Invoices.Add(invoice1);
_context.SaveChanges();
}
~DbService()
{
if (_transaction != null)
{
_transaction.Rollback();
_transaction.Dispose();
}
if (_context != null)
{
_context.Dispose();
}
}
}
and test:
[TestMethod]
public void Test3()
{
int invoiceId = 3350;
Parallel.For(0, 30, i =>
{
var srvc = new DbService();
srvc.InsertInvoice(invoiceId + i);
});
}
As was suggested by #WynDysel in a comment section - the problem is resolvable by putting a context in a using block.
The actual reasons for the issue are still unknown to me. It looks logical, that unless something is explicitly said to be committed - to be committed. Well, I guess I have to live with this solution for now.
Perhaps I should make some clarifications about the reasons why I was not using the using block to begin with. It's because the DbContext is used from within a service. Within a service there are multiple operations being done in scope of the same transaction.
To multiple entities of database. So when the code is ready for commit - a Commit() method is executed and all of the changes done are pushed to DB at once. Otherwise if something goes wrong along the way, then all of the changes are rolled back. So for this I needed a service and normally am not allowed to use a using block by design.
To make a long story short - I will be using following service for managing context and transaction.
public class DbService : IDisposable
{
private bool _isDisposed = false;
private ApplicationDbContext _context;
private DbContextTransaction _transaction;
public DbService()
{
_context = new ApplicationDbContext();
_transaction = _context.Database.BeginTransaction();
}
public void InsertInvoice(int invoiceId)
{
try
{
var invoice1 = new OracleDatabaseService.Models.Invoice()
{
InvoiceId = (invoiceId).ToString(),
ClientId = "3",
ExternalSystemId = "0"
};
_context.Invoices.Add(invoice1);
_context.SaveChanges();
}
catch (Exception)
{
Dispose(false);
throw;
}
}
public void Commit(bool isFinal)
{
if (!_isDisposed)
{
_transaction.Commit();
if (isFinal)
{
Dispose(false);
}
else
{
_transaction.Dispose();
_transaction = _context.Database.BeginTransaction();
}
}
}
public void Rollback(bool isFinal)
{
if (!_isDisposed)
{
if (isFinal)
{
Dispose(false);
}
else
{
_transaction.Rollback();
_transaction.Dispose();
_transaction = _context.Database.BeginTransaction();
}
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_isDisposed)
{
if (disposing)
{
// Free other state (managed objects).
}
if (_transaction != null)
{
if (_transaction.UnderlyingTransaction.Connection != null)
{
_transaction.Rollback();
}
_transaction.Dispose();
}
if (_context != null)
{
_context.Dispose();
}
_isDisposed = true;
}
}
~DbService()
{
Dispose(false);
}
}
It is still possible to use the service in a using block. If something along the way goes wrong then a destructor shall be called to roll back the transaction and dispose of a context. There are 2 helper methods for committing and rolling back chnages manually. It could either be a final commit when the service is no longer needed or a temporary commit of current transaction and initialization of a new transaction while keeping an integrity of a service.
InsertInvoice method's contexts are also wrapped in a try/catch block in case something unexpected goes wrong.
I can't afford to insert any pending transaction data on a production environment so am taking all possible precautions! Perhaps I will be asking a question on Github about this issue Entity Framework creators themselves.
Update #1
It is very unfortunate, but the code I provided above does NOT guarantee that records will not be inserted. You have to make some additional validations, when using the service.
For example, this testcase will cause the data to be inserted into database sometimes:
[TestMethod]
public void TestFail()
{
int invoiceId = 3700;
Parallel.For(0, 30, i =>
{
var srvc = new DbService();
srvc.InsertInvoice(invoiceId + i, i);
if (i > 15)
{
throw new Exception();
}
});
}
And following code will guarantee disposal of context correctly:
[TestMethod]
public void TestGood()
{
int invoiceId = 3700;
Parallel.For(0, 30, i =>
{
DbService srvc = null;
try
{
srvc = new DbService();
srvc.InsertInvoice(invoiceId + i, i);
if (i > 25)
throw new Exception();
}
catch(Exception ex)
{
if (srvc != null)
srvc.Dispose();
throw ex;
}
});
}
What is the custom wen the record inserting/updating is carried out?
I have this Log table in the MS SQL server database, and a C# class (example is simplified)
[Table(Name = "dbo.Sys_Log")]
public class Sys_Log
{
// Read-only, db-generated primary key ID
private int _logID;
[Column(IsPrimaryKey=true, Storage="_logID", IsDbGenerated=true)]
public int logID
{
get
{
return this._logID;
}
}
// Read-only db-generated datetime field
private System.DateTime _logTime;
[Column(Storage="_logTime", IsDbGenerated=true)]
public System.DateTime logTime
{
get
{
return this._logTime;
}
}
// Read-write string field
private string _logEvent;
[Column(Storage="_logEvent")]
public string logEvent
{
get
{
return this._logEvent;
}
set
{
this._logEvent = value;
}
}
public Sys_Log() {}
public Sys_Log(string logEvent)
{
this.logEvent = logEvent;
}
}
And this is how I add a log entry:
Table<Sys_Log> linqLog = db.GetTable<Sys_Log>();
Sys_Log l = new Sys_Log("event");
linqLog.InsertOnSubmit(l);
db.SubmitChanges();
I am not particularly happy about this code. I'd like something like this instead:
Sys_Log.Log("event");
I have idea how this can be achieved, but I'd like to know if I am following the LINQ philosophy. With this code added to the Sys_Log class
private static DataContext db;
public static void Connect(DataContext db)
{
Sys_Log.db = db;
}
public static void Log(string logEvent)
{
Table<Sys_Log> linqLog = db.GetTable<Sys_Log>();
Sys_Log l = new Sys_Log(logEvent);
linqLog.InsertOnSubmit(l);
db.SubmitChanges();
}
I can now do this:
Sys_Log.Connect(db); // Only once, at init
Sys_Log.Log("event1");
Sys_Log.Log("event2");
Are there any pitfalls, apart from the fact that the database is updated several times, that could be considered ineffective?
************** Update ******************
Following the advice of #usr not to reuse the DataContext object, I have made these changes to the Sys_Log class:
private static SqlConnection db;
public static void Connect(SqlConnection db)
{
Sys_Log.db = db;
}
public static void Log(string logEvent)
{
DataContext ctx = new DataContext(db);
ctx.CommandTimeout = 240;
Table<Sys_Log> linqLog = ctx.GetTable<Sys_Log>();
Sys_Log l = new Sys_Log(logEvent);
linqLog.InsertOnSubmit(l);
ctx.SubmitChanges();
}
Use a fresh data context each time. Reusing the same context has to catastrophic consequences:
No entity memory is ever released
When an invalid entity enters the context (due to a bug) it is stuck and will forever prevent SubmitChanges from succeeding. The application will never recover
Also note, that L2S is deprecated and EF has superseded it.
You can share a SqlConnection and use it long-term if you really want. That requires, through, that you deal with broken connections. Thanks to connection pooling there are little performance incentives to do this.
It usually is the easiest and most clear way to use throw-away connections. Inject a factory, for example:
Func<SqlConnection> myFactory = () => new SqlConnection(myConnStr);
That's all there is to it. Use it, as always, with using:
using(var conn = myFactory()) { ... }
How can one set the TransactionHandler for ObjectContext?
I am checking this example: Handling of Transaction Commit Failures, but it only shows for DbContext.
TransactionHandler also works for ObjectContext. The only problem is that the code based configurations (DbConfiguration) are not evaluated before the first DbContext is instantiated.
Two possible workarounds
Dummy DbContext:
public class MyDbConfiguration : DbConfiguration
{
public MyDbConfiguration()
{
SetTransactionHandler(SqlProviderServices.ProviderInvariantName,
() => new CommitFailureHandler());
}
}
public class TestContext : DbContext { }
static void Main(string[] args)
{
// instantiate DbContext to initialize code based configuration
using (var db = new TestContext()) { }
using (var db = new TransactionHandlerDemoEntities()) {
var handler = db.TransactionHandler; // should be CommitFailureHandler
db.AddToDemoTable(new DemoTable { Name = "TestEntiry1" });
db.SaveChanges();
}
}
Or DbConfiguration.Loaded event
static void Main(string[] args)
{
DbConfiguration.Loaded += DbConfiguration_Loaded;
using (var db = new TransactionHandlerDemoEntities()) {
var handler = db.TransactionHandler;
db.AddToDemoTable(new DemoTable { Name = "TestEntiry1" });
db.SaveChanges();
}
}
static void DbConfiguration_Loaded(object sender, DbConfigurationLoadedEventArgs e)
{
e.AddDependencyResolver(new TransactionHandlerResolver(
() => new CommitFailureHandler(),
SqlProviderServices.ProviderInvariantName,
null),true);
}
TransactionHandlerDemoEntities is an ObjectContext.
This is exclusively for DbContext. If you can, refactor your ObjectContext-based application into DbContext as soon as possible. I think that many more new features will appear that only work with the DbContext API. Maybe ObjectContext will even get deprecated as a public API some day.
You can create a DbContext from an ObjectContext, but I don't think that's of much help to you. The main problem is undoubtedly that the rest of the data logic currently expects ObjectContext.
so I am trying to make a generic function for a where query, not using repository
so it is possible to do something like this?
public IEnumerable<T> Something<T>(int authorId) where T : class
{
return Vmsb.Set<T>().Where(c => c.AuthorId== authorId);
}
now I can't because it dont know what c.AuthorId is
Create an interface IHaveAuthor and specify it on partial classes with this property:
public interface IHaveAuthor
{
int AuthorId { get; set; }
}
//Note that the interface is already implemented in auto-generated part.
//Or if it's Code First, just specify it directly on your classes.
public partial class Book : IHaveAuthor
{
}
public partial class Article : IHaveAuthor
{
}
Then point the interface under the generic type where constraint:
public IEnumerable<T> GetAuthorPublicationsOf<T>(int authorId)
where T : class, IHaveAuthor
{
return Vmsb.Set<T>().Where(c => c.AuthorId == authorId);
}
And the usage:
var authorBooks = query.GetAuthorPublicationsOf<Book>(authorId);
var authorArticles = query.GetAuthorPublicationsOf<Article>(authorId);
Adding on to Olexander's answer, since EF recommends you use the Unit of Work pattern, I usually don't assume a DbContext in my methods - I pass in the most generic object possible instead. Also just as a matter of style, I like to return the interface.
EDIT Updated to include Olexander's important fix to use IQueryable instead of IEnumerable.
So my method signature would look like:
public IQueryable<IHaveAuthor> Something(int authorId, IQueryable<IHaveAuthor> items)
{
return items.Where(c => c.AuthorId == authorId);
}
So calling this would be a bit different than your current calls to it - presumably something like:
var db = new MyDbContext();
var items = db.Books;
var itemForAuthor1 = Something(1, items);
Otherwise your "Something" method isn't terribly flexible - it assumes a single existing DbContext on your current object which might not be a safe assumption (since it's only supposed to live as long as this small chunk of work, whatever it is), you can't chain it with other commands, etc.
Diego hope my code helps u.
protected List<T> ListAll<T>() where T : class
{
using (MyDbContext db = new MyDbContext ())
{
return db.Set(typeof(T)).Cast<T>().AsNoTracking<T>().ToList();
}
}
protected T ListAllById<T>(int id) where T : class
{
using (MyDbContext db = new MyDbContext ())
{
return db.Set(typeof(T)).Cast<T>().Find(id);
}
}
protected void InsertObj(Object obj)
{
using (MyDbContext db = new MyDbContext())
{
db.Set(obj.GetType()).Add(obj);
db.SaveChanges();
}
}
protected void UpdateObj(Object obj)
{
try
{
using (MyDbContext db = new MyDbContext())
{
db.Set(obj.GetType()).Attach(obj);
db.Entry(obj).State = EntityState.Modified;
db.SaveChanges();
}
}
catch (System.Exception ex)
{
System.Windows.Forms.MessageBox.Show(" " + ex.Message);
}
}
protected void DeleteObj(Object obj)
{
using (MyDbContext db = new MyDbContext ())
{
db.Set(obj.GetType()).Attach(obj);
db.Entry(obj).State = EntityState.Deleted;
db.SaveChanges();
}
}
I would like to use Entity Framework Code first approach with SQLCE4 database. Everything seems to be really nice but I have problem with debugging sql queries. I found that EFTracing from http://efwrappers.codeplex.com/ should be exactly what I need but I don't know how to use it without app.config file. I am not big fan of this configuration. I want to use only C# code to set everything up and running. I think it should be fine to use code like this:
using (System.Data.Common.DbConnection c =
new EFTracingProvider.EFTracingConnection(
new System.Data.SqlServerCe.SqlCeConnection(conn)))
{
using (var context = new MyContext(c))
{
var a = from data in context.Projects select data;
}
}
But it doesn't work. It throws exception:
Unable to determine the provider name for connection of type
EFTracingProvider.EFTracingConnection'.
Is there any simple way how to correctly create wrapped connection only in code?
Solution for my problem is following DbContext object.
public class MyContext : DbContext
{
public MyContext()
: base(CreateConnection("Data Source=file.sdf",
"System.Data.SqlServerCe.4.0"), true)
{ }
public DbSet<Project> Projects { get; set; }
public static bool TraceEnabled = true;
private static DbConnection CreateConnection(string connectionString,
string providerInvariantName)
{
DbConnection connection = null;
if (TraceEnabled)
{
EFTracingProviderConfiguration.RegisterProvider();
EFTracingProviderConfiguration.LogToConsole = true;
string wrapperConnectionString = String.Format(#"wrappedProvider={0};{1}",
providerInvariantName, connectionString);
connection = new EFTracingConnection()
{
ConnectionString = wrapperConnectionString
};
}
else
{
DbProviderFactory factory = DbProviderFactories.GetFactory(providerInvariantName);
connection = factory.CreateConnection();
connection.ConnectionString = connectionString;
}
return connection;
}
}
So now I can use just context and connection is created automatically for wrapped or unwrapped SqlCe depending on TraceEnabled property.
using (var context = new MyContext())
{
var a = context.Projects.FirstOrDefault();
}
The genuine way to trace SQL queries is to call the ToString method like that :
var t = from c in _entities.CompanyDetail
select c;
string test = t.ToString();
I don't know EFTracing, but you might want to try MVCMiniProfiler. Despite the name MVCMiniProfiler also provide SQL queries profiling and work without config file.
I've done this by creating a wrapper class around the ObjectContext and using that wrapper instead of the original context. Here's an example context wrapper:
public partial class LoggedContext : MyContext
{
public LoggedContext()
: this("name=MyEntities") // Adjust this to match your entities
{
}
public LoggedContext(string connectionString)
: base(EntityConnectionWrapperUtils.CreateEntityConnectionWithWrappers(connectionString)
{
}
private EFTracingConnection TracingConnection
{
get { return this.UnwrapConnection<EFTracingConnection>(); }
}
public event EventHandler<CommandExecutionEventArgs> CommandExecuting
{
add { this.TracingConnection.CommandExecuting += value; }
remove { this.TracingConnection.CommandExecuting -= value; }
}
public event EventHandler<CommandExecutionEventArgs> CommandFinished
{
add { this.TracingConnection.CommandFinished += value; }
remove { this.TracingConnection.CommandFinished -= value; }
}
public event EventHandler<CommandExecutionEventArgs> CommandFailed
{
add { this.TracingConnection.CommandFailed += value; }
remove { this.TracingConnection.CommandFailed -= value; }
}
}
I also have a static class that defines the tracing output method and has a static method to initialize tracing. Here:
public static class EFTracingExtensions
{
private static ILogger _logger;
public static void InitSqlTracing(ILogger logger)
{
_logger = logger;
EFTracingProviderConfiguration.RegisterProvider();
if (logger.IsLoggingEnabled()) // Don't add logging hooks if logging isn't enabled
{
EFTracingProviderConfiguration.LogAction = new Action<CommandExecutionEventArgs>(AppendSqlLog);
}
}
private static void AppendSqlLog(CommandExecutionEventArgs e)
{
if (e.Status != CommandExecutionStatus.Executing) // we only care about Finished and Failed
{
StringBuilder msg = new StringBuilder(e.ToTraceString().TrimEnd());
msg.Append(Environment.NewLine);
if (e.Result is SqlDataReader)
{
int rows = ((SqlDataReader)e.Result).HasRows ? ((SqlDataReader)e.Result).RecordsAffected : 0;
msg.AppendFormat("*** {0} rows affected", rows);
}
else if (e.Result is int)
{
msg.AppendFormat("*** result: {0}", e.Result);
}
else
{
msg.AppendFormat("*** finished, result: {0}", e.Result);
}
msg.Append(Environment.NewLine);
msg.AppendFormat(" [{0}] [{1}] in {2} seconds", e.Method, e.Status, e.Duration);
_logger.Log(msg.ToString(), LoggerCategories.SQL);
}
}
}
ILogger is the logging interface I'm using. You need to substitute your own interface/methods.
The InitSqlTracing method is invoked once when my program starts up, and then the LoggedContext class is used to log all the SQL generated by Entity Framework.
Putting it all together with your sample code:
EFTracingExtensions.InitSqlTracing(logger); // only call this once
using (var context = new LoggedContext())
{
var a = from data in context.Projects select data;
}