EntityFramework TransactionScope results in error if there's a call to asp.net security within its scope - entity-framework

I want to wrap some Entity Framework db I/o within a TransactionScope. In general it's working fine. But: I have some case that are basically like this:
using (var tx=new TransactionScope())
{
using (var cx=new MyDbcontext())
{
var myrecord=cx.somerecorord.find(someid);
if (Roles.IsUserInRole(username, "admin"))
DoAdminThing(myrecord);
else
DoNonAdminThing(myrecord);
cx.SaveChanges();
}
}
This blows up on Roles.IsUserInRole with "The provider did not return a ProviderManifestToken string."
It appears that it doesn't like to do the asp.net security db I/o within an EF TransactionScope. Is there any way around this?
I really really don't want to have to move the Roles stuff to before the transaction. That would require breaking functions that logically hang together into pieces.

Related

Entity Framework first Access to Database

Everytime when I open the database connection, the very first interaction with the database takes a lot of time. From the second interaction on the "speed" is much higher. I think it's an optimization of the Entity Framework and it looks like something good. But it's still the same even if I'm working with more than one database. The very first interaction with the first database is slow. But the first interaction with the second database is fast. Why isn't the first interaction with another database slow too? Another problem is that the Initializer only works with the first database. (I think this problem is caused by this optimization)
Related to this question (asked by myself):
Entity Framework 6 SetInitializer DbContext does not work for SQL Server Express and SQL Server Compact
Does anyone know how this optimization works and how to disable it? I don't actually work with the databases I compare them, for this case it would be nice to disable any optimization.
I'm wrote an answer as comment is not appropriate.
For the speed Yuliam pointed you to the good link.
For the rest, I'm not sure to understand your problem.
For me the following code (pertinent extract)
class Program {
static void Main(string[] args) {
Database.SetInitializer<TestEFContext>(new DropCreateDatabaseAlways<TestEFContext>());
String cs = #"Data Source=ALIASTVALK;Initial Catalog=TestEF;Integrated Security=True; MultipleActiveResultSets=True";
using (TestEFContext ctx = new TestEFContext(cs)) {
Console.WriteLine(ctx.Orders.Count());
}
cs = #"Data Source=ALIASTVALK;Initial Catalog=TestEF2;Integrated Security=True; MultipleActiveResultSets=True";
using (TestEFContext ctx = new TestEFContext(cs)) {
Console.WriteLine(ctx.Orders.Count());
}
}
}
well create and recreate databases TestEF and TestEF2.
Did I misunderstand your question ?
===== Response to comment:
In this case both databases are always (re)created:
first run: creation
use SSMS to populate some data in some tables
second run
check tables content with SSMS : tables are empty <=> databases were recreated at second run.
Only trivial difference with your case: I use EF 6;

Transaction Scope Isolation level and EF6

