Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/330.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用EF Core 2.2使用SQL Server DECRYPTBYKEY解密字符串_C#_Sql Server_Entity Framework Core_Asp.net Core 2.2 - Fatal编程技术网

C# 使用EF Core 2.2使用SQL Server DECRYPTBYKEY解密字符串

C# 使用EF Core 2.2使用SQL Server DECRYPTBYKEY解密字符串,c#,sql-server,entity-framework-core,asp.net-core-2.2,C#,Sql Server,Entity Framework Core,Asp.net Core 2.2,基本上我有一个POCO模型,它有一个加密的字符串。使用EF core 2.2 我们使用DECRYPTBYKEY来使用对称密钥解密字符串 我使用DBSet.FromSQL传入SQL查询,该查询调用开放对称密钥,获取包括解密值、闭合对称密钥在内的数据 FromSQL只允许您带回实体,而不是字符串本身 我尝试在模型上添加一个解密的字符串值,然后尝试在FromSQL查询中设置该值 当存储库数据库集没有任何.Include时,这实际上会填充ok 当DBSet确实有.Include(用于过滤外键表上的DBS

基本上我有一个POCO模型,它有一个加密的字符串。使用EF core 2.2

我们使用DECRYPTBYKEY来使用对称密钥解密字符串

我使用DBSet.FromSQL传入SQL查询,该查询调用开放对称密钥,获取包括解密值、闭合对称密钥在内的数据

FromSQL只允许您带回实体,而不是字符串本身

我尝试在模型上添加一个解密的字符串值,然后尝试在FromSQL查询中设置该值

当存储库数据库集没有任何.Include时,这实际上会填充ok

当DBSet确实有.Include(用于过滤外键表上的DBSet)时,会出现一个运行时错误,该错误抱怨解密的字符串不是数据库表上的列,当然不是。所以having.Include首先调用基表上的SQL

如果我将[NotMapped]属性放在解密的字符串列上,那么当FromSQL查询运行时,它不会填充该列

那么,我如何在不使用[NotMapped]的情况下使用这个解密的字符串列,而在DBSet上使用.Include呢

我已经添加了代码,以便您可以更多地了解问题。没有办法像一个答案中所建议的那样在模型上添加Decrypt的实现。Decrypt方法要求数据库集调用FromSQL。DbSet源于ConcreteRepository。我也看不到调用一个临时SQL查询来返回1个字符串的方法

从原始SQL(SQL Server)剪切

混凝土存储库

