I have a number of columns in a database that are encrypted. I can pull the record from the database using EF Core 6, I can pass the record to a decryption routine, but I need to update the record with the unencrypted values.
Here is the decrypt routine:
public Subject DecryptSubjects(Subject decryptSubject, List<string> EncryptedColumns)
{
if (decryptSubject == null)
{
return null;
}
var decryptedProperty = decryptSubject.GetType().GetProperty("Decrypted");
if (decryptedProperty != null)
{
bool? value = (bool?)decryptedProperty.GetValue(decryptSubject, null);
if (value != null)
{
if (value == true)
{
return decryptSubject;
}
}
}
foreach (string name in EncryptedColumns)
{
var property = decryptSubject.GetType().GetProperty(name);
if (property != null)
{
var value = property.GetValue(decryptSubject, null);
if (value != null)
{
property.SetValue(decryptSubject, Decrypt(value.ToString()), null);
}
}
}
if (decryptedProperty != null)
{
decryptedProperty.SetValue(decryptSubject, true, null);
}
return decryptSubject;
}
This is returning the decrypted values just fine.
Here is the call to this routine:
var subjects = context.Subjects.ToList();
foreach (var subject in subjects)
{
// decrypt each subject and then update them in the database
var decryptedSubject = encrypt.DecryptSubjects(subject, subjectCols);
}
Once I get the decryptedSubject
back, I want to update the record in the database. Is there a better way than setting each property on the subject object to the decrypted values in decryptedSubject
likes this:
...
subject.FirstName = decrytpedSubject.FirstName;
subject.LastName = decrytedSubject.LastName;
...
CodePudding user response:
A quick and easy way is with Automapper setting up a mapper configuration for CreateMap<Subject, Subject>()
along with any exclusions for fields you definitely do not want to allow to be overwritten. From there you can use the little-documented .Map(decryptedSubject, subject)
method to copy values across.
Update
can work as well, though you need to assert that the DbContext isn't already tracking an instance for that same record. This means checking context.Subjects.Local
for any existing tracked references and either detaching them, or using the copy across method if one is already tracked. Otherwise you will get an exception on the Update
call. Detaching an existing tracked reference could have negative flow-on effects if that reference was being tracked as part of a larger operation where that reference was associated to another entity that might be updated during this operation.
Update
will also overwrite everything. (including FKs and other stuff you might not expect to allow changing) It will also generate an UPDATE
SQL statement for all columns whether any value changed at all or not, where using an Automapper Map
or manual copy over approach would only generate and execute an UPDATE
statement for values that actually changed, if any value actually changed. This also doesn't add the validation to ensure the record you intend to update actually exists in the database, where loading the existing entity and copying values across with Map()
or manually does pre-assert that there is actually an entity to be updated rather than failing on SaveChanges()
whenever that might occur if this is part of a larger operation.