我使用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?
答案 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;