限制域类中的字符串长度

发布于 2024-08-19 08:46:08 字数 2471 浏览 2 评论 0原文

我有一个持久性无知的域模型,它使用抽象存储库来加载域对象。 我的存储库(数据访问层 (DAL))的具体实现使用实体框架从 SQL Server 数据库获取数据。 数据库的许多 varchar 列都有长度限制。 现在假设我有以下域类:

public class Case
{
    public Case(int id, string text)
    {
         this.Id = id;
         this.Text = text;
    }

    public int Id { get; private set; }
    public string Text { get; set; }
}

以及定义如下的抽象存储库:

public abstract class CaseRepository
{
    public abstract void CreateCase(Case item);
    public abstract Case GetCaseById(int id);
}

sqlserver 中表的 [text] 列定义为 nvarchar(100)

现在我知道我提到我的域类(Case)对持久性一无所知,但我觉得它允许 对于 text 参数的值,该值最终无法由我的具体存储库实现保存,因为实体框架 当将 text 属性分配给实体框架生成的类时,当它超过 100 个字符时,将引发异常。 因此,我决定希望在域模型中检查此约束,因为这允许我在尝试之前检查数据有效性 将其传递给 DAL,从而使错误报告更加以域对象为中心。我想你可能会说我可以检查一下 我的构造函数和属性设置器中的约束,但由于我有数百个类,它们都具有类似的约束,所以我想要一个 解决问题的更通用方法

现在,我想出了一个名为 ConstrainedString 的类,定义如下:

public abstract class ConstrainedString
{
    private string textValue;

    public ConstrainedString(uint maxLength, string textValue)
    {
        if (textValue == null) throw new ArgumentNullException("textValue");
        if (textValue.Length > maxLength) 
            throw new ArgumentException("textValue may not be longer than maxLength", "textValue");

        this.textValue = textValue;
        this.MaxLength = maxLength;
    }

    public uint MaxLength { get; private set; }

    public string Value 
    { 
        get 
        {
            return this.textValue;
        } 

        set 
        {
            if (value == null)
                throw new ArgumentNullException("value");
            if (value.Length > this.MaxLength) throw new ArgumentException("value cannot be longer than MaxLength", "value");
            this.textValue = value;
        } 
    }
}

此外,我有一个名为 ConstrainedString 的实现String100

public class String100 : ConstrainedString
{
    public String100(string textValue) : base(100, textValue) { }
}

从而导致 Case 的不同实现,如下所示:

public class Case
{
    public Case(int id, String100 text)
    {
         this.Id = id;
         this.Text = text;
    }

    public int Id { get; private set; }
    public String100 Text { get; set; }
}

现在,我的问题是;我是否忽略了一些内置类或我可以使用的其他方法?或者这是一个合理的方法吗?

任何意见和建议都非常欢迎。

先感谢您

I have a persistence ignorant domain model that uses abstract repositories to load domain objects.
The concrete implementation of my repositories (the data access layer (DAL)) uses entity framework to fetch data from a sql server database.
The database has length constraints on a lot of its varchar columns.
Now imagine that I have the following domain class:

public class Case
{
    public Case(int id, string text)
    {
         this.Id = id;
         this.Text = text;
    }

    public int Id { get; private set; }
    public string Text { get; set; }
}

And an abstract repository defined as follows:

public abstract class CaseRepository
{
    public abstract void CreateCase(Case item);
    public abstract Case GetCaseById(int id);
}

The [text] column of the table in sqlserver is defined as nvarchar(100)

Now I know that I mentioned that my domain class (Case) was persistence ignorant, nevertheless I feel that it is wrong that it allows
for values of the text parameter that cannot ultimately be saved by my concrete repository implementation because the entity framework
will throw an exception when assigning the text property to the entity framework generated class when it is longer than 100 characters.
So I have decided that I wish to check this constraint in the domain model, because this allows me to check data validity before attempting to
pass it on to the DAL, and thus making error reporting more centric to the domain object. I guess you could argue that I could just check the
constraint in my constructor and in the property setter, but since I have hundreds of classes that all have similar constraints I wanted a
more generic way to solve the problem

