Transaction Per Request Middleware Not working - mysql

I am trying to add a middleware so that transaction begins at the beginning of the request and commits or rollbacks depending on the situation. This is my middleware:
public class TransactionPerRequestMiddleware
{
private readonly RequestDelegate next_;
public TransactionPerRequestMiddleware(RequestDelegate next)
{
next_ = next;
}
public async Task Invoke(HttpContext context, AppDbContext dbContext)
{
var is_everything_ok = true;
var transaction = dbContext.Database.BeginTransaction(
System.Data.IsolationLevel.ReadCommitted);
if (context.Response.StatusCode != 200 && is_everything_ok)
{
is_everything_ok = false;
}
await next_.Invoke(context);
if ((context.Response.StatusCode == 200 ||
context.Response.StatusCode == 302) && is_everything_ok)
{
transaction.Commit();
}
else
{
transaction.Rollback();
}
}
}
I registered the above middleware in Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMiddleware<TransactionPerRequestMiddleware>();
}
I have a method in service which performs two database operations one after another.
public void method()
{
databaseOperation1();
databaseOperation2();
}
If exception occurs in databaseOperation2() , databaseOperation1() is not rolling back currently. I am trying to implement Unit Of Work for each web request.
Thanks in advance.

Related

UWP app crashes while performing an async method

I have created two methods in my BaseViewModel (MVVMLight App) to perform any async code with some logic:
public async Task PerformOperation(Func<Task> action)
{
IsBusy = true;
try
{
await action?.Invoke();
}
catch(Exception ex)
{
// logging is here
}
finally
{
IsBusy = false;
}
}
public async Task PerformOperation(params Operation[] actions)
{
IsBusy = true;
MultipleOperatrions = true;
OperationStatuses = new ObservableCollection<OperationStatus>();
try
{
foreach(var action in actions)
{
var status = new OperationStatus() { StatusText = action.StatusText };
OperationStatuses.Add(status);
try
{
await action?.AsyncAction();
status.Success = true;
}
catch
{
status.Success = false;
}
finally
{
status.IsFinished = true;
}
}
}
catch (Exception ex)
{
// logging is here
}
finally
{
await Task.Delay(1000);
IsBusy = false;
MultipleOperatrions = false;
OperationStatuses = new ObservableCollection<OperationStatus>();
}
}
My models:
public class Operation
{
public Func<Task> AsyncAction { get; private set; }
public string StatusText { get; private set; }
public Operation(Func<Task> action, string statusText)
{
AsyncAction = action;
StatusText = statusText;
}
}
My code of calling the methods in the view model:
...
private IAsyncCommand _buildCommand;
public IAsyncCommand BuildCommand => _buildCommand ?? (_buildCommand = new AsyncCommand(Build));
#endregion
#region Methods
public async Task Build()
{
// IT WORKS
// this method is used for performing only a single operation
// await PerformOperation(async () => { await Task.Delay(3000); });
// IT CRASHES THE APP
// for many tasks
await PerformOperation(new Operation(async () => { await Task.Delay(3000); }, "Preparing..."));
}
...
So, if I call PerformOperation to call just only a single method - it works fine. But it doesn't for the method of performing of several operations - the application just crashes without any exception or messages. I subscribed in App.cs to UnhandledException event to catch it but the app just crashes.

Nested Transactions with MySQL and Entity Framework Core

I'm using MySQL with EF Core. I am currently using Pomelo Provider for MySQL. I need to implement Unit Of Work Pattern for transactions. I have a Service which calls two methods in repository. I am not able to implement nested transactions. This is how my method in service looks now:
public void methodA(param)
{
using (TransactionScope tx = new
TransactionScope(TransactionScopeOption.Required))
{
repo1.save(data1);
repo2.save(data2);
tx.complete();
}
}
This is how save method in repo1 is implemented
private readonly UserDbContext appDbContext;
public repo1(UserDbContext _appDbContext)
{
appDbContext = _appDbContext;
}
public void save(User entity)
{
var dbset = appDbContext.Set<User>().Add(entity);
appDbContext.SaveChanges();
}
This is how save method in repo2 is implemented
private readonly UserDbContext appDbContext;
public repo2(UserDbContext _appDbContext)
{
appDbContext = _appDbContext;
}
public void save(UserRole entity)
{
var dbset = appDbContext.Set<UserRole>().Add(entity);
appDbContext.SaveChanges();
}
I am getting the following error while running method in service:
Error generated for warning 'Microsoft.EntityFrameworkCore.Database.Transaction.AmbientTransactionWarning: An ambient transaction has been detected. The current provider does not support ambient transactions. See http://go.microsoft.com/fwlink/?LinkId=800142'. This exception can be suppressed or logged by passing event ID 'RelationalEventId.AmbientTransactionWarning' to the 'ConfigureWarnings' method in 'DbContext.OnConfiguring' or 'AddDbContext'.
This is how I registered UserDbContext in Startup.cs
services.AddDbContext<UserDbContext>(options => options.UseLazyLoadingProxies().UseMySql("Server = xxxx; Database = xxx; Uid = xx;ConnectionReset=True;", b => b.MigrationsAssembly("AssemblyName")));
I even tried adding a middleware which starts transaction at the begining of request and commits/rollbacks during the response . But still I am not able to manage nested transactions.
This is how my middleware looks:
public class TransactionPerRequestMiddleware
{
private readonly RequestDelegate next_;
public TransactionPerRequestMiddleware(RequestDelegate next)
{
next_ = next;
}
public async Task Invoke(HttpContext context, UserDbContext
userDbContext)
{
var transaction = userDbContext.Database.BeginTransaction(
System.Data.IsolationLevel.ReadCommitted);
await next_.Invoke(context);
int statusCode = context.Response.StatusCode;
if (statusCode == 200 || statusCode==302)
{
transaction.Commit();
}
else
{
transaction.Rollback();
}
}
}
Can anyone help me please?

