包括相关表中的字段

时间:2018-10-27 14:41:57

标签: ef-core-2.1 audit.net

我在我的应用程序中将EF与DotNet Core 2.1一起使用。该应用程序处理多个相关的数据以及FK互连表。

我需要审核日志记录数据仅更改为一张表。但是,我的问题是,我需要审核日志的表具有相当多的FK,对于这些表,我想记录FK本身以及相关表中的字段。

让我尝试说明一下我的意思-假设这是我的模型:

public class Blog {
  public int Id { get; set; }
  public string Name { get; set; }
  public string Url { get; set; }

  [InverseProperty ("Blog")]
  public ICollection<Post> Posts { get; set; }

  public Blog() {
    Posts = new Collection<Post> ();
  }
}
...
[AuditInclude]
public class Post
{
  public int Id { get; set; }
  public string Title { get; set; }
  public string Content { get; set; }

  [Required]
  public int BlogId { get; set; }
  public Blog Blog { get; set; }
}

如前所述,我想只记录对一个实体的更改的审核日志记录-假设它是Post-这是一个审核类:

public class Audit_Post : IAudit {
  public int Id { get; set; }

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

  public int BlogId { get; set; }
  public string Blog { get; set; }    // <- I need populating this from Blog.Name

  [StringLength (64)]
  public string AuditUsername { get; set; }
  public DateTime AuditDt { get; set; }
  public string AuditAction { get; set; }

  public Audit_Manufacturer () { }
}

这就是我在startup.cs -> ConfigureService()中设置审核日志记录的方式:

...
Audit.Core.Configuration.Setup ()
  .UseEntityFramework (ef => ef
    .AuditTypeExplicitMapper (m => m
      .Map<Post, Audit_Post> ((d, al) => {
        al.Blog = d.Blog?.Name;    // !! This doesn't work
      })
      .AuditEntityAction<IAudit> ((evt, entry, auditEntity) => {
        Object val;
        var gotVal = evt.CustomFields.TryGetValue ("AuditUsername", out val);
        string username = null;
        if (gotVal && val is string)
          username = val as string;
        else
          username = "<anonymous>";
        auditEntity.AuditDt = DateTime.UtcNow;
        auditEntity.AuditUsername = username;
        auditEntity.AuditAction = entry.Action;
      })
    )
  );

问题:是否可以从依赖表(一对多)关系中获取和审核日志数据?

除了上述问题外,我还遇到了一个离题的问题,即-如果我忘记了使用迁移来更新数据库以初始化Audit_Posts表,而我正在对{{ 1}}表中,即使审计日志无法写入(UnitOfWork保存异常),数据也将存储到后面。 Posts是否有一个标志可以使其在与原始查询相同的事务中运行?

2 个答案:

答案 0 :(得分:1)

正如@ thepirat000所指出的,足以保证DbContext内存中存在所有相关项目。这意味着:

  • 插入。在执行context.Posts.Add(item)之前,要查询所有相关项,例如context.Blogs.Find(item.BlogId)
  • 更新:检索Post时,请与.Include(d => d.Blog)和其他相关项目一起使用。
  • 删除:检索Post时,请与.Include(d => d.Blog)和其他相关项目一起使用。

另一个引起我麻烦的重要事情是我的Audit表的布局。问题是我在审计表中重复使用了相同的属性名称,但类型不同-在原始表中,属性Blog是一个关系属性,而在审计表中,它是一个字符串。这会导致从一种模型转换为另一种模型时出现错误。

[AuditInclude]
public class Post
{
  ...
  [Required]
  public int BlogId { get; set; }
  public Blog Blog { get; set; }
  ...
}

只需将其重命名为其他名称即可

public class Audit_Post
{
  ...
  public int BlogId { get; set; }
  public string BlogName { get; set; }
  ...
}
...
// and in startup.cs use ...
...
.Map<Post, Audit_Post> ((d, al) => {
    al.BlogName = d.Blog?.Name;
  })
...

关于第二个问题-在事务内部运行审核。我决定暂时不使用它。我将通过测试来介绍所描述的情况。

也许是该软件包未来开发的一个建议-提及的案例很容易涵盖-我的意思是,传递属性。

答案 1 :(得分:0)

如果您在检索data Robot = Robot { name :: String, attack :: Int, hp :: Int } -- no need to define getters because we get them for free and no setters because we don’t use them robot (name,attack,hp) = Robot name attack hp instance (Show Robot) where show (Robot n a h) = n ++ " attack:" ++ (show a) ++ " hp:"++ (show h) -- fight r1 r2 returns r2 after being attacked by r1 fights robot1 robot2 = robot2 { hp = hp robot2 - attack robot1 } 实体时拥有Include属性Blog,则您的代码应该可以正常工作,例如:

Post

如果由于任何原因而不能包含Blog实体,则可以在映射上进行查询,但是您将需要在using (var context = new BlogsContext()) { var post = context.Posts .Include(p => p.Blog) // Important, otherwise post.Blog will be NULL .First(); post.Content += " adding this"; context.SaveChanges(); } 方法上使用较低级别的重载,如下所示:

Map

关于另一个问题,当前没有内置的机制可以在审计保存失败时回滚数据库更改,但是也许您尝试使用.Map<Post, PostAudit>((ev, entry, postAudit) => { var entryEf = entry.GetEntry(); var post = entryEf.Entity as Post; var dbContext = entryEf.Context as BlogsContext; // Get the blog related var blog = dbContext.Blogs.FirstOrDefault(b => b.Id == post.BlogId); postAudit.Blog = blog?.Name; }) overrides