Now, the thing that I've come up with is a class called ConstrainedString, defined as follows:

public abstract class ConstrainedString
{
    private string textValue;

    public ConstrainedString(uint maxLength, string textValue)
    {
        if (textValue == null) throw new ArgumentNullException("textValue");
        if (textValue.Length > maxLength) 
            throw new ArgumentException("textValue may not be longer than maxLength", "textValue");

        this.textValue = textValue;
        this.MaxLength = maxLength;
    }

    public uint MaxLength { get; private set; }

    public string Value 
    { 
        get 
        {
            return this.textValue;
        } 

        set 
        {
            if (value == null)
                throw new ArgumentNullException("value");
            if (value.Length > this.MaxLength) throw new ArgumentException("value cannot be longer than MaxLength", "value");
            this.textValue = value;
        } 
    }
}

Furthermore I have an implementation of ConstrainedString called String100 :

public class String100 : ConstrainedString
{
    public String100(string textValue) : base(100, textValue) { }
}

Thus leading to a different implementation of Case that would look like this:

public class Case
{
    public Case(int id, String100 text)
    {
         this.Id = id;
         this.Text = text;
    }

    public int Id { get; private set; }
    public String100 Text { get; set; }
}

Now, my question is; Am I overlooking some built-in classes or some other approach that I could use instead? Or is this a reasonable approach?

Any comments and suggestions are most welcome.

Thank you in advance

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

风柔一江水 2024-08-26 08:46:08

我相信您的验证应该驻留在您的域模型中。字段上的约束直接代表一些业务逻辑。最终你必须在坚持之前进行验证。

I believe your validation should reside in your domain model. The constraints on your fields directly represent some business logic. Ultimately you have to validate before you persist anyway.

挽清梦 2024-08-26 08:46:08

我认为这取决于很多因素(以及一些个人喜好)。有时,约束应该构成域对象的一部分 - 例如,社会安全号码/护照号码... - 这些通常具有固定长度,并且不能作为域规则而变化 - 不是数据持久性规则(尽管您可能会约束数据库)以及)。

有些人不喜欢在其域模型中进行此类检查,而是在属性上设置类似验证属性的内容,可以通过单独的验证器从域对象外部检查和执行该属性。

您的方法可能遇到的问题(尽管并不难解决)是让任何 ORM/Mapper(如果您使用的是 ORM/Mapper)了解如何将字符串映射到数据库或从数据库映射到 ConstrainedString。

ConstrainedString 可能无法解决域对象具有有关约束的额外信息的问题,因为它可能需要构造 ConstrainedString

I think this depends on many factors (as well as some personal preferences). Sometimes the constraint should form part of the domain object - for example with social security numbers/passport numbers... - these normally have a fixed length and cannot vary as a domain rule - not a data persistence rule (although you might constrain the db as well).

Some prefer to not have these sort of checks in their domain model and instead have something like a validation attribute on the property that can be inspected and executed external from the domain object by a seperate validator.

The issue you might have with your method (although not difficult to get around) is getting any ORM/Mapper - if you're using one - to know how to map a string to/from the db to your ConstrainedString.

The ConstrainedString might not get around the issue of the domain object having extra info about the constraint as it might need to construct the ConstrainedString

花落人断肠 2024-08-26 08:46:08

如果您更改案例的约束,那么您必须制定一个新案例是有道理的 - 您已经更改了合同,并且旧代码将不再知道它是否满足要求。

不用担心您的存储库将允许或不允许什么,而是定义将在您的类中允许什么,并确保您找到一种方法来使用您将来更改为的任何存储库。您拥有您的 API,但您的依赖项则不然。

If you change the constraints of a Case, it makes sense that you'd have to make a new one - you've changed the contract, and old code will no longer know if it's meeting the requirements or not.

Instead of worrying about what your repository will or will not allow, define what you will allow in your class, and make sure that you find a way to work with any repository that you change to in the future. You own your API - your dependencies do not.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文