Database Transaction Unit

They tried to get around this for a couple of days, and there are a lot of tutorials around the unit of work and around TransactionScope, but I can not find anything that says about them together. Any help is much appreciated!

I am using Entity Framework with Unit Of Work template and repository for each type. According to the simple code below, I have a Member member and MembershipDefinition. I want to create a Membership object that links two, but when I create a Membership object. I want to query DB for maximum value based on some business logic . Therefore, I need to use some kind of database transaction to prevent another thread from adding a value to db before my thread writes the Membership object back to the database.

If I used stored procs, that would be pretty simple, but I can't figure out how to do this using pure C # ...

The code below creates 100 database memberships with duplicate MembershipNumbers. I need to use this use to use transactions to ensure that all membership numbers generated in C # code are unique.

class Program { static void Main(string[] args) { var p = new Program(); p.Go();; } public void Go() { long memberId; long membershipDefId; using(var unitOfWork = new UnitOfWork()) { // Setup - create test club and member entities var testUsername = ("TestUserName" + Guid.NewGuid()).Substring(0, 29); var member = new Member() { UserName = testUsername }; var testmemebrshpDefName = ("TestMembershipDef" + Guid.NewGuid()).Substring(0, 29); var membershipDefinition = new ClubMembershipDefinition() { ClubId = 1, Name = testmemebrshpDefName }; unitOfWork.MemberRepository.Add(member); unitOfWork.MembershipDefinitionRepository.Add(membershipDefinition); unitOfWork.Save(); memberId = member.Id; membershipDefId = membershipDefinition.Id; } Task[] tasks = new Task[100]; // Now try to add a membership to the Member object, linking it to the test Club single Club Definition for (int i = 0; i < 100; i++) { var task = new Task(() => CreateMembership(memberId, membershipDefId)); tasks[i] = task; task.Start(); } Task.WaitAll(tasks); } private void CreateMembership(long memberId, long membershipDefId) { using (var unitOfWork = new UnitOfWork()) { var member = unitOfWork.MemberRepository.GetById(memberId); var membershipDef = unitOfWork.MembershipDefinitionRepository.GetById(membershipDefId); var membership = new ClubMembership() { ClubMembershipDefinition = membershipDef }; membership.MembershipNumber = (unitOfWork.MembershipRepository.GetMaxMembershipNumberForClub(membershipDef.ClubId) ?? 0) + 1; member.ClubMemberships.Add(membership); unitOfWork.Save(); } } } public class UnitOfWork : IUnitOfWork, IDisposable { internal ClubSpotEntities _dbContext = new ClubSpotEntities(); internal MemberRepository _memberRepository; internal MembershipRepository _membershipRepository; internal MembershipDefinitionRepository _membershiDefinitionpRepository; public MemberRepository MemberRepository { get { if (_memberRepository == null) _memberRepository = new MemberRepository(_dbContext); return _memberRepository; ; } } public MembershipRepository MembershipRepository { get { if (_membershipRepository == null) _membershipRepository = new MembershipRepository(_dbContext); return _membershipRepository; ; } } public MembershipDefinitionRepository MembershipDefinitionRepository { get { if (_membershiDefinitionpRepository == null) _membershiDefinitionpRepository = new MembershipDefinitionRepository(_dbContext); return _membershiDefinitionpRepository; ; } } public virtual int Save() { return _dbContext.SaveChanges(); } private bool _disposed = false; protected virtual void Dispose(bool disposing) { if (!this._disposed) { if (disposing) { _dbContext.Dispose(); } } this._disposed = true; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } } public class MembershipRepository { ClubSpotEntities _dbContext = new ClubSpotEntities(); public MembershipRepository(){} public MembershipRepository(ClubSpotEntities dbContext) { _dbContext = dbContext; } public IEnumerable<ClubMembership> GetAll() { return _dbContext.Set<ClubMembership>().ToList<ClubMembership>(); } public ClubMembership GetById(long id) { return _dbContext.ClubMemberships.First(x => x.Id == id); } public long? GetMaxMembershipNumberForClub(long clubId) { return _dbContext.ClubMemberships.Where(x => x.ClubMembershipDefinition.ClubId == clubId).Max(x => x.MembershipNumber); } public ClubMembership Add(ClubMembership entity) { return _dbContext.Set<ClubMembership>().Add(entity); } public void Delete(ClubMembership membership) { _dbContext.Set<ClubMembership>().Remove(membership); } public void Save() { _dbContext.SaveChanges(); } } public partial class ClubMembership { public long Id { get; set; } public long MembershipDefId { get; set; } public Nullable<long> MemberId { get; set; } public Nullable<long> MembershipNumber { get; set; } public virtual ClubMembershipDefinition ClubMembershipDefinition { get; set; } public virtual Member Member { get; set; } } public partial class ClubMembershipDefinition { public ClubMembershipDefinition() { this.ClubMemberships = new HashSet<ClubMembership>(); } public long Id { get; set; } public long ClubId { get; set; } public string Name { get; set; } public virtual ICollection<ClubMembership> ClubMemberships { get; set; } } public partial class Member { public Member() { this.ClubMemberships = new HashSet<ClubMembership>(); } public long Id { get; set; } public string UserName { get; set; } public virtual ICollection<ClubMembership> ClubMemberships { get; set; } } 
+7
source share
1 answer

You can create a transaction scope when creating a new UnitOfWork module and execute it when completed. This is not a full version:

 class UnitOfWork { ClubSpotEntities _dbContext; TransactionScope _transaction; public UnitOfWork() { _dbContext = new ClubSpotEntities(); _transaction = new TransactionScope(); } public void Complete() { _dbContext.SaveChanges(); _transaction.Complete(); } ... } 

UPD: Stephen said that this is not a solution to the problem. And UnitOfWork cannot help you, TransactionScope will not be the solution in this case. EF does not support the pessimistic locks you want to use, but you can try this.

+2
source

All Articles