In the following example I have a controller that generates a key serial Id and encrypt some sensitive information like for example SSN. I have my functions in static classes to allow me to call them from anywhere in my project. both classes are running in the same context and run SP. I think the problem lies in there.
startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MvcOptions>(options =>
{
options.Filters.Add(new RequireHttpsAttribute());
});
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("ARTNetCore")));
services.AddIdentity<ApplicationUser, IdentityRole>(config =>
{
config.SignIn.RequireConfirmedEmail = true;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
// Add application services.
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
services.Configure<SMSoptions>(Configuration);
services.AddMvc();
// Configure Identity
services.Configure<IdentityOptions>(options =>
{
// Password settings
options.Password.RequireDigit = true;
options.Password.RequiredLength = 8;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = true;
options.Password.RequireLowercase = false;
// Lockout settings
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
options.Lockout.MaxFailedAccessAttempts = 10;
options.Lockout.AllowedForNewUsers = true;
services.AddCookieAuthentication(o =>
{
o.LoginPath = "/Account/LogIn";
o.LogoutPath = "/Account/LogOut";
});
// User settings
options.User.RequireUniqueEmail = true;
});
services.Configure<AuthMessageSenderOptions>(Configuration);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
var options = new RewriteOptions()
.AddRedirectToHttps();
app.UseRewriter(options);
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
//Comment when migrating or demigrating
//SeedData.Initialize(app.ApplicationServices);
}
Controller is
private readonly ApplicationDbContext _context;
public PatRegController(ApplicationDbContext context)
{
_context = context;
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("FName,MName,LName,Dob,GenrId,StasId,NatyId,MarsId,CouyId,StaeId,CityId,OccnId,Email,SNN,PassNo,MobNo,LLine,MAdds,StrtNo,SDirn,AptNo,Locy,ALevl,PCode,Couy,ProeId")] PatReg patReg)
{
if (ModelState.IsValid)
{
patReg.FileId = DbSerializerHandler.SerializeFileId(_context);
patReg.SNN = DbEncryptionHandler.DynamicEncrypt(patReg.SNN, _context);
_context.Add(patReg);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(patReg);
}
Encryption static class is
public static class DbEncryptionHandler
{
public static string DynamicEncrypt(string clearText, ApplicationDbContext _context)
{
try
{
if (!string.IsNullOrWhiteSpace(clearText))
{
List<EncKeys> Keys = new List<EncKeys>();
Keys = _context.EncKeys.FromSql("EncKeysSP").ToList();
string EncryptionKey = Keys[0].DynamicKey;
byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
using (Aes encryptor = Aes.Create())
{
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(clearBytes, 0, clearBytes.Length);
cs.Close();
}
clearText = Convert.ToBase64String(ms.ToArray());
}
}
}
else
{
clearText = null;
}
}
catch (Exception ex)
{
throw;
}
return clearText;
}
}
and Serializer static class is
public static class DbSerializerHandler
{
public static Int64 SerializeFileId(ApplicationDbContext _context)
{
List<FileIdSeq> NewFileSeq = new List<FileIdSeq>();
NewFileSeq = _context.FileIdSeq.FromSql("FileIdSeqSP").ToList();
var FileID = NewFileSeq[0].LastSequence;
return FileID;
}
}
When I try to save my data I get this error
An unhandled exception occurred while processing the request.
SqlException: New transaction is not allowed because there are other threads running in the session. System.Data.SqlClient.SqlConnection.OnError(SqlException exception, bool breakConnection, Action wrapCloseInAction)
The error returns on this controller line await _context.SaveChangesAsync();
What am I doing wrong here?
update I checked, I can run only one of these functions at a time but not both at the same time.
The
DbEncryptionHandler
andDbSerializerHandler
classes do not need to have a dependency onApplicationDbContext
, just the data they need from it.In your controller you can get the data from your context before you use your static classes.