Using TransactionScope with Entity Framework code first and universal providers - asp.net-membership

I'm trying to set up a transaction where my code creates a new user using the membership provider and then goes on to create an object and put it into one of my Entity Framework tables. If the EF operation fails, I want to be able to rollback to before the user was created. I have a single connection string for both EF and membership, so I think both operations should be using the same sql connection.
When I first run it, I get an
"MSDTC on server ... is unavailable."
exception on the Membership.CreateUser line. When I start the DTC service, I get an
"The underlying provider failed on open"
exception with an inner exception
"The operation is not valid for the state of the transaction."
on the same line. If I change the order around and do the EF save first and then the membership, the EF part works, but CreateUser fails with the same exceptions.
It seems like 2 sql connections are being used even though I have one connection string. Is there a way to force both the membership and EF operations to use the same connection or is there some other way to put this inside of a transaction?
Here's the code
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
MembershipCreateStatus createStatus;
MembershipUser user = Membership.CreateUser(model.UserName, model.Password, model.UserName, null, null, true, null, out createStatus);
//add objects to the DbContext db
db.SaveChanges();
scope.Complete();

As u say even u use the same connection string for both EF and membership they use different connections then transaction scope automatically escalating to DTC
And as far i know for use same connection for both
it is not possible to put a custom connection into the SqlMembershipProvider
according to this link
to avoid involving DTC

Related

Multiple entity framework 6 contexts in oracle throw ORA-00955

I am using Entity Framework 6.1.3 connecting to an oracle database.
I am trying to use multiple contexts with the same schemaName for oracle. But when I create the contexts it appears they share the __MigrationHistory table and when the second context attempts to create itself it throws "ORA-00955: name is already used by an existing object". To be clear the two contexts I attempted to split by domain design and do not share any entities between the two.
This is the code I'm attempting to run, and it works fine when I run it against SQL Server. But Oracle throws the ORA-00955 error.
try
{
using (var hContext = new HContextORCL(connectionString, "SchemaName"))
using (var aContext = new AContextORCL(connectionString, "SchemaName"))
{
hContext.Database.Initialize(true);
aContext.Database.Initialize(true);
}
}
I've tried using CreateIfNotExists() instead of the Initialize but receive the same error. I have tried setting both contexts Database.SetInitializer<context>(null); because I don't need the migrations at this point. But that doesn't seem to work either.
Ideally I would like to keep the __MigrationHistory table and have both my contexts initialized in Oracle. But that's not necessary. I can sense myself going off the rails trying to figure out all these work-arounds which when I look at them seem overly complicated for something that works in SQL Server.
I am at a loss how to intialize two contexts with the same schema name in an oracle database.
Alright, for better or worse this is the work around I'm going with. My issue appears to be the MigrationHistory table is shared between the two contexts.
Entity Framework 6 allows you to manipulate the migration table using the System.Data.Entity.Migrations.History namespace (Migrations.History Namespace).
So I open the migrations table, copy all the history rows out, delete the migration table, perform my second context initialization and copy the history rows back into the migration database (created during the second context initialization).
try
{
var dbConnection = new OracleConnection(connectionString);
using (var migrationContext = new Migrations.History.HistoryContext(dbConnection, "SchemaName"))
using (var hContext = new HContextORCL(connectionString, "SchemaName"))
using (var aContext = new AContextORCL(connectionString, "SchemaName"))
{
hContext.Database.Initialize(true);
List<HistoryRow> currentHistory = migrationContext.History.ToList();
migrationContext.Database.Delete();
aContext.Database.Initialize(true);
currentHistory.ForEach(rowItem => migrationContext.History.Add(rowItem));
migrationContext.SaveChanges();
}
}

WebAPI + EF5, Entity framework does not recover from a DBUpdateException and requires a full restart of the service

I have two table in my database. Table USER and table USER_ADDRESSES
Table USER has UserID , UserName , HumanName
Table USER_ADDRESSES has UserAddresseID, UserID, Address
The two tables are related through the UserID foreign key.
I am working code first. The POCOs are as one would expect I guess:
Public class User
{
Public guid UserID {get;set;}
Public string UserName {get;set;}
Public string HumanName {get;set;}
}
Public class UserAddress
{
Public guid UserAddressID {get;set;}
Public guid UserID {get;set;}
Public string Address {get;set;}
Public virtual User User {get;set;}
}
If I POST a user, it flows through EF and into the physical database - cool
If I POST a UserAddress with the UserID foreign key properly assigned as the UserID of the above user, the userAddress flows through EF into the physical database - perfect.
Now as I am learning this subject, I like to test scenarios that I know will cause errors… Understanding how something fails often leads to greater understanding (IMHO).
To this end, I repeat the above steps.POST a user, then POST an Address but this time with an empty GUID in the Address.UserID property, something I know will cause SQL server to error on,this time EF gets an error back from the DB and surfaces that error in my controller class as a DbUpdateException, which I then act upon to send back a BadRequest ErrorResponse, this would be fine except now I can post nothing to the DB, If I subsequently try to post new Users EF throws a DbUpdateException on every POST. The only way out of this condition is to Stop debugging the WebAPI project, kill IISExpress and restart everything.
My suspicion is that it is something to do with the tracked entities in dbset.Local it appears this collection is happy to store entities which violate database constraints, and even after the DB has thrown an error EF does not remove the faulted entity from its cache, and I guess that every time I call DataContext.SaveChanges(), EF keeps trying to submit the Faulty address entity that it has in it's bdset.Local property and thus prevents me from submitting anything further to the DB until the whole project is restarted and the various dbset.Local collections are flushed.
Ok I understand that, In my controller class I can prevent some of these issues by evaluating the various properties of the object before submitting them to my repository/UnitOfWork and thus prevent them from getting into dbset.Local in the first place, perhaps there is some decoration I can add to my POCOs such that ModelState.IsValid returns false if UserID == Guid.Empty in the UserAddress Entity??? . But surely EF should be able to recover from DB originated errors, such that if an INSERT statement fails when run against SQL server, EF should remove the faulting Entity from its Local cache of tracked objects. Thus preventing the whole system from requiring a complete restart. The upshot of this is that if I had unique constraints on other non key fields, before I attempt to submit an entity I will have to query the DB to ensure that there are no records in the DB which would result in a DBUdateException from being thrown, that approach seems inefficient to me. I would rather take the error and throw it back to the client for them to consider.
Now perhaps it's my responsibility that on the occurrence of a DbUpdateException to do something to remove the faulted entity from the dbset.Local collection or similar. If that's the case how should I go about this?
As I have been writing this I am left wondering: Should EF not be able to recover from DBUdateExceptions in and of its self!? Without the need for an IIS restart. Or should I build my DB with no constraints what so ever and allow clients to put any old rubbish in there. It's my believe that EF Entity tracking should honour db constraints and prevent faulted entities from getting into the dbset in the first place.
Please guide me on the correct way to recover from a DBUpdateException?

