【问题标题】:Unable to save internal properties of a class into DB using EFCore无法使用 EFCore 将类的内部属性保存到数据库中
【发布时间】:2021-09-12 06:43:32
【问题描述】:

我经历了以下 - stackoverflow question,它对我不起作用,我没有使用导航属性。

Entity,位于 - Component.Shared.csproj(所有内容都是公开的)

public class Entity
{
    [Key]
    public Guid Id { get; set; }
    public Guid CreatedBy { get; set; }
    public Guid UpdatedBy { get; set; }
    public DateTime CreatedOn { get; set; }
    public DateTime UpdatedOn { get; set; }
}

User,位于-Component.User.csproj,继承Entity类。

internal class User : Entity
{
    internal string FirstName { get; set; }
    internal string MiddleName { get; set; }
    internal string LastName { get; set; }
    internal string EmailId { get; set; }
    internal DateTime DateOfBirth { get; set; }
}

UserDbContext 位于 - Component.User.csproj

internal sealed class UserDbContext : DbContext
{
    public UserDbContext(DbContextOptions<UserDbContext> options)
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        //Remove pluralizing table name convention (Install package - Microsoft.EntityFrameworkCore.Relational)
        foreach (var entity in builder.Model.GetEntityTypes())
        {
            entity.SetTableName(entity.DisplayName());
        }

        base.OnModelCreating(builder);
    }

    internal DbSet<User> Users { get; set; }
}

UserRepository.cs (Component.User.csproj)。

internal class UserRepository : IUserRepository
{
    #region Private Fields
    // To detect redundant calls
    private bool _disposed = false;
    private readonly IUnitOfWorkWithDbContext<UserDbContext, User> _uow;        
    #endregion Private Fields

    public UserRepository(IUnitOfWorkWithDbContext<UserDbContext, User> uow)
    {
        _uow = uow;
    }

    bool IRepository<User>.Create(User user)
    {
        var userEntry = _uow.DbContext.Users.Add(user);
        return userEntry.IsAdded(); // IsAdded is an extension method - return EntityState.Added == entityEntry.State;
    }
}

问题 - 如何强制 EF(目前,但将来可能是 Dapper/Raw ADO.NET 等)保存 User 或任何内部属性的值类,而不公开暴露它们,以避免对 Components.User.csproj 之外的代码进行任何直接更改?

【问题讨论】:

  • 来自the docs “按照惯例,所有带有getter和setter的公共属性都将包含在模型中。”,您可能需要手动添加所有内部属性
  • (1) 从 OOP 的角度来看,internalpublic 类的 internal 成员之间没有区别。因此,只需将它们设为public。 (2) EF 实体代表所谓的数据模型,因此应该使用最少的 OOP 糖 - 基本上是类继承和虚拟导航属性,仅此而已 - 没有接口,没有方法,没有业务逻辑,没有私有/受保护/内部/未映射的成员,简单的公共获取/设置属性。
  • 1/2 @IvanStoev 我这样做是为了在编译器本身强制执行的Component.User.csproj 成员周围创建一个边界。这样他们就不会被我的开发人员(非)有意访问,他们正在努力让我们说Components.Post.csproj 成员。如果他们需要来自Componet.User DB 的东西,那么他们会访问我提供的API,而不是直接访问IUserRepository。我观看了西蒙的一个会议视频-Modular Monoliths,我正在努力实现它。 GitHub
  • 2/2 @IvanStoev GitHub 的代码非常基础,处于早期阶段,请随意浏览。如果您知道一个好的帖子/博客可以帮助我实现这一目标,请分享。
  • 我很抱歉,但我不同意这些概念(已经分享了我的观点)并且不会浪费我的时间来支持它们。但即使没有这一切,您似乎也不理解 (1) - internal 类的 publicinternal 属性之间存在 no 区别.其他项目无法访问该类,因此类属性的类型无关紧要。在同一个项目中,所有代码都可以访问类+它的内部成员,所以这些成员在那里就像 public。只有当类是公共的时,内部成员才有意义。

标签: c# .net-core entity-framework-core


【解决方案1】:

您可以通过这种方式设置保存实体的内部属性:

public class ApplicationDbContext : DbContext
{
    private readonly IHttpContextAccessor _contextAccessor;

    public AuditTrail(IHttpContextAccessor contextAccessor)
    {
        _contextAccessor = contextAccessor;
    }

    public void SaveChanges()
    {
        SetAuditInfo();
        base.SaveChanges();
    }

    public Task SaveChangesAsync()
    {
        SetAuditInfo();
        return base.SaveChangesAsync();
    }

    private void SetAuditInfo()
    {
        var entries = context.ChangeTracker
            .Entries<Entity>()
            .Where(e => e.State == EntityState.Added || e.State == EntityState.Modified)
            .ToList();

        var claim = _contextAccessor?.HttpContext?.User.Claims.FirstOrDefault(c => c.Type == "UserId")?.Value;
        var userId = claim != null ? Guid.Parse(claim) : Guid.Empty;

        foreach (var entry in entries)
        {
            switch (entry.State)
            {
                case EntityState.Added:
                    entry.Entity.CreatedOn = DateTime.Now;
                    entry.Entity.CreatedBy = userId;
                    break;

                case EntityState.Modified:
                    entry.Entity.UpdatedOn = DateTime.Now;
                    entry.Entity.CreatedBy = userId;
                    break;
            }
        }
    }
}

为了防止更改,您可以创建另一个对象,例如 UserDto,其内部属性只有 getter 或从 DTO 对象中删除它们。

另一个选项是在数据库中设置日期:

entity.Property(e => e.CreatedOn)
    .HasDefaultValueSql("(getdate())")
    .Metadata.AfterSaveBehavior = PropertySaveBehavior.Throw;

你也可以使用Shadow properties:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<User>()
        .Property<DateTime>("CreatedOn");
}

并设置值:

context.Entry(user).Property("CreatedOn").CurrentValue = DateTime.Now;

【讨论】:

    猜你喜欢
    • 2017-06-09
    • 2012-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多