How to inject services in a controller inside a reusable Razor Class Library

I am using a Razor Class Library for making a reusable complex View (which includes its controller and several View Components) that can be used across several ASP.NET Core MVC projects. The problem is that the controller use dependency injection (a custom service called "GatewayProxy" and string localization). What is the correct way to inject services into a controller inside a RCL?
Here is the structure of my RCL:
Here is the exception:
You mentioned how you fixed this by adding the dependencies to Startup.cs of your main project. But consider that any consumer of this reuseable library may not remember (or know) what dependencies are needed for your library.
Something you can do to solve this is to create an extension off of IServiceCollection in your Rcl that does the dependency registration.
public static void AddMyRclServices(this IServiceCollection serviceCollection, IConfiguration config)
{
serviceCollection.AddTransient<IRclService1, RclService1>();
serviceCollection.AddScoped<IRclService2, RclService2>();
}
Then in Startup.cs for your MVC project call the extension
using Rcl.Extensions
public void ConfigureServices(IServiceCollection services)
{
services.AddMyRclServices(config);
}
You can inject from load assemblies.
my code
ISturtupInitializers.cs
public interface ISturtupInitializers {
void Compose(IServiceCollection serviceCollection);
}
TypeLoader.cs
public static readonly Lazy<HashSet<Assembly>> AllAssemblies = new Lazy<HashSet<Assembly>>(() => {
var bin_folder = Assembly.GetExecutingAssembly().GetFileInfo().Directory.FullName;
var bin_assembly_files = Directory.GetFiles(bin_folder, "*.dll", SearchOption.TopDirectoryOnly).Where(x => !SystemAssemblies.Any(xs => x.IndexOf(xs, StringComparison.CurrentCultureIgnoreCase) >= 0)).ToList();
var assemblies = new HashSet<Assembly>();
foreach (var a in bin_assembly_files)
{
try {
var assName = AssemblyName.GetAssemblyName(a);
var ass = Assembly.Load(assName);
assemblies.Add(ass);
} catch (SecurityException e) {
//ignore
} catch (FileNotFoundException e) {
//ignore
} catch (BadImageFormatException e) {
//ignore
}
catch (Exception e) {
throw;
}
}
return assemblies;
});
public static string[] SystemAssemblies = new[] {
"Microsoft.",
"System.",
"Newtonsoft."
};
public static FileInfo GetFileInfo(this Assembly assembly)
{
var uri = new Uri(assembly.CodeBase);
var path = uri.LocalPath;
return new FileInfo(path);
}
Composer.cs
public class Composer {
private static readonly Lazy<HashSet<Type>> _sturtup_initializers = new Lazy<HashSet<Type>>(() => {
var aseemblies = TypeLoader.AllAssemblies.Value.SelectMany(x=>GetAllTypesInAssembly(x, typeof(ISturtupInitializers)));
var result = new HashSet<Type>();
foreach (var item in aseemblies) {
result.Add(item);
}
return result;
});
public static void Compose(IServiceCollection services) {
var composers = _sturtup_initializers.Value;
foreach (var compose in composers) {
public static void Compose(IServiceCollection services, IConfiguration configuration) {
var composers = _sturtup_initializers.Value;
var provider = services.BuildServiceProvider();
foreach (var compose in composers) {
((IStartupInitializers) ActivatorUtilities.CreateInstance(provider, compose)).Compose(services, configuration);
}
}
}
}
private static IEnumerable<Type> GetAllTypesInAssembly(Assembly assembly, Type implementInterface) {
var types = assembly.GetTypes();
return types.Where(x => x.GetInterfaces().Any(i => i == implementInterface));
}
}
MyStartup.cs
public class ServiceCollectionExtensions : ISturtupInitializers
{
public void Compose(IServiceCollection serviceCollection) {
/** YOUR CODE **/
}
}
Startup.cs
public void ConfigureServices(IServiceCollection services) {
/** YOUR CODE **/
Composer.Compose(services);
}

