在实体框架模型中保护字段的正确方法是什么?

问题描述 投票:0回答:2

我有一个模型类,我希望 Id 是只读的,我希望字符串属性有一个支持字段(应用 Trim 来设置值)。这样做的正确方法是什么?

public class DisallowedEmail
{
    private string _email;

    public int Id { get; }
    public required string Email
    {
        get => _email;
        set => _email = value.Trim();
    }

    private DisallowedEmail(int id, string email)
    {
        Id = id;
        _email = email;
    }

    public DisallowedEmail()
    {
        _email = string.Empty;
    }
}

或...

public class DisallowedEmail
{
    private string _email;

    public int Id { get; private set; }
    public required string Email
    {
        get => _email;
        set => _email = value.Trim();
    }
}
entity-framework entity-framework-core
2个回答
0
投票

我怀疑第一个不会工作,因为 Id 需要一个 setter 来设置构造函数。然而,这就是说我决定测试它并且在它编译时,当我尝试下面的示例时它实际上不起作用,它确实与私有 setter 一起工作。 (它抱怨没有找到合适的构造函数,即使构造函数没有改变)

EF Core 支持使用构造函数参数,并将发现/使用私有构造函数,因此您可以施加更多控制以确保实体在有效状态下构造。

例如,EF 在构造实体时需要一个构造函数或公共/受保护的属性设置器来填充数据库中的状态。您可能需要一个公共或内部(如果使用工厂模式)构造函数来初始化新实例的代码,以确保有效和完整的状态。

假设您有一个客户实体,客户有一组必填字段,包括账单地址(实体)以及一些可选字段:

public class Customer
{
    [Key]
    public int CustomerId { get; private set; } = 0;

    public string Name { get; private set; }

    public string? PreferredName { get; set; } = null;

    public virtual Address BillingAddress { get; protected set; }

    // Constructor EF will use.
    protected Customer(int customerId, string name)
    {
        CustomerId = customerId;
        Name = name;
    }

    // Constructor consumers or factories will use. (could be internal for factory use)
    public Customer(string name, Address billingAddress)
    {
        Name = name;
        BillingAddress = billingAddress;
    }
}

EF 构造函数可以是

private
,但前提是您不启用代理。否则它需要
protected
老实说它受到足够的 IMO 保护和更安全的选择。

这里的区别在于,虽然 EF 可以使用构造函数来初始化实体,但它不能使用引用导航属性的构造函数。对于我们新客户的公共设置,我们可能希望确保代码提供帐单地址,因为这是必填字段。我们还可以保护在验证它的方法后面更改帐单地址,或执行一些其他操作,如更新更改历史记录等,而不是简单的 setter。我们不希望消费者看到 EF 所做的版本,因为他们没有新客户的 ID,我们也不应该鼓励“hacky”行为,例如填充和附加假定代表实时数据的客户。


0
投票

对于您的具体用例,最简单的方法是有一个用于创建实体的构造函数,在此期间您修剪电子邮件地址,以及 EntityFramework 在从数据库读取实体时将用于创建实体的第二个构造函数。

public class DisallowedEmail
{
    // Properties
    public int Id { get; private set; }
    public string Address { get; private set; }
        
    // Constructors
    public DisallowedEmail(string address)
    {
        if (string.IsNullOrEmpty(address))
        {
            throw new ArgumentNullException(nameof(address));
        }
        Address = address.Trim();
    }

#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
    /// <summary>Constructor for EntityFrameworkCore.</summary>
    /// <remarks>EntityFrameworkCore chooses the constructor with the least amount of parameters (including 0) where all of them correspond to a property.</remarks>
    private DisallowedEmail() { }
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
}

此外,如果您想要一些适当的 DotNet 电子邮件地址验证和清理,您可以使用我的扩展方法

string.ToEmailAddress()
通过复制它或使用包含它的my Nuget 包(它是纯 C# 而不是有任何依赖关系):

using AndrejKrizan.DotNet.Extensions;

namespace Domain.Entities
{
    public class DisallowedEmail
    {
        // Properties
        public int Id { get; private set; }
        public string Address { get; private set; }
        
        // Constructors
        public DisallowedEmail(string address)
        {
            Address = address.ToEmailAddress();
        }

#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
        /// <summary>Constructor for EntityFrameworkCore.</summary>
        /// <remarks>EntityFrameworkCore chooses the constructor with the least amount of parameters (including 0) where all of them correspond to a property.</remarks>
        private DisallowedEmail() { }
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.