How to link an entity with database?

I have a complex entity with many relations, so I need many forms in many pages to create one. Therefore, I use a session to keep my entity.
Everything is going okay, but when comes the time to flush, the entity manager returns the "entity through relationship is not configured to cascade persist" thinking that some entities are new but they're actually stored in db !
For instance, I create a User with a ManyToOne Group, using $u->setGroup(Group $group); ($group being an existing group from the db). When I put it in session, then get it back in another page and then flush it, the entity manager tries to create a whole new group, not knowing that it is an existing one in db.
I use a little trick to overcome this :
$u = $this->get('session')->get('userToAdd');
$group = $em->getRepository('MyBundle\Entity\Group')->find($u->getGroup()->getId());
$u->setGroup($group);
With this, EM will recognize the group stored in db and the flush will go just fine, but with my entity having so much relationships like this, it is very convenient to do this for every single one.
Any ideas for this issue ?
Before find group try to refresh $u object.
$em->refresh($u)
You have to do:
$em->merge($u);

Does instantiating a DbContext From EF read the whole database?

When instantiating EntityFramework's DbContext in MVC3, does the whole database get read? When debugging, it is possible to access all of the data in the entire database by looking at the instantiated DbContext so wouldn't this imply that all the data is grabbed when the first connection is made?
No definitly not. The data is loaded, when you access the DbSet/ObjectSet properties of your DbContext.
Only the data you query for is loaded from the db and mapped to objects. For example, when you query like
DbContext.Table.Where(row => row.Prop1 == "Value")
Its translated to SQL, evaluated at the database and only the rows that match your query are returned to your app.
No, the entire database is not read on instantiation. The entity collections (DbSet<>) in your DbContext are lazy evaluated. So when you're debugging and navigate into one, it's being queried then, not when the DbContext instance is instantiated.
No, Entity Framework tries to only query the database when you need information, or you need to modify information.
The following example is my personal interpretation of what I think EF is doing behind the scenes. It's probably somewhat inaccurate, but serves a point for illustration purposes.
using(var db = new MyDbContext()) // 1
{
var entities = db.MyEntities; // 2
foreach(var entity in entities) // 3
{
// 4
} // 5
} // 6
Establish connection with database
Get some object representing a query that says "Get me all dem entities."
Enumerate the object. Aka, have the context's query provider translate "get me all dem entites" to (assuming sql) "select * from MyEntity with (nolock);", and run the query returning an ADO.NET SqlDataReader, which will read the first row, map the object into an (optionally) lazy object with some metadata, so EF knows what row it's mapped to, etc, and yeild that as the "entity" variable.
Do something with the entity you magically received from the database without actually doing any real work (thank Entity Framework!)
Ask EF to move the SqlDataReader to the next row and yield another entity (aka, go back to step 3, but only if another row exists)
Close the connection with the database.

What reasons could cause WebSecurity.ChangePassword() to FAil?

I'm working on a Razor project and need to integrate an existing User database into the SimpleMembership Provider DB Schema. This is done by specifying my existing User table and which columns are to be used by the SimpleMembership API for the Username and UserID.
WebSecurity.InitializeDatabaseConnection("DB_ConnStr", "User", "UserId", "Username", true);
In the process though, I am populating the webpages_Membership table with a new record for each User row in my existing database. This has gone fine and I have written some code to handle the inserts for each existing user.
During the insert, I use a dummy encrypted password token for simplicity and set the password to be the same for everyone. Then I need to run another script over the records to set the correct password for each user in the webpages_Membership table. This involves decrypting the current password from the existing User table, and then calling:
WebSecurity.ChangePassword( username, dummyPwd, newPwd)
on each user, passing the decrypted current password as the 'newPwd' parameter.
This works fine in 99% of the cases that it's called - for over 100,000 records. But it is failing in about 40 cases.
What reasons could cause this method to fail?
My first guess would be that the hash of the new password might be exceeding the 128 character limit.
When the ChangePassword call fails, can you catch the exception to get details behind the reason of the failure?

Resources