Does EF Core support TransactionScope? - entity-framework-core

I have .NET Core 3.1 and EntityFrameworkCore 3.1.8.
In a command (which writes data to db) I use SaveChanges() method multiple times. Therefore, I decided to make all these transactional and use TransactionScope() like this:
using (var scope = new TransactionScope())
{
}
Is this a correct approach? Will this work correctly?
Note: I have one DbContext

Yes, TransactionScope would most likely work for you. As a personal recommendation, I would use DbContext.Database.BeginTransaction() instead.
The reason for this is because TransactionScope must be implemented by the library you are using. In general, SDKs for relational databases should support TransactionScope (I know Microsoft.Data.SqlClient for SQL Server does, not sure about the other databases).
Source: https://learn.microsoft.com/en-us/ef/core/saving/transactions#using-systemtransactions

Related

.net core 2.1 multiple DbContext for same database

I am using .Net core 2.1 + EF Core to build WebApi. Based on this discussion: Entity Framework: One Database, Multiple DbContexts. Is this a bad idea? and the bounded context concept from DDD I want to create multiple DbContext for different functionalities in my app. There are several resources provided by Julie Lerman on PluralSight.com but non of those cover dependency injections in .net core. So far I did something like this:
var connectionString = Configuration.GetConnectionString("DatabaseName");
services.AddDbContext<DatabaseContext>(options =>
options.UseSqlServer(connectionString, optionsBuilder =>
optionsBuilder.MigrationsAssembly("Database.Migrations")));
services.AddDbContext<ProductContext>(options =>
options.UseSqlServer(connectionString));
services.AddDbContext<CountryContext>(options =>
options.UseSqlServer(connectionString));
here DatabaseContext is a context used for EF Core migrations (does not actually query the data) and ProductContext and CountryContext are two of the contexts I use for my data manipulation. My questions are:
Is this a right way to do it? It feels a bit odd to re-use same connection string
It feels like I would be using 2 separete connections (and this will grow), could this cause some data access issues in the future, locks, concurency, etc?
UPDATE (2022-11-11)
I have been using this for few years now. Did not had any issues related to DB connection so far. Planning to use this this approach in future projects as well.
The concept of having bounded contexts is fine, per se. It's based on the idea that the particular application shouldn't have access to things it doesn't need. Personally, I think it's a tad bit of overkill, but reasonable people can disagree on the issue.
However, what you have here, is simply wrong. If your application needs access to all these bounded contexts, it makes no sense to have them. Just feed it one context with all the access it needs. Yes, each context will have a separate connection, so yes, you will likely have multiple connections in play servicing requests. Again, this is why it makes no sense to divy out your contexts in this scenario.
If, however, you were to take a microservice architecture approach, where each microservice dealt with one discreet unit of functionality, and you wanted to be a purist about it, employing bounded contexts would make sense, but in a monolithic app, it does not.
Just do it for each context:
services.AddScoped<YourContext>(s =>
{
var builder = new
DbContextOptionsBuilder().UseSqlServer("ConnectionString");
return new YourContext(builder.Options);
});

Why can't LinqPad autogenerate a context object with EF?

Does anyone know why LinqPad cannot autogenerate an Entity Framework context object (like it does with Linq-to-SQL)? It seems I have to create an assembly containing an EF context and then reference the assembly in LinqPad. But I don't need to do this with L2S.
Thanks very much.
LINQPad uses LINQ-to-SQL for automatic data contexts because it's lighter and faster. LINQ-to-SQL also generates better SQL in many cases and allows arbitrary functions in the final projection.
It wouldn't be hard, in principle, to write a driver for Entity Framework. The reason it isn't present as an option is lack of demand.
If you wanted, you could implement EF support seamlessly as a third-party driver. The only tricky thing to implement is supporting every version of EF.

Transaction wrapped around Enterprise Data Library code + Entity Framework code

Hey I have a unique and troubling situation.
I am working on a project where my team has used a mixture of Enterprise Data Library plus Entity Framework. Obviously this probably not recommended but it is what I'm stuck with. I would like to take a method written using Enterprise Data Library and also take a different method written using Entity Framework, and wrap both of these methods in a single transaction (without requiring Microsoft Distributed Transaction Service). I'm hoping to minimize rewriting code and be able to wrap the two methods in a single transaction just as they are. Is this possible? Thanks.
Using SQL Server 2008 and .NET 4.0
I suppose you want to mix EF and EntLib queries in the same TransactionScope, without using DTC.
Good news: it's possible. If you use exactly the same connection string for EntLib DAAB and EF DbContext, it will work, depending on the versions of EF, EntLib and SQL Server.
So what you have to do is:
check you have the right versions
get the version of your connection string changed by EF and use it instead of yours.
For 1, I have checked these combinations:
SQL Server must be 2008 or older
EntLib 4.1 + EF 5 doesn't work
EntLib 5 + EF 5 works
For 2, to get the connection string changed by EF you have to debug the execution of code that uses an instance of a DbContext. Set a breakpoint and watch or inspect the DbContext Database.Connection.ConnectionString property. That's the EF version of your connection string. Use it for both EF and EntLib and you'll get rid of DTC.

How to run two Entity Framework Contexts inside TransactionScope without MSDTC?

