当字符串超过数据库列长度时防止保存实体框架实体

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

我有一本大字典(有超过 1k 个条目),定义如下:

 Dictionary<string, string> dic = new Dictionary<string, string>
 {
   ...
   { PlanVariablesDefinitions.Growth_Proposed_Savings_Rate, L.GrowthProposedSavingsRate.ToString("0.00") },
   { PlanVariablesDefinitions.Growth_Proposed_Income_Growth_Rate, L.GrowthProposedIncomeGrowthRate.ToString() },
   { PlanVariablesDefinitions.Growth_Proposed_Portfolio_Growth_Rate, L.GrowthProposedPortfolioGrowthRate.ToString() },
   { PlanVariablesDefinitions.Growth_Proposed_FMVRealEstate_Growth_Rate, L.GrowthProposedFMVRealEstateGrowthRate.ToString() },
    ...
}     

常量定义如下:

 ...
 public const string Growth_Proposed_Savings_Rate = "Growth_Proposed_Savings_Rate";
 public const string Growth_Proposed_Income_Growth_Rate = "Growth_Proposed_Income_Growth_Rate";
 public const string Growth_Proposed_Portfolio_Growth_Rate = "Growth_Proposed_Portfolio_Growth_Rate";
 public const string Growth_Proposed_FMVRealEstate_Growth_Rate = "Growth_Proposed_FMVRealEstate_Growth_Rate";
 ...

此字典保存在 SQL 数据库中的某些列中,其中每个列的长度都已设置

nvarchar(50)
。我遇到过这样的情况:由于重构(为了给出有意义的描述),常量的值被更改为超过 50 个字符,例如 "Growth_Propose_FMVRealEstate_Growth_Rate""Growth_Propose_FMVRealEstate_Growth_Rate_from_2023_2024_First_Quater"。执行此更改时,开发人员不知道此修改会在将此字符串插入数据库时的某个时刻使系统崩溃。

现在我放置此代码只是为了快速崩溃并在第一次运行时检测到此类情况:

 foreach (var item in dic)
 if (item.Key.Length > 50)
     throw new Exception($"{item.Key} is over 50 chars and will throw an exception on DB save.");

我的问题是......我可以在编译时使用某些属性来防止这种情况吗?或任何其他有效的解决方案来避免这种解决方法?

c# entity-framework-core
1个回答
0
投票

在编译时,除了运行外部验证插件/工具之外,实际上没有任何选择。请记住,外部验证适用于生成的常量等内容,但不适用于变量字符串的运行时分配。在运行时,您可以将固定长度检查集成到断言其长度的自定义类型中:

public abstract class StringN(string value, uint length)
{
    public string Value { get; } = value.LengthChecked(length);

    public override string ToString()
    {
       return Value;
    }

    public static implicit operator string(StringN value)
    {
        return value.Value;
    }
}

public sealed class String50(string value) : StringN(value, Length)
{
    public const uint Length = 50;
    public static implicit operator String50(string value)
    {
        return new String50(value);
    }
}

public static class FixedLengthStringExtensions
{
    public static string LengthChecked(this string value, uint length)
    {
        if (!string.IsNullOrEmpty(value) && value.Length > length)
            throw new ArgumentException($"Length of fixed string exceeded. '{value}' {value.Length}/{length}");

        return value;
    }
}

当使用字典或任何其他使用 String{N} 时,这将在运行时验证并抛出。在你的情况下,唯一的变化是声明:

Dictionary<String50, string> dic = new Dictionary<String50, string>
{
   ...
   { PlanVariablesDefinitions.Growth_Proposed_Savings_Rate, L.GrowthProposedSavingsRate.ToString("0.00") },
   { PlanVariablesDefinitions.Growth_Proposed_Income_Growth_Rate, L.GrowthProposedIncomeGrowthRate.ToString() },
   { PlanVariablesDefinitions.Growth_Proposed_Portfolio_Growth_Rate, L.GrowthProposedPortfolioGrowthRate.ToString() },
   { PlanVariablesDefinitions.Growth_Proposed_FMVRealEstate_Growth_Rate, L.GrowthProposedFMVRealEstateGrowthRate.ToString() },
    ...
}

非常紧凑。我选择长度验证作为扩展方法,我认为它与主构造函数初始化很好地结合在一起。

无需编写预验证循环,并且可以捕获运行时重新分配固定长度值时的任何情况。您确实需要为要使用的每个/任何固定长度字符串长度声明一个类,但这些非常简单,只需名称更新和常量长度。往返

string
的分配已实施并准备就绪。

来自这个答案的灵感:固定长度的字符串对象C#

© www.soinside.com 2019 - 2024. All rights reserved.