transaction transacciones practices net mvc isolationlevel framework efcore best begintransaction asp entity-framework-4 transactions transactionscope

entity framework 4 - transacciones - MĂșltiples llamadas a SaveChanges en el marco de la entidad



transaction efcore (5)

Es una mala práctica llamar a SaveChanges varias veces (sin un alcance de transacción) cuando las entidades relacionadas deben persistir en una sola transacción. Lo que has creado es una abstracción permeable. Cree una clase de Unidad de trabajo separada o use el ObjectContext/DbContext .

Estoy creando mi propio repositorio personalizado, basado en el marco de la entidad, y estoy creando algunos métodos de extensión que me permiten guardar modelos de vista parcial como modelos de entidad, así que estoy creando mis propios métodos de Agregar y Actualizar.

Actualmente, cada método tiene SaveChanges () de DbContext al final, lo que significa que para cada modelo, se invocará una llamada.

Estoy construyendo este patrón DAL base para sitios MVC4, lo que significa que la mayoría de las veces accederé a 1 modelo, pero no tiene por qué ser el caso.

¿Es una mala práctica llamar a SaveChanges () para cada modelo al actualizar, es decir, 3 entidades, o debo agregar todo primero al contexto del objeto y luego guardar SaveChanges () como algún tipo de compromiso de transacción?


Este es otro enfoque para manejar múltiples context.SaveChanges() Guardar cambios context.SaveChanges() usando UnitOfWork que utilizo actualmente.

Mantendremos todo el método context.SaveChanges() hasta que se llame a este último.

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; namespace DataAccess { public class UnitOfWork : IUnitOfWork { private readonly Context context; private readonly Dictionary<Type, object> repositories = new Dictionary<Type, object>(); private int beginChangeCount; private bool selfManagedTransaction = true; public UnitOfWork(Context context) { this.context = context; } //Use generic repo or init the instance of your repos here public IGenericRepository<TEntity> GetRepository<TEntity>() where TEntity : BaseEntityModel { if (repositories.Keys.Contains(typeof(TEntity))) return repositories[typeof(TEntity)] as IGenericRepository<TEntity>; var repository = new Repository<TEntity>(context); repositories.Add(typeof(TEntity), repository); return repository; } public void SaveChanges() { if (selfManagedTransaction) { CommitChanges(); } } public void BeginChanges() { selfManagedTransaction = false; Interlocked.Increment(ref beginChangeCount); } public void CommitChanges() { if (Interlocked.Decrement(ref beginChangeCount) > 0) { return; } beginChangeCount = 0; context.SaveChanges(); selfManagedTransaction = true; } } }

Muestra utilizando.

Encuentra mi comentario en el código de abajo

using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; namespace BusinessServices.Domain { public class AService : BaseBusinessService, IAService { private readonly IBService BService; private readonly ICService CService; private readonly IUnitOfWork uow; public AService (IBService BService, ICService CService, IUnitOfWork uow) { this.BService = BService; this.CService = CService; this.uow = uow; } public void DoSomeThingComplicated() { uow.BeginChanges(); //Create object B - already have uow.SaveChanges() inside //still not save to database yet BService.CreateB(); //Create object C - already have uow.SaveChanges() inside //still not save to databse yet CService.CreateC(); //if there are no exceptions, all data will be saved in database //else nothing in database uow.CommitChanges(); } } }


Recomiendo encarecidamente no llamar a SaveChanges () en cada método. Usar el patrón de repositorio y la unidad de trabajo es la mejor manera de avanzar. Unidad de trabajo, le permite ser más eficiente con sus llamadas a la base de datos y también lo ayuda a no contaminar su base de datos si algunos datos no son válidos (por ejemplo, los detalles del usuario están bien, pero la dirección falla).

Aquí hay un buen tutorial para ayudarte.

http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application


Sé que es una respuesta tardía, pero me pareció útil compartirla.

Ahora en EF6 es más fácil conseguir esto usando dbContext.Database.BeginTransaction()

Me gusta esto :

using (var context = new BloggingContext()) { using (var dbContextTransaction = context.Database.BeginTransaction()) { try { // do your changes context.SaveChanges(); // do another changes context.SaveChanges(); dbContextTransaction.Commit(); } catch (Exception ex) { //Log, handle or absorbe I don''t care ^_^ } } }

para mas informacion mira this

de nuevo está en EF6 en adelante


Un nuevo enfoque moderno como se articula aquí se aconseja en tales escenarios.

Si está familiarizado con la clase TransactionScope , entonces ya sabe cómo usar un DbContextScope . En esencia, son muy similares: la única diferencia es que DbContextScope crea y administra DbContext instancias de DbContext lugar de las transacciones de base de datos. Pero al igual que TransactionScope , DbContextScope es ambiental, se puede anidar, se puede inhabilitar su comportamiento de anidamiento y funciona bien con flujos de ejecución asíncronos.

public void MarkUserAsPremium(Guid userId) { using (var dbContextScope = _dbContextScopeFactory.Create()) { var user = _userRepository.Get(userId); user.IsPremiumUser = true; dbContextScope.SaveChanges(); } }

Dentro de un DbContextScope , puede acceder a las instancias de DbContext que el alcance administra de dos maneras. Puede obtenerlos a través de la propiedad DbContextScope.DbContexts esta manera:

public void SomeServiceMethod(Guid userId) { using (var dbContextScope = _dbContextScopeFactory.Create()) { var user = dbContextScope.DbContexts.Get<MyDbContext>.Set<User>.Find(userId); [...] dbContextScope.SaveChanges(); } }

Pero eso, por supuesto, solo está disponible en el método que creó el DbContextScope . Si necesita acceder a las instancias de DbContext ambiente en cualquier otro lugar (por ejemplo, en una clase de repositorio), simplemente puede tomar una dependencia de IAmbientDbContextLocator , que usaría así:

public class UserRepository : IUserRepository { private readonly IAmbientDbContextLocator _contextLocator; public UserRepository(IAmbientDbContextLocator contextLocator) { if (contextLocator == null) throw new ArgumentNullException("contextLocator"); _contextLocator = contextLocator; } public User Get(Guid userId) { return _contextLocator.Get<MyDbContext>.Set<User>().Find(userId); } }