I am testing the EF6 isolation level but the test fails with:
Assert.AreEqual failed. Expected:<ReadUncommitted>. Actual:<Unspecified>.
The test:
public void TestIsolationLevelReadUncommitted()
{
// Arrange
using (
new TransactionScope(TransactionScopeOption.Required,
new TransactionOptions {IsolationLevel = IsolationLevel.ReadUncommitted}))
{
using (var context = new BoligEntities())
{
// Act
context.GetDbConnection().Open();
var isolationLevel = context.GetDbConnection().GetIsolationLevel();
// Assert
Assert.AreEqual(System.Data.IsolationLevel.ReadUncommitted, isolationLevel);
}
}
}
The test doesn't make much sense but I am wondering why it fails.
there is plenty of posts around transaction scope and EF.
Actually add uncommitted read and nolock to your searches.
Good basic explanation and example
FROM EF 6 on...
EF transaction scope docu
Generally you dont need it. ( there are exceptions)
and
i hope i dont have to support the system that uses uncommitted reads. ;-) Filthy...
good luck
Transaction is not the same as database. Looks like you are checking Isolation level for open connection but not for running transaction.
Generally speaking - you can open connection and run multiple transaction on this connection with different Isolation level.
using (var context = new MyEntities())
{
using (var tran = context.Database.BeginTransaction(System.Data.IsolationLevel.ReadUncommitted))
{
Assert.AreEqual(System.Data.IsolationLevel.ReadUncommitted, tran.UnderlyingTransaction.IsolationLevel);

Managing transactions between EntityFramework and EnterpriseLibrary's DatabaseFactory

I'm working with an existing set of code that manages multiple database updates in a single transaction. Here is a simplified example:
Database db = DatabaseFactory.CreateDatabase();
using (DbConnection dbConnection = db.CreateConnection())
{
dbConnection.Open();
DbTransaction dbTransaction = dbConnection.BeginTransaction();
try
{
//do work
dbTransaction.Commit();
}
catch (Exception ex)
{
dbTransaction.Rollback();
}
}
I am also using EntityFramework in this same project for new development. Below is a simplified example of the usage of my repository class:
List<ThingViewModel> things = new List<ThingViewModel>();
// populate list of things
IObjectRepository thingRepository = new ThingRepository();
thingRepository.AddThings(things);
thingRepository.Save();
I want the work done in 'AddThings' to happen as part of the transaction in the first block of code.
Is there some clean way of blending my repository pattern into this existing code or vice-versa? I'm not at the point yet where it is feasible to rewrite the existing code to be entirely within EntityFramework, so I'm looking for some some interim approach.
I have tried passing the transaction from the older code into the repository, and thus EntityFramework, but that does not seem to work. I have also tried passing the ObjectContext back out to the older code in order to enlist it in the transaction. Neither approach works.
I cannot believe that I am the first person to encounter this hurdle in migrating existing code to EntityFramework... there must be something I am not considering.
I'll list the things that I have tried below:
using (TransactionScope transactionScope = new TransactionScope())
{
Database db = DatabaseFactory.CreateDatabase();
using (DbConnection dbConnection = db.CreateConnection())
{
dbConnection.Open();
DbTransaction dbTransaction = dbConnection.BeginTransaction();
try
{
//do work
dbTransaction.Commit();
}
catch (Exception ex)
{
dbTransaction.Rollback();
}
}
Thing thing = new Thing(){
Prop1 = Val1,
Prop2 = Val2
};
ThingObjectContext context = new ThingObjectContext();
context.Things.AddObject(thing);
context.SaveChanges();
transactionScope.Complete();
}
This last example 'works', it does not function as a transaction. When the EF insert fails, the EL commands are not rolled back by the TransactionScope. If I don't put those explicit calls to .Commit() and .SaveChanges(), nothing happens. I would really like for this to share the same connection if possible. Two other variations of this I am currently playing around with is trying to use the same connection between both EF and EL as well as use EnlistTransaction on one side or the other. Definitely trying to keep this from becoming a MSDTC - don't want the extra overhead associated with that.
Use TransactionScope instead of explicit transaction management. You'll simplify the overall code and everything you do should automatically detect and use the same transaction.
Is there any way you can call Database.GetOpenConnection() instead of CreateConnection() in your EL code, and pass in the things.Connection that you create inside of a TransactionScope block? I haven't tested this, but that is what I would try first.

EF4.1 based Repository and consistent view of data

using the unit of work and repository patterns i recently came across the issue, that changes to the unit of work are not reflected to subsequent queries. Example:
var ctx = DIContainer.Current.Resolve<IB2bContext>();
var rep = DIContainer.Current.Resolve<IRepository<Word>>(
new DependencyOverride<IB2bContext>(ctx));
rep.Add(new Word () { "One" };
rep.Add(new Word () { "Two" };
rep.GetAll().ToList().ForEach(i =>
Console.Write(i.text)); // nothing seen here
So in other words, unless i call SaveChanges() to persist the objects into the Database, i dont see them. Well ofcause i can fiddle around with the ChangeTracker and/or do things like context.Entry(foo).Property(...).CurrentValue. But does that play with a ddd like decoupling of layers? I dont think so. And where is my consistent dataview that once was called a database transaction?
Please enlighten me.
Armin
Your repository exposes some GetAll method. The method itself executes database query. If you want to see local data not inserted to database you must add them to result set. For example like:
public IEnumerable<Word> GetAll()
{
DbSet<Word> set = context.Set<Word>();
return set.AsEnumerable().Concat(set.Local);
}
The query execution is only responsible for returning persisted (real) data.

EF 4 Self Tracking Entities does not work as expected

I am using EF4 Self Tracking Entities (VS2010 Beta 2 CTP 2 plus new T4 generator). But when I try to update entity information it does not update to database as expected.
I setup 2 service calls. one for GetResource(int id) which return a resource object. the second call is SaveResource(Resource res); here is the code.
public Resource GetResource(int id)
{
using (var dc = new MyEntities())
{
return dc.Resources.Where(d => d.ResourceId == id).SingleOrDefault();
}
}
public void SaveResource(Resource res)
{
using (var dc = new MyEntities())
{
dc.Resources.ApplyChanges(res);
dc.SaveChanges();
// Nothing save to database.
}
}
//Windows Console Client Calls
var res = service.GetResource(1);
res.Description = "New Change"; // Not updating...
service.SaveResource(res);
// does not change anything.
It seems to me that ChangeTracker.State is always show as "Unchanged".
anything wrong in this code?
This is probably a long shot... but:
I assume your Service is actually in another Tier? If you are testing in the same tier you will have problems.
Self Tracking Entities (STEs) don't record changes until when they are connected to an ObjectContext, the idea is that if they are connected to a ObjectContext it can record changes for them and there is no point doing the same work twice.
STEs start tracking once they are deserialized on the client using WCF, i.e. once they are materialized to a tier without an ObjectContext.
If you look through the generated code you should be able to see how to turn tracking on manually too.
Hope this helps
Alex
You have to share assembly with STEs between client and service - that is the main point. Then when adding service reference make sure that "Reuse types in referenced assemblies" is checked.
The reason for this is that STEs contain logic which cannot be transfered by "Add service reference", so you have to share these types to have tracing logic on client as well.
After reading the following tip from Daniel Simmons, the STE starts tracking. Here is the link for the full article. http://msdn.microsoft.com/en-us/magazine/ee335715.aspx
Make certain to reuse the Self-Tracking Entity template’s generated entity code on your client. If you use proxy code generated by Add Service Reference in Visual Studio or some other tool, things look right for the most part, but you will discover that the entities don’t actually keep track of their changes on the client.
so in the client make sure you don't use add service reference to get the proxy instead access service through following code.
var svc = new ChannelFactory<IMyService>("BasicHttpBinding_IMyService").CreateChannel();
var res = svc.GetResource(1);
If you are using STEs without WCF you may have to call StartTracking() manually.
I had the same exact problem and found the solution.
It appears that for the self-tracking entities to automatically start tracking, you need to reference your STE project before adding the service reference.
This way Visual Studio generates some .datasource files which does the final trick.
I found the solution here:
http://blogs.u2u.be/diederik/post/2010/05/18/Self-Tracking-Entities-with-Validation-and-Tracking-State-Change-Notification.aspx
As for starting the tracking manually, it seems that you do not have these methods on the client-side.
Hope it helps...