我在我的应用程序中将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
是否有一个标志可以使其在与原始查询相同的事务中运行?
答案 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