如何使用UnitOfWork模式从Add获取id?

时间:2017-01-25 22:24:16

标签: c# asp.net asp.net-mvc entity-framework unit-of-work

我使用UnitOfWork模式来抽象我的Asp.Net应用程序中的数据库访问。基本上我遵循这里描述的UnitOfWork模式方法:

https://chsakell.com/2015/02/15/asp-net-mvc-solution-architecture-best-practices/

但是,我很难理解,我将如何获得新添加项目的ID。就像我想在我的客户存储库中添加新客户一样,我将如何获得客户ID?问题是Add和Commit是分离的,只有在Commit之后才知道Id。

有没有办法使用UnitOfWork模式获取添加项目的ID?

7 个答案:

答案 0 :(得分:1)

我已经解决了。我认为效果很好:

当您需要一个存储库(例如Cars)并插入一个新的DomainCar时,您想要获取仅在应用SaveChanges()时生成的ID。请注意,我不希望我的EF模型类传播到我的域层:

public DomainCar
{
    public int Id{get;set;}
    public string Name{get;set;}
}

首先,您创建一个通用的IEntity接口和一个实现该接口的类:

public interface IEntity<T>
{
    T Id { get; }
}

public class Entity<T> : IEntity<T>
{
    dynamic Item { get; }
    string PropertyName { get; }
    public Entity(dynamic element,string propertyName)
    {
        Item = element;
        PropertyName = propertyName;
    }
    public T Id
    {
        get
        {
            return (T)Item.GetType().GetProperty(PropertyName).GetValue(Item, null);
        }
    }
}

然后在存储库的add方法中,返回ID类型的IEntity:

public IEntity<int> AddCar(DomainCar car)
{
    var carDb=Mapper.Map<Car>(car);//automapper from DomainCar to Car (EF model class)
    var insertedItem=context.CARS.Add(carDb);
    return new Entity<int>(insertedItem,nameof(carDb.Id));
}

然后,在某个地方,您要在UnitofWork中调用add方法和随后的Save():

using (var unit = UnitOfWorkFactory.Create())
{
   IEntity<int> item =unit.CarsRepository.AddCar(new CarDomain(){Name="Ferrari"});
   unit.Save();
   int newId= item.Id; //you can extract the recently generated Id
}

答案 1 :(得分:1)

我的方法如下。只需继续使用添加的实体作为对象即可。因此,不返回其ID,而是返回添加的对象本身。然后,在某个时候(通常在控制器中),您将调用UoW.Commit();,结果,添加的实体的Id属性将包含更新的值。

此时,您可以开始使用Id属性,例如按照您所说的将其存储在cookie中。

答案 2 :(得分:1)

这里的问题是ID由数据库生成,因此我们需要调用SaveChanges,以便数据库生成ID,而EntityFramework将使用生成的ID修复实体。

那如果我们可以避免数据库往返怎么办?

执行此操作的一种方法是使用uuid而不是整数作为id。这样,您可以简单地在域模型的构造函数中生成一个新的uuid,并且可以(漂亮地)安全地假定它在整个数据库中都是唯一的。

当然,在id的uuid和整数之间进行选择是对它自己的完整讨论:Advantages and disadvantages of GUID / UUID database keys但是至少这是支持uuid的观点。

答案 3 :(得分:0)

除非你打破模式并传递一些关于新创建的实体的额外信息,否则我认为没有办法做到这一点。

由于ID只会在提交成功后分配,如果您没有关于创建/更新/删除哪些实体的信息,则几乎不可能知道。

我曾经使用下面的代码做过(我不推荐它,但我特意使用它)

public string Insert(Person entity)
{
    uow.Repository.Add(entity); //public Repository object in unit of work which might be wrong
    Response responseObject = uow.Save();
    string id = entity.Id;  //gives the newly generated Id
    return id;
}

答案 4 :(得分:0)

工作单位应该是整个请求的交易。

只需从新创建的对象中返回人员ID。

根据您用于数据访问的技术,这将有所不同,但如果您使用的是Entity Framework,则可以执行以下操作:

enablePersistentStorage

通过这种方式创建Person实例,您将获得一个允许延迟加载和使用Id属性的跟踪实例,因为它将在调用SaveChanges时更新(通过结束您的工作单元)。

答案 5 :(得分:0)

我在数据库级别使用SEQUENCE代替IDENTITY。创建新实体时,首先,我要获取序列的下一个值并将其用作ID。

数据库:

CREATE SEQUENCE dbo.TestSequence
START WITH 1
INCREMENT BY 1

CREATE TABLE [dbo].[Test](
    [Id]    [int] NOT NULL DEFAULT (NEXT VALUE FOR dbo.TestSequence),
    [Name]  [nvarchar](200) NOT NULL,
    CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED ([Id] ASC)
)

C#:

public enum SequenceName
{
    TestSequence
}

public interface IUnitOfWork : IDisposable
{
    DbSet<TEntity> Set<TEntity>() where TEntity : class;
    void Commit(SqlContextInfo contextInfo);
    int NextSequenceValue(SequenceName sequenceName);
}

public class UnitOfWork : MyDbContext, IUnitOfWork
{
...
    public void Commit(SqlContextInfo contextInfo)
    {
        using (var scope = Database.BeginTransaction())
        {
            SaveChanges();
            scope.Commit();
        }
    }

    public int NextSequenceValue(SequenceName sequenceName)
    {
        var result = new SqlParameter("@result", System.Data.SqlDbType.Int)
        {
            Direction = System.Data.ParameterDirection.Output
        };
        Database.ExecuteSqlCommand($"SELECT @result = (NEXT VALUE FOR [{sequenceName.ToString()}]);", result);
        return (int)result.Value;
    }
...
}

internal class TestRepository
{
    protected readonly IUnitOfWork UnitOfWork;
    private readonly DbSet<Test> _tests;

    public TestRepository(IUnitOfWork unitOfWork)
    {
        UnitOfWork = unitOfWork;
        _tests = UnitOfWork.Set<Test>();
    }

    public int CreateTestEntity(NewTest test)
    {
        var newTest = new Test
        {
            Id = UnitOfWork.NextSequenceValue(SequenceName.TestSequence),
            Name = test.Name
        };
        _tests.Add(newTest);
        return newTest.Id;
    }
}

答案 6 :(得分:0)

我的解决方案通过存储库方法返回Lazy<MyModel>

    public class MyRepository
    {
       // ----
       public Lazy<MyModel> Insert(MyModel model)
       {
          MyEntity entity = _mapper.MapModel(model);
          _dbContext.Insert(entity);
         return Lazy<MyModel>(()=>_mapper.MapEntity(entity));
       }
 

   }

在域中:

Lazy<MyModel> mayModel = unitOfWork.MyRepository.Insert(model);
unitOfWork.Save();
MyModel myModel = myModel.Value;