public async Task<IEnumerable<Model>> GetAllById(int id)
{

            var filteredSet = Set.Where(x => x.Id == id)
               .Include(x => x.Table2)
               .Where(x => x.Table2.IsSomething).ToList();

            var models = filteredSet.Select(f =>
                GetDecryptValue($"Id = {f.Id}");

            return models;

}


protected DbSet<TEntity> Set => _dbContext.Set<TEntity>();

public virtual TEntity GetDecryptValue(string filterCriteria)
        {
            string buildSelectStmt = $"SELECT TOP 1 Encrypted FROM Model";
            string buildSelectStmt2 = $"SELECT *, CONVERT(VARCHAR(MAX), DECRYPTBYKEY(@Value)) AS Decrypted FROM Model";

            buildSelectStmt = $"{buildSelectStmt} WHERE {filterCriteria}";
            buildSelectStmt2 = $"{buildSelectStmt2} WHERE {filterCriteria}";

            string sql = string.Format(@"
                DECLARE @Value NVARCHAR(MAX)
                SET @Value = ({0});
                OPEN SYMMETRIC KEY {1} DECRYPTION BY PASSWORD = '{2}';
                {3};
                CLOSE SYMMETRIC KEY {1};",
                buildSelectStmt, SymmetricKeyName, SymmetricKeyPassword, buildSelectStmt2);

            var result = Set.FromSql(sql);

            return result.FirstOrDefault();
        }

因此,正如我在评论中所暗示的,侵入EFCore的管道并使其执行自定义SQL函数确实是可能的。这是一个可以做到这一点的方法

我将提前声明,我在一个数据库(请参阅我的repo链接中的SQL脚本)上试验了
解密功能,因为我没有密钥。我也只安装了.net core 2.1。尽管如此,我还是希望你能明白要点。话虽如此,我将强调几点,让您进一步探讨解决方案:

我最终定义了我的模型,如下所示:

public partial class Model
{
    public int Id { get; set; }
    public byte[] Encrypted { get; set; } // apparently encrypted data is stored in `VARBINARY`, which translates to `byte[]`, so I had to tweak it here
    [NotMapped] // this is still required as EF will not know where to get the data unless we tell it (see down below)
    public string Decrypted { get; set; } // the whole goal of this exercise here
    public Table2 Table2 { get; set; }
}
考虑到我应该能够只选择值而不必进行第二次往返,我稍微修改了您的
具体存储库
代码:

public IEnumerable<Model> GetAllById(int id)
{
    // you will need to uncomment the following line to work with your key
    //_dbContext.Database.ExecuteSqlCommand("OPEN SYMMETRIC KEY {1} DECRYPTION BY PASSWORD = '{2}';", SymmetricKeyName, SymmetricKeyPassword);
    var filteredSet = Set.Include(x => x.Table2)
        .Where(x => x.Id == id)
        .Where(x => x.Table2.IsSomething)
        .Select(m => new Model
    {
        Id = m.Id,
        //Decrypted = EF.Functions.DecryptByKey(m.Encrypted), // since the key's opened for session scope - just relying on it should do the trick
        Decrypted = EF.Functions.Decrypt("test", m.Encrypted),
        Table2 = m.Table2,
        Encrypted = m.Encrypted
    }).ToList();
    // you will need to uncomment the following line to work with your key
    //_dbContext.Database.ExecuteSqlCommand("CLOSE SYMMETRIC KEY {1};", SymmetricKeyName);
    return filteredSet;
}
我最终实现了三个表达式存根:
DecryptByKey
decryptbaysphase
encrypbaysphase
,例如:

public class DecryptByKeyExpression : Expression
{
    private readonly Expression _value;

    public override ExpressionType NodeType => ExpressionType.Extension;
    public override Type Type => typeof(string);
    public override bool CanReduce => false;

    protected override Expression VisitChildren(ExpressionVisitor visitor)
    {
        var visitedValue = visitor.Visit(_value);

        if (ReferenceEquals(_value, visitedValue))
        {
            return this;
        }

        return new DecryptByKeyExpression(visitedValue);
    }

    protected override Expression Accept(ExpressionVisitor visitor)
    {
        if (!(visitor is IQuerySqlGenerator))
        {
            return base.Accept(visitor);
        }
        visitor.Visit(new SqlFragmentExpression("CONVERT(VARCHAR(MAX), DECRYPTBYKEY("));
        visitor.Visit(_value);
        visitor.Visit(new SqlFragmentExpression("))"));
        return this;
    }

    public DecryptByKeyExpression(Expression value)
    {
        _value = value;
    }
}
毕竟,这是一个非常简单的字符串构建练习。希望这能为您提供足够的燃料,让您的解决方案启动并运行

UPDEF Core 3似乎仍然支持,因此上述解决方案仍应适用。
UPD2:确实可以做到。请参见my.

,解密/加密方法将稍后在数据库中,并且依赖于ef dbset.fromsql来执行。我看不出如何在模型中实现。要遵循的代码..我现在添加了代码,以便您可以更多地了解问题。无法在模型上添加解密的实现。解密需要DbSet调用FomSQL。DbSet只来自ConcreteRepository。我也看不到调用特殊SQL查询来返回1个字符串的方法。是的,我现在看到了这个问题。这篇博文(不是我的)让我相信,确实可以定义一个解密/加密函数调用,EF将透明地转换为SQL。我建议你看看这里的EF核心源代码:我知道这可能不是你想要的答案,但如果我要探索tWow。这里有很多细节。谢谢你。星期一我会试试的谢谢你的帮助。您知道EFCore 3解决方案吗?看起来很容易出错。
public IEnumerable<Model> GetAllById(int id)
{
    // you will need to uncomment the following line to work with your key
    //_dbContext.Database.ExecuteSqlCommand("OPEN SYMMETRIC KEY {1} DECRYPTION BY PASSWORD = '{2}';", SymmetricKeyName, SymmetricKeyPassword);
    var filteredSet = Set.Include(x => x.Table2)
        .Where(x => x.Id == id)
        .Where(x => x.Table2.IsSomething)
        .Select(m => new Model
    {
        Id = m.Id,
        //Decrypted = EF.Functions.DecryptByKey(m.Encrypted), // since the key's opened for session scope - just relying on it should do the trick
        Decrypted = EF.Functions.Decrypt("test", m.Encrypted),
        Table2 = m.Table2,
        Encrypted = m.Encrypted
    }).ToList();
    // you will need to uncomment the following line to work with your key
    //_dbContext.Database.ExecuteSqlCommand("CLOSE SYMMETRIC KEY {1};", SymmetricKeyName);
    return filteredSet;
}
public class TranslateImpl : IMethodCallTranslator
{

    private static readonly MethodInfo _encryptMethod
        = typeof(DbFunctionsExtensions).GetMethod(
            nameof(DbFunctionsExtensions.Encrypt),
            new[] { typeof(DbFunctions), typeof(string), typeof(string) });
    private static readonly MethodInfo _decryptMethod
        = typeof(DbFunctionsExtensions).GetMethod(
            nameof(DbFunctionsExtensions.Decrypt),
            new[] { typeof(DbFunctions), typeof(string), typeof(byte[]) });

    private static readonly MethodInfo _decryptByKeyMethod
        = typeof(DbFunctionsExtensions).GetMethod(
            nameof(DbFunctionsExtensions.DecryptByKey),
            new[] { typeof(DbFunctions), typeof(byte[]) });

    public Expression Translate(MethodCallExpression methodCallExpression)
    {
        if (methodCallExpression.Method == _encryptMethod)
        {
            var password = methodCallExpression.Arguments[1];
            var value = methodCallExpression.Arguments[2];
            return new EncryptExpression(password, value);
        }
        if (methodCallExpression.Method == _decryptMethod)
        {
            var password = methodCallExpression.Arguments[1];
            var value = methodCallExpression.Arguments[2];
            return new DecryptExpression(password, value);
        }

        if (methodCallExpression.Method == _decryptByKeyMethod)
        {
            var value = methodCallExpression.Arguments[1];
            return new DecryptByKeyExpression(value);
        }

        return null;
    }
}
public class DecryptByKeyExpression : Expression
{
    private readonly Expression _value;

    public override ExpressionType NodeType => ExpressionType.Extension;
    public override Type Type => typeof(string);
    public override bool CanReduce => false;

    protected override Expression VisitChildren(ExpressionVisitor visitor)
    {
        var visitedValue = visitor.Visit(_value);

        if (ReferenceEquals(_value, visitedValue))
        {
            return this;
        }

        return new DecryptByKeyExpression(visitedValue);
    }

    protected override Expression Accept(ExpressionVisitor visitor)
    {
        if (!(visitor is IQuerySqlGenerator))
        {
            return base.Accept(visitor);
        }
        visitor.Visit(new SqlFragmentExpression("CONVERT(VARCHAR(MAX), DECRYPTBYKEY("));
        visitor.Visit(_value);
        visitor.Visit(new SqlFragmentExpression("))"));
        return this;
    }

    public DecryptByKeyExpression(Expression value)
    {
        _value = value;
    }
}