gpt4 book ai didi

c# - 如何使 Entity Framework 6 (DB First) 显式插入 Guid/UniqueIdentifier 主键?

转载 作者:太空狗 更新时间:2023-10-29 17:57:51 26 4
gpt4 key购买 nike

我将 Entity Framework 6 DB First 与 SQL Server 表一起使用,每个表都有一个 uniqueidentifier 主键。这些表在主键列上有一个默认值,将其设置为 newid()。我相应地更新了我的 .edmx,将这些列的 StoreGeneratedPattern 设置为 Identity。所以我可以创建新记录,将它们添加到我的数据库上下文中,然后自动生成 ID。但现在我需要保存一个具有特定 ID 的新记录。我读过 this article这表示在使用 int 标识 PK 列时,您必须在保存之前执行 SET IDENTITY_INSERT dbo.[TableName] ON。因为我的是 Guid 而不是一个标识列,所以这基本上已经完成了。然而,即使在我的 C# 中我将 ID 设置为正确的 Guid,该值甚至没有作为参数传递给生成的 SQL 插入,并且 SQL Server 为主键生成了一个新 ID。

我需要能够做到:

  1. 插入一条新记录并让其自动创建ID,
  2. 插入一个具有指定 ID 的新记录。

我有 # 1. 如何插入具有特定主键的新记录?


编辑:
保存代码摘录(注意accountMemberSpec.ID是具体的Guid值我想做AccountMember的主键):

IDbContextScopeFactory dbContextFactory = new DbContextScopeFactory();

using (var dbContextScope = dbContextFactory.Create())
{
//Save the Account
dbAccountMember = CRMEntity<AccountMember>.GetOrCreate(accountMemberSpec.ID);

dbAccountMember.fk_AccountID = accountMemberSpec.AccountID;
dbAccountMember.fk_PersonID = accountMemberSpec.PersonID;

dbContextScope.SaveChanges();
}

--

public class CRMEntity<T> where T : CrmEntityBase, IGuid
{
public static T GetOrCreate(Guid id)
{
T entity;

CRMEntityAccess<T> entities = new CRMEntityAccess<T>();

//Get or create the address
entity = (id == Guid.Empty) ? null : entities.GetSingle(id, null);
if (entity == null)
{
entity = Activator.CreateInstance<T>();
entity.ID = id;
entity = new CRMEntityAccess<T>().AddNew(entity);
}

return entity;
}
}

--

public class CRMEntityAccess<T> where T : class, ICrmEntity, IGuid
{
public virtual T AddNew(T newEntity)
{
return DBContext.Set<T>().Add(newEntity);
}
}

这是为此记录的生成的 SQL:

DECLARE @generated_keys table([pk_AccountMemberID] uniqueidentifier)
INSERT[dbo].[AccountMembers]
([fk_PersonID], [fk_AccountID], [fk_FacilityID])
OUTPUT inserted.[pk_AccountMemberID] INTO @generated_keys
VALUES(@0, @1, @2)
SELECT t.[pk_AccountMemberID], t.[CreatedDate], t.[LastModifiedDate]
FROM @generated_keys AS g JOIN [dbo].[AccountMembers] AS t ON g.[pk_AccountMemberID] = t.[pk_AccountMemberID]
WHERE @@ROWCOUNT > 0


-- @0: '731e680c-1fd6-42d7-9fb3-ff5d36ab80d0' (Type = Guid)

-- @1: 'f6626a39-5de0-48e2-a82a-3cc31c59d4b9' (Type = Guid)

-- @2: '127527c0-42a6-40ee-aebd-88355f7ffa05' (Type = Guid)

最佳答案

一个解决方案可能是覆盖 DbContext SaveChanges。在此函数中,查找要指定其 Id 的 DbSets 的所有添加条目。

如果还没有指定Id,指定一个,如果已经指定:使用指定的。

覆盖所有 SaveChanges:

public override void SaveChanges()
{
GenerateIds();
return base.SaveChanges();
}
public override async Task<int> SaveChangesAsync()
{
GenerateIds();
return await base.SaveChangesAsync();
}
public override async Task<int> SaveChangesAsync(System.Threading CancellationToken token)
{
GenerateIds();
return await base.SaveChangesAsync(token);
}