This problem is not readily reproducible in a simple example here but was wondering if anyone has any experience and tips, here is the issue:
using Entity Framework
have many points in application where (1) data is written to some entity table e.g. Customer, (2) data is written to history table
both of these actions use Entity Framework, HOWEVER, they use different contexts
these actions need to be both in one transaction: i.e. if one fails to write, the other should not write, etc.
I can wrap them with a TransactionScope,
like this:
using (TransactionScope txScope = new TransactionScope()) {
...
}
but this gives me:
Microsoft Distributed Transaction Coordinator (MSDTC) is disabled for
network transactions.
Our database admin has told me that MSDTC is disabled by choice and can not be installed.
Hence I am making changes trying to create my own EntityConnection with a MetadataWorkspace with the idea that each context will use the same EntityConnection. However, this is proving near impossible trying to get it to work, e.g. currently I continue to get the above error even though theoretically both contexts are using EntityConnection. It's difficult to understand where/why Entity Framework is requiring the MSDTC for example.
Has anyone gone down this road before, have experience or code examples to share?
Well, the problem is quite easy.
If you are using sql server 2008 you should not have that problem because you have promotable transaction, and as .NET knows that you are using the same persistence store (the database) it wont promote it to DTC and commit it as local. look into promotable transaction with sql server 2008.
As far as I know Oracle is working in its driver to support promotable transactions, but I do not know the state, MS oracle driver does not support it.
http://www.oracle.com/technology/tech/windows/odpnet/col/odp.net_11.1.0.7.20_twp.pdf
If you are using a driver that do not support promotable transactions it is impossible for .NET to use local transaction doing two connections. You should change your architecture or convince the database admin for installing MSDTC.
I had a similar problem with SQL 2008, Entity Framework.
I had two frameworks defined (EF1, and EF2) but using identical connection strings to a sql 2008 database.
I got the MSDTC error above, when using nested "usings" across both.
eg the code was like this:
using (TransactionScope dbContext = new TransactionScope())
{
using (EF1 context = new EF1())
{
// do some EF1 db call
using (EF2 context2 = new EF2())
{
// do some EF2 db call
}
}
dbContext.Complete();
}
It wasnt as simple as this, because it was split across several methods, but this was the basic structure of "usings".
The fix was to only open one using at a time. No MTDSC error, No need to open distributed transactions on db.
using (TransactionScope dbContext = new TransactionScope())
{
using (EF1 context = new EF1())
{
// do some EF1 db call
}
using (EF2 context2 = new EF2())
{
// do some EF2 db call
}
dbContext.Complete();
}
I think that what you need to do is to force your contexts to share single database connection. You will be able then to perform these two operations against two different contexts in single transaction. You can achieve this by passing one EntityConnection object to both of your context's constructors. Of course this approach will require you to pass this object to methods which update DB.
I have recently blogged about creating database context scope which will make using multiple EF contexts and transactions easier.

Is there an in-memory provider for Entity Framework?

I am unit testing code written against the ADO .NET Entity Framework. I would like to populate an in-memory database with rows, and make sure that my code retrieves them properly.
I can mock the Entity Framework using Rhino Mocks, but that would not be sufficient. I would be telling the query what entities to return to me. This would neither test the where clause nor the .Include() statements. I want to be sure that my where clause matches only the rows I intend, and no others. I want to be sure that I have asked for the entities that I need, and none that I don't.
For example:
class CustomerService
{
ObjectQuery<Customer> _customerSource;
public CustomerService(ObjectQuery<Customer> customerSource)
{
_customerSource = customerSource;
}
public Customer GetCustomerById(int customerId)
{
var customers = from c in _customerSource.Include("Order")
where c.CustomerID == customerId
select c;
return customers.FirstOrDefault();
}
}
If I mock the ObjectQuery to return a known customer populated with orders, how do I know that CustomerService has the right where clause and Include? I would rather insert some customer rows and some order rows, then assert that the right customer was selected and the orders are populated.
An InMemory provider is included in EF7 (pre-release).
You can use either the NuGet package, or read about it in the EF repo on GitHub (view source).
The article http://www.codeproject.com/Articles/460175/Two-strategies-for-testing-Entity-Framework-Effort  describes Effort  -Entity Framework provider that runs in memory.
You can still use your DbContext or ObjectContext classes within unit tests, without having to have an actual database.
A better approach here might be to use the Repository pattern to encapsulate your EF code. When testing your services you can use mocks or fakes. When testing your repositories you will want to hit the real DB to ensure that you are getting the results you expect.
There is not currently a in memory provider for EF, but if you take a look at Highway.Data it has a base abstraction interface and an InMemoryDataContext.
Testing Data Access and EF with Highway.Data
Yes, there is at least one such provider - SQLite. I have used it a bit and it works. Also you can try SQL Server Compact. It's an embeded database and has EF providers too.
Edit:
SQLite has support for in-memory databases (link1). All you need is to specify a connection string like: "Data Source=:memory:;Version=3;New=True;". If you need in an example you may look at SharpArchitecture.
I am not familiar with Entity Framework and the ObjectQuery class but if the Include method is virtual you can mock it like this:
// Arrange
var customerSourceStub = MockRepository.GenerateStub<ObjectQuery<Customer>>();
var customers = new Customer[]
{
// Populate your customers as if they were coming from DB
};
customerSourceStub
.Stub(x => x.Include("Order"))
.Return(customers);
var sut = new CustomerService(customerSourceStub);
// Act
var actual = sut.GetCustomerById(5);
// Assert
Assert.IsNotNull(actual);
Assert.AreEqual(5, actual.Id);
You could try SQL Server Compact but it has some quite wild limitations:
SQL Server Compact does not support SKIP expressions in paging queries when it is used with the Entity Framework
SQL Server Compact does not support entities with server-generated keys or values when it is used with the Entity Framework
No outer joins, collate, modulo on floats, aggregates
In EF Core there are two main options for doing this:
SQLite in-memory mode allows you to write efficient tests against a provider that behaves like a relational database.
The InMemory provider is a lightweight provider that has minimal dependencies, but does not always behave like a relational database
I am using SQLite and it supports all queries, that I need to do with Azure SQL production database.