你好我有SQL服务器设置始终加密的功能,我也设置EF用于始终加密的列,但是当我尝试添加/更新时,对于Db操作我使用DbContext,在我的Db中输入我得到关注错误:
Operand type clash: decimal(1,0) encrypted with (encryption_type = 'DETERMINISTIC', encryption_algorithm_name = 'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name = '****', column_encryption_key_database_name = '****') is incompatible with decimal(6,2) encrypted with (encryption_type = 'DETERMINISTIC', encryption_algorithm_name = 'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name = '*****', column_encryption_key_database_name = '****')
我使用的模型
public class Model
{
/// <summary>
/// Payment method name
/// </summary>
[Column(TypeName = "nvarchar(MAX)")]
public string Name { get; set; }
/// <summary>
/// Payment method description
/// </summary>
[Column(TypeName = "nvarchar(MAX)")]
public string Description { get; set; }
/// <summary>
/// Fee charges for using payment method
/// </summary>
[Column(TypeName = "decimal(6,2)")]
public decimal Fee { get; set; }
}
我还尝试在OnModelCreating方法中指定十进制格式
builder.Entity<Model>().Property(x => x.Fee).HasColumnType("decimal(6,2)");
我错过了什么? 感谢您的任何建议
答案 0 :(得分:0)
我和我的同事已经使用DiagnosticSource找到了解决该问题的方法。
您必须知道:
想法是填充命令对象(由EFCore创建)的'Precision'和'Scale'字段,这样对Sql的调用将包含正确执行查询所需的所有信息。
首先,创建侦听器:
namespace YOUR_NAMESPACE_HERE
{
public class EfGlobalListener : IObserver<DiagnosticListener>
{
private readonly CommandInterceptor _interceptor = new CommandInterceptor();
public void OnCompleted()
{
}
public void OnError(Exception error)
{
}
public void OnNext(DiagnosticListener value)
{
if (value.Name == DbLoggerCategory.Name)
{
value.Subscribe(_interceptor);
}
}
}
}
CommandInterceptor在哪里:
namespace YOUR_NAMESPACE_HERE
{
public class CommandInterceptor : IObserver<KeyValuePair<string, object>>
{
// This snippet of code is only as example, you could maybe use Reflection to retrieve Field mapping instead of using Dictionary
private Dictionary<string, (byte Precision, byte Scale)> _tableMapping = new Dictionary<string, (byte Precision, byte Scale)>
{
{ "Table1.DecimalField1", (18, 2) },
{ "Table2.DecimalField1", (12, 6) },
{ "Table2.DecimalField2", (10, 4) },
};
public void OnCompleted()
{
}
public void OnError(Exception error)
{
}
public void OnNext(KeyValuePair<string, object> value)
{
if (value.Key == RelationalEventId.CommandExecuting.Name)
{
// After that EF Core generates the command to send to the DB, this method will be called
// Cast command object
var command = ((CommandEventData)value.Value).Command;
// command.CommandText -> contains SQL command string
// command.Parameters -> contains all params used in sql command
// ONLY FOR EXAMPLE PURPOSES
// This code may contain errors.
// It was written only as an example.
string table = null;
string[] columns = null;
string[] parameters = null;
var regex = new Regex(@"^INSERT INTO \[(.+)\] \((.*)\)|^VALUES \((.*)\)|UPDATE \[(.*)\] SET (.*)$", RegexOptions.Multiline);
var matches = regex.Matches(command.CommandText);
foreach (Match match in matches)
{
if(match.Groups[1].Success)
{
// INSERT - TABLE NAME
table = match.Groups[1].Value;
}
if (match.Groups[2].Success)
{
// INSERT - COLS NAMES
columns = match.Groups[2].Value.Split(",", StringSplitOptions.RemoveEmptyEntries).Select(c => c.Replace("[", string.Empty).Replace("]", string.Empty).Trim()).ToArray();
}
if (match.Groups[3].Success)
{
// INSERT - PARAMS VALUES
parameters = match.Groups[3].Value.Split(",", StringSplitOptions.RemoveEmptyEntries).Select(c => c.Trim()).ToArray();
}
if (match.Groups[4].Success)
{
// UPDATE - TABLE NAME
table = match.Groups[4].Value;
}
if (match.Groups[5].Success)
{
// UPDATE - COLS/PARAMS NAMES/VALUES
var colParams = match.Groups[5].Value.Split(",", StringSplitOptions.RemoveEmptyEntries).Select(p => p.Replace("[", string.Empty).Replace("]", string.Empty).Trim()).ToArray();
columns = colParams.Select(cp => cp.Split('=', StringSplitOptions.RemoveEmptyEntries)[0].Trim()).ToArray();
parameters = colParams.Select(cp => cp.Split('=', StringSplitOptions.RemoveEmptyEntries)[1].Trim()).ToArray();
}
}
// After taking all the necessary information from the sql command
// we can add Precision and Scale to all decimal parameters
foreach (var item in command.Parameters.OfType<SqlParameter>().Where(p => p.DbType == DbType.Decimal))
{
var index = Array.IndexOf<string>(parameters, item.ParameterName);
var columnName = columns.ElementAt(index);
var key = $"{table}.{columnName}";
// Add Precision and Scale, that fix our problems w/ always encrypted columns
item.Precision = _tableMapping[key].Precision;
item.Scale = _tableMapping[key].Scale;
}
}
}
}
}
最后在Startup.cs中添加以下代码行以注册侦听器:
DiagnosticListener.AllListeners.Subscribe(new EfGlobalListener());