I am getting error in LoginAsync method during wp8 App development for sky drive

I am developing a windows phone 8 application to access sky drive. I am getting following error when I call LoginAsync() method-
An exception of type 'Microsoft.Live.LiveAuthException' occurred in mscorlib.ni.dll but was not handled in user code
using System;
using System.Windows;
using Microsoft.Phone.Controls;
using Microsoft.Live;
namespace SkyDriveApp
{
public partial class MainPage : PhoneApplicationPage
{
// Constructor
LiveConnectClient client;
public MainPage()
{
InitializeComponent();
}
public async void Auth()
{
string clientId = "My_client_id";
LiveAuthClient auth = new LiveAuthClient(clientId);
// var result = await auth.InitializeAsync(new[] { "wl.basic", "wl.signin", "wl.skydrive_update" });
var result = await auth.LoginAsync(new[] { "wl.basic", "wl.signin", "wl.skydrive_update" });
if (result.Status == LiveConnectSessionStatus.Connected)
{
client = new LiveConnectClient(result.Session);
tbMessage.Text = "Connected!";
}
}
private void btnLogin_SessionChanged(object sender, Microsoft.Live.Controls.LiveConnectSessionChangedEventArgs e)
{
Auth();
}
}
}
I see that you are using provided login buton, try this:
In xaml:
<live:SignInButton Name="skyBtn" ClientId="your client ID" Scopes="wl.signin wl.skydrive wl.skydrive_update" Branding="Skydrive" TextType="Login"/>
In code behind:
private void skyBtn_SessionChanged(object sender, Microsoft.Live.Controls.LiveConnectSessionChangedEventArgs e)
{
if (e.Status == LiveConnectSessionStatus.Connected)
{
session = e.Session;
client = new LiveConnectClient(session);
tbMessage.Text = "Connected!";
}
else tbMessage.Text = "Not Connected!";
if (e.Error != null)
{
tbMessage.Text = "Not Connected!";
Dispatcher.BeginInvoke(() =>
{
MessageBox.Show(e.Error.Message);
});
}
}

json ihttpmodule compression

I wrote an IHttpModule that compress my respone using gzip (I return a lot of data) in order to reduce response size.
It is working great as long as the web service doesn't throws an exception.
In case exception is thrown, the exception gzipped but the Content-encoding header is disappear and the client doesn't know to read the exception.
How can I solve this? Why the header is missing? I need to get the exception in the client.
Here is the module:
public class JsonCompressionModule : IHttpModule
{
public JsonCompressionModule()
{
}
public void Dispose()
{
}
public void Init(HttpApplication app)
{
app.BeginRequest += new EventHandler(Compress);
}
private void Compress(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
HttpRequest request = app.Request;
HttpResponse response = app.Response;
try
{
//Ajax Web Service request is always starts with application/json
if (request.ContentType.ToLower(CultureInfo.InvariantCulture).StartsWith("application/json"))
{
//User may be using an older version of IE which does not support compression, so skip those
if (!((request.Browser.IsBrowser("IE")) && (request.Browser.MajorVersion <= 6)))
{
string acceptEncoding = request.Headers["Accept-Encoding"];
if (!string.IsNullOrEmpty(acceptEncoding))
{
acceptEncoding = acceptEncoding.ToLower(CultureInfo.InvariantCulture);
if (acceptEncoding.Contains("gzip"))
{
response.AddHeader("Content-encoding", "gzip");
response.Filter = new GZipStream(response.Filter, CompressionMode.Compress);
}
else if (acceptEncoding.Contains("deflate"))
{
response.AddHeader("Content-encoding", "deflate");
response.Filter = new DeflateStream(response.Filter, CompressionMode.Compress);
}
}
}
}
}
catch (Exception ex)
{
int i = 4;
}
}
}
Here is the web service:
[WebMethod]
public void DoSomething()
{
throw new Exception("This message get currupted on the client because the client doesn't know it gzipped.");
}
I appriciate any help.
Thanks!
Even though it's been a while since you posted this question, I just had the same issue and here's how I fixed it:
In the Init() method, add a handler for the Error event
app.Error += new EventHandler(app_Error);
In the handler, remove Content-Type from the Response headers and set the Response.Filter property to null.
void app_Error(object sender, EventArgs e)
{
HttpApplication httpApp = (HttpApplication)sender;
HttpContext ctx = HttpContext.Current;
string encodingValue = httpApp.Response.Headers["Content-Encoding"];
if (encodingValue == "gzip" || encodingValue == "deflate")
{
httpApp.Response.Headers.Remove("Content-Encoding");
httpApp.Response.Filter = null;
}
}
Maybe there's a more elegant way to do this but did the trick for me.