c# - 具有数据库事务的工作单元模式

标签 c# entity-framework transactions transactionscope

这几天我一直在努力解决这个问题,有很多关于工作单元和 TransactionScope 的教程,但我找不到任何一起讨论这两者的内容。非常感谢任何帮助!

我正在使用具有工作单元模式的 Entity Framework 和每个类型的存储库。根据下面的简单代码,我有一个 Member 和 MembershipDefinition 实体。我想创建一个链接两者的成员资格实体,但是当我创建成员资格对象时,我想根据某些业务逻辑查询数据库中的最大值。因此,我需要使用某种数据库事务来防止另一个线程在我的线程将成员资格对象写回数据库之前递增数据库中的值。

如果我使用的是存储过程,这将非常简单,但我不知道如何使用纯 C# 来实现...

下面的代码在数据库中创建了 100 个具有重复 MembershipNumbers 的 Membership 实体。我需要使用事务来确保在 c# 代码中生成的所有成员(member)编号都是唯一的。

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's 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; }
}

最佳答案

您可以在实例化新的 UnitOfWork 时创建事务范围,并在完成时提交。这不是完整的例子:

class UnitOfWork
{
     ClubSpotEntities _dbContext;
     TransactionScope _transaction;

     public UnitOfWork()
     {
         _dbContext = new ClubSpotEntities();
         _transaction = new TransactionScope();
     }

     public void Complete()
     {
         _dbContext.SaveChanges();
         _transaction.Complete();
     }

     ...
}

更新: 正如史蒂文所说,这不是您问题的解决方案。 UnitOfWork 帮不了你,TransactionScope 在这种情况下也不是解决方案。 EF 不支持您想使用的悲观锁,但您可以试试这个 solution .

关于c# - 具有数据库事务的工作单元模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11982828/

相关文章:

c# - .NET 线程池 - 无响应的 WinForms UI

java - StepExecutionListener 中的 Spring Batch 事务

postgresql - 为什么这个 PostgreSQL 事务给出 "WARNING: there is no transaction in progress"

c# - 在 C# 6 字符串插值中对 "{"进行转义

C# 通用字典 TryGetValue 找不到键

c# - 在 EF Core 中指定 Azure SQL Server 版本而不破坏本地开发

entity-framework - 如何在 Entity Framework 中将字段设置为DBNull

silverlight - EF5 Code First 和 RIA 服务 Silverlight "Object reference not set to an instance of an object"错误构建客户端

php - 最佳实践数据库事务和在 PHP 中将文件存储到文件系统

c# - 当类型仅在执行时已知时调用泛型函数