【问题标题】:实体添加/插入列等于身份主键值
【发布时间】:2022-01-21 04:02:21
【问题描述】:

在插入/添加过程中,我可以在一个写入/保存过程中创建一个与新值主键相等的不同列吗?这是一个身份自动生成的值?我知道我可以事后抓住并进行更改,但这是我试图避免的额外数据库命中。

public class myDataTableRec
{
    public int Id { get; set; }    //This is an Identity Primary Key
    public string Name { get; set; }
    public int PostId { get; set; }  //Want this the same as Id when it gets generated
}


myrec = new myDataTableRec;
db.myDataTable.Add(myrec);
db.SaveChanges();

【问题讨论】:

  • 主要问题是:为什么您希望将相同的值存储在对象的两个单独变量中吗?似乎并没有多大意义......并且“正确”执行此操作的唯一方法是在INSERT之后在您的数据库表上放置一个触发器,这也会将PostId 设置为新插入的标识值。
  • 试图让这个表存储主记录和详细信息,因为它们具有相同的列。将所有主数据和详细数据与主身份记录以相同的 id 分组。也许这不是好的架构。所以 postid 只会在主记录上是相同的,如果我留在一个 db 中,那么一个读取会抓住它,而不是两个 dbs。
  • 将规则设为“如果父项为空,则为主记录”是否更容易?
  • 这不会让我一拉就抓住所有主/细节记录,这是目标。我将重新考虑我的策略。

标签: c# sql entity-framework


【解决方案1】:

你必须将StoreGeneratedPattern的属性设置为identity,然后你就可以实现这个

 myrec = new myDataTableRec;
 db.myDataTable.Add(myrec);
 db.SaveChanges();
 var id = myrec.Id;

【讨论】:

  • 我试图避免读回 id,然后设置我的 PostId = id,并再次保存。这需要在数据库上保存两次。只想要一个。有可能吗?
  • 您似乎想要一个自己生成的自定义ID,如果是这种情况,请从数据库中的列中删除自动生成的ID并让您自己控制它。
  • 试图让这个表存储主记录和详细信息,因为它们具有相同的列。将所有主数据和详细数据与主身份记录以相同的 id 分组。也许这不是好的架构。不确定。
【解决方案2】:

来自https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.dbcontext.add?view=efcore-5.0

"添加(TEntity)

开始跟踪给定实体,以及任何其他尚未被跟踪的可到达实体,处于已添加状态,以便在调用 SaveChanges() 时将它们插入数据库。

使用 State 仅设置单个实体的状态。”

由于它跟踪实体,您可以执行以下操作:

        myrec=new myDataTableRec;
        db.myDataTable.Add(myrec);
        myrec.PostId = myrec.Id
        db.SaveChanges();

编辑:如果它在保存后创建 Id,您始终可以创建自己的 Id (Guid.NewGuid()),不要让 EF 生成它。

【讨论】:

  • 你显然没有测试这个。
【解决方案3】:

在 SQL 中,不可能在一个原子操作中插入实体并将其在外键列中生成的主对象设置为自身。仅当未生成主键(即不是标识列)时才有可能。

然而,正如 EF 能够在一个操作中插入相关实体并在运行中在外键中设置生成的键值一样,有人可能会争辩说 EF 可以支持将父级设置为自身。

采用这个实体类(替换您的占位符名称并使用导航属性增强它):

class Post
{
    public int ID { get; private set; }
    public string Name { get; set; }
    public int? ParentPostID { get; private set; }

    public Post ParentPost { get; set; }
    public ICollection<Post> ChildPosts { get; private set; }
}

EF 可以选择支持这种情况:

using var db = new MyContext();
var root = new Post { Name = "Root" };
root.ParentPost = root;
db.Set<Post>().Add(root);

但事实并非如此。它尝试插入具有等于临时(负)ID 值的ParentPostID 的实体。显然,这是违反 FK 的。

要清楚地做到这一点,您必须添加一个事务并单独设置和保存自我引用:

using var db = new MyContext();
var root = new Post { Name = "Root" };
db.Set<Post>().Add(root);

using var ts = new TransactionScope();
db.SaveChanges();
root.ParentPost = root;
db.SaveChanges();
ts.Complete();

请注意,父 id 必须可以为空。另请注意,导航属性允许在不知道其键值的情况下设置父级。有些人喜欢以 DDD 风格做这些事情。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-09-13
    • 1970-01-01
    • 2018-05-15
    • 1970-01-01
    • 2018-09-25
    • 2018-04-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多