GenerateIds 应该检查您是否已经为添加的条目提供了 Id。如果没有,请提供一个。

我不确定是否所有 DbSet 都应该具有请求的功能,或者只有一些。要检查主键是否已填充,我需要知道主键的标识符。

我在你的类(class) CRMEntity 中看到你知道每个 T 都有一个 Id,这是因为这个 Id 在 CRMEntityBase 中,或者在IGuid中,我们假设它在IGuid中。如果它在 CRMEntityBase 中,请相应地更改以下内容。

以下是小步骤;如果需要,您可以创建一个大的 LINQ。

private void GenerateIds()
{
// fetch all added entries that have IGuid
IEnumerable<IGuid> addedIGuidEntries = this.ChangeTracker.Entries()
.Where(entry => entry.State == EntityState.Added)
.OfType<IGuid>()

// if IGuid.Id is default: generate a new Id, otherwise leave it
foreach (IGuid entry in addedIGuidEntries)
{
if (entry.Id == default(Guid)
// no value provided yet: provide it now
entry.Id = GenerateGuidId() // TODO: implement function
// else: Id already provided; use this Id.
}
}

就是这样。因为您的所有 IGuid 对象现在都有一个非默认 ID(预定义或在 GenerateId 中生成)EF 将使用该 ID。

添加:HasDatabaseGeneratedOption

正如 xr280xr 在其中一条评论中指出的那样,我忘记了您必须告诉 Entity Framework Entity Framework 不应(始终)生成 Id。

例如,我对一个包含博客和帖子的简单数据库执行相同的操作。博客和帖子之间的一对多关系。为了表明思路不依赖GUID,主键是long。

// If an entity class is derived from ISelfGeneratedId,
// entity framework should not generate Ids
interface ISelfGeneratedId
{
public long Id {get; set;}
}
class Blog : ISelfGeneratedId
{
public long Id {get; set;} // Primary key

// a Blog has zero or more Posts:
public virtual ICollection><Post> Posts {get; set;}

public string Author {get; set;}
...
}
class Post : ISelfGeneratedId
{
public long Id {get; set;} // Primary Key
// every Post belongs to one Blog:
public long BlogId {get; set;}
public virtual Blog Blog {get; set;}

public string Title {get; set;}
...
}

现在是有趣的部分:通知 Entity Framework 主键值已经生成的流畅 API。

我更喜欢 Fluent API 避免使用属性,因为 Fluent API 的使用允许我在不同的数据库模型中重用实体类,只需重写 Dbcontext.OnModelCreating。

例如,在某些数据库中,我喜欢我的 DateTime 对象是 DateTime2,而在某些数据库中,我需要它们是简单的 DateTime。有时我想要自己生成的 ID,有时(比如在单元测试中)我不需要。

class MyDbContext : Dbcontext
{
public DbSet<Blog> Blogs {get; set;}
public DbSet<Post> Posts {get; set;}

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Entity framework should not generate Id for Blogs:
modelBuilder.Entity<Blog>()
.Property(blog => blog.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
// Entity framework should not generate Id for Posts:
modelBuilder.Entity<Blog>()
.Property(blog => blog.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

... // other fluent API
}

SaveChanges 与我上面写的类似。 GenerateIds 略有不同。在这个例子中,我没有遇到有时 Id 已经填满的问题。每个添加的实现 ISelfGeneratedId 的元素都应该生成一个 Id

private void GenerateIds()
{
// fetch all added entries that implement ISelfGeneratedId
var addedIdEntries = this.ChangeTracker.Entries()
.Where(entry => entry.State == EntityState.Added)
.OfType<ISelfGeneratedId>()

foreach (ISelfGeneratedId entry in addedIdEntries)
{
entry.Id = this.GenerateId() ;// TODO: implement function
// now you see why I need the interface:
// I need to know the primary key
}
}

对于那些正在寻找简洁的 Id 生成器的人:我经常使用与 Twitter 使用的相同的生成器,一个可以处理多个服务器的生成器,没有每个人都可以从主键猜测添加了多少项的问题。

它在 Nuget IdGen package

关于c# - 如何使 Entity Framework 6 (DB First) 显式插入 Guid/UniqueIdentifier 主键?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46857417/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com