Dapper, .NET Core and Transactions fail - .net-core

We port our Dapper based application to .NET Core and we have a problem with our transaction code.
We use "actions" to execute stuff
public Action<IDbConnection> CreateAction(string statement, object values)
{
return (dbConnection) => dbConnection.Execute(statement, values);
}
And we use methods to execute those actions with
public void Execute(IEnumerable<Action<IDbConnection>> actions)
{
using (IDbConnection connection = OpenConnection())
using (IDbTransaction transaction = connection.BeginTransaction())
{
try
{
foreach (var action in actions)
{
action(transaction.Connection);
}
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
}
This works great with .NET Framework and Dapper 1.42, but it fails on .NET Core with Dapper 1.50.2.
System.InvalidOperationException: 'ExecuteNonQuery requires the command to have a transaction when the connection assigned to the command is in a pending local transaction. The Transaction property of the command has not been initialized.'
If we remove the transaction using it also works fine.
What needs to change to make it work?

Ok, it seems we now have to pass the transaction explicit.
public void Execute(IEnumerable<Action<IDbConnection, IDbTransaction>> actions)
{
using (IDbConnection connection = OpenConnection())
using (IDbTransaction transaction = connection.BeginTransaction())
{
try
{
foreach (var action in actions)
{
action(transaction.Connection, transaction);
}
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
}

Related

Use a message for a topic in Confluent.Kafka when consumer run

I'm using Confluent.Kafka(1.4.4) in a .netCore project as a message broker. In the startup of the project I set only "bootstrapservers" to the specific servers which were in the appSetting.json file and I produce messages in an API when necessary with the code below in related class:
public async Task WriteMessage<T>(string topicName, T message)
{
using (var p = new ProducerBuilder<Null, string>(_producerConfig).Build())
{
try
{
var serializedMessage= JsonConvert.SerializeObject(message);
var dr = await p.ProduceAsync(topicName, new Message<Null, string> { Value = serializedMessage });
logger.LogInformation($"Delivered '{dr.Value}' to '{dr.TopicPartitionOffset}'");
}
catch (ProduceException<Null, string> e)
{
logger.LogInformation($"Delivery failed: {e.Error.Reason}");
}
}
}
I have also added the following code In the consumer solution :
public async Task Run()
{
using (var consumerBuilder = new ConsumerBuilder<Ignore, string>(_consumerConfig).Build())
{
consumerBuilder.Subscribe(new List<string>() { "ActiveMemberCardForPanClubEvent", "CreatePanClubEvent", "RemovePanClubEvent"
});
CancellationTokenSource cts = new CancellationTokenSource();
Console.CancelKeyPress += (_, e) =>
{
e.Cancel = true; // prevent the process from terminating.
cts.Cancel();
};
try
{
while (true)
{
try
{
var consumer = consumerBuilder.Consume(cts.Token);
if (consumer.Message != null)
{
using (LogContext.PushProperty("RequestId", Guid.NewGuid()))
{
//Do something
logger.LogInformation($"Consumed message '{consumer.Message.Value}' at: '{consumer.TopicPartitionOffset}'.");
await DoJob(consumer.Topic, consumer.Message.Value);
consumer.Topic.Remove(0, consumer.Topic.Length);
}
}
else
{
logger.LogInformation($"message is null for topic '{consumer.Topic}'and partition : '{consumer.TopicPartitionOffset}' .");
consumer.Topic.Remove(0, consumer.Topic.Length);
}
}
catch (ConsumeException e)
{
logger.LogInformation($"Error occurred: {e.Error.Reason}");
}
}
}
catch (OperationCanceledException)
{
// Ensure the consumer leaves the group cleanly and final offsets are committed.
consumerBuilder.Close();
}
}
}
I produce a message and when the consumer project is run everything goes perfectly and the message is being read in the consumer solution.
The problem is raised when the consumer project is not run and I queue a message in the API with the message producer in API. After running consumers there is not any valid message for that topic that it's message is being produced.
I am familiar with and have experiences with message brokers and I know that by sending a message it will be on the bus until it is being used but I don't understand why it doesn't work with Kafka in this project.
The default setting for the "auto.offset.reset" Consumer property is "latest".
That means (in the context of no offsets being written yet) if you write a message to some topic and then subsequently start the consumer, it will skip past any messages written before the consumer was started. This could be why your consumer is not seeing the messages queued by your producer.
The solution is to set "auto.offset.reset" to "earliest" which means that the consumer will start from the earliest offset on the topic.
https://docs.confluent.io/current/installation/configuration/consumer-configs.html#auto.offset.reset

not able to migrated between 2 cosmos containers

I am trying to migrate between data between 2 containers with option StartFromBeginning =true. while migrating I am also making a small modification to the document also. When I add this logic I live sync between collections is not working. I had used Migrating data from old container to new partitioned container using change feed as reference which works. After deploying there seems to be no error but. How can I check what I am doing wrong. I have also enabled application insights.
namespace CosmosContainerMigration.Trigger
{
public class ContainerMigration
{
private IUpdatedDocument updatedDocument;
public ContainerMigration(IUpdatedDocument updatedDocument)
{
this.updatedDocument = updatedDocument;
}
[FunctionName("CosmosContainerMigration")]
public async Task Run([CosmosDBTrigger(
databaseName: "%SourceDatabaseName%",
collectionName: "%ContainerName%",
ConnectionStringSetting = "connectionString",
StartFromBeginning =true,
LeaseCollectionName ="%ContainerLeaseName%",
CreateLeaseCollectionIfNotExists = true)]IReadOnlyList<Document> source,
[CosmosDB(databaseName:"%TargetDatabaseName%",
collectionName:"%ContainerName%",
ConnectionStringSetting = "connectionString")]IAsyncCollector<Document> destination,
ILogger log)
{
log.LogInformation("Documents modified " + source.Count);
foreach (var item in source)
{
try
{
Document updatedItem = await this.updatedDocument.Update(item);
await destination.AddAsync(updatedItem);
}
catch (Exception)
{
log.LogInformation("Failed document ", item.Id);
throw;
}
}
}
}
}

Unable to Access SQLite Data in MvvmCross ViewModel

Hello StackOverflow community,
I know there's a lot of code in this post, but I wanted to give you guys, the community as good of a picture as possible as to what is going on here so that maybe someone can help me figure out what my issue is.
Recently for a project I'm working on we've decided to upgrade from MvvmCross 5.7.0 to 6.2.2. I've managed to get our UWP app to successfully complete the initialization and setup process. The first viewmodel for which we register the app start also starts initializing. However, I'm finding that my vm initialization hangs at a particular line of code (shown in the code below). The weirdest part though is similar methods called in the app initialization code run perfectly fine without hanging/deadlock, so I'm not sure what's different Here's a simplified version of my viewmodel code to illustrate:
public class MyViewModel : BaseAuthenticatedTabBarViewModel, IMvxViewModel<int>
{
private int? _settingValue;
public override async Task Initialize()
{
//Some irrelevant initialization code
Exception e = null;
try
{
//This line of code never returns
_settingValue = _settingValue ?? await AppSettingService.GetSettingValue();
}
catch (Exception ex)
{
e = ex;
}
if (e != null)
{
await HandleCatastrophicError(e);
}
}
}
The AppSettingService.GetSettingValue() method looks like this:
public async Task<int?> GetCurrentEventId()
{
return await GetNullableIntSetting("SettingValue");
}
private static async Task<int?> GetNullableIntSetting(string key)
{
try
{
var setting = await SettingDataService.SettingByName(key);
if (setting != null)
{
return string.IsNullOrEmpty(setting.Value) ? (int?)null : Convert.ToInt32(setting.Value);
}
}
catch (Exception ex)
{
//Handle the exception
}
return null;
}
All the code for SettingDataService:
public class SettingDataService : DataService<SettingDataModel>, ISettingDataService
{
public async Task<SettingDataModel> SettingByName(string name)
{
try
{
var values = (await WhereAsync(e => e.Name == name));
return values.FirstOrDefault();
}
catch(Exception ex)
{
//Handle the exception
}
return null;
}
}
Finally, the implementation for WhereAsync() is in a class called DataService and is as follows:
public virtual async Task<IEnumerable<T>> WhereAsync(System.Linq.Expressions.Expression<Func<T, bool>> condition, SQLiteAsyncConnection connection = null)
{
return await (connection ?? await GetConnectionAsync())
.Table<T>()
.Where(condition)
.ToListAsync();
}
Thank you very much for your help in advance
Edit: Forgot to also add this crucial bit of code to help you guys even further:
protected async Task<SQLiteAsyncConnection> GetConnectionAsync()
{
SQLiteAsyncConnection connection = null;
while (true)
{
try
{
connection = Factory.Create(App.DatabaseName);
// This line of code is the culprit. For some reason this hangs and I can't figure out why.
await connection.CreateTableAsync<T>();
break;
}
catch (SQLiteException ex)
{
if (ex.Result != Result.CannotOpen && ex.Result != Result.Busy && ex.Result != Result.Locked)
{
throw;
}
}
await Task.Delay(20);
}
return connection;
}
I'm suspecting that you are calling Task.Wait or Task<T>.Result somewhere further up your call stack. Or if you're not doing it, MvvmCross is probably doing it for you. This will cause a deadlock when called from a UI context.
Personally, I prefer the approach that ViewModels should always be constructed synchronously, and cannot have an asynchronous "initialization". That is, they must construct themselves (synchronously) into a "loading" state, and this construction can kick off an asynchronous operation that will later update them into a "loaded" state. The synchronous-initialization pattern means there's never an unnecessary delay when changing views; your users may only see a spinner or a loading message, but at least they'll see something. See my article on async MVVM data binding for a pattern that helps with this, and note that there's a newer version of the helper types in that article.

How to use Transactions in ASP.NET MVC identity 2?

In My ASP.NET MVC5 Identity 2 Application trying to use transactions but it is not working.please see the below code the transactions not working.If var saveteacher = _teacherService.Create(aTeacher); not insert successfully then AspNetUsers not rollback from database.
Code:
using (var dataContext = new SchoolMSDbContext())
{
using (var trans = dataContext.Database.BeginTransaction(IsolationLevel.ReadCommitted))
{
try
{
var adminresult =await UserManager.CreateAsync(user, teacherViewModel.Password);
if (adminresult.Succeeded)
{
aTeacher.Id = user.Id;
var saveteacher = _teacherService.Create(aTeacher);
}
else
{
trans.Rollback();
ModelState.AddModelError("", adminresult.Errors.First());
return View();
}
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
Console.WriteLine(ex.InnerException);
}
}
}
I think the problem could be with async stuff.
Try creating the transaction like this:
TransactionScope transaction = new TransactionScope(System.Transactions.TransactionScopeAsyncFlowOption.Enabled);
(you'll have to add System.Transactions) to references.
To commit transaction go transaction.Complete() to rollback do transaction.Dispose().
The problem is that you are creating a new instance of SchoolMSDbContext when you should be acquiring the existing one from the HttpContext.
Example:
using (var dataContext = HttpContext.GetOwinContext().Get<ApplicationDbContext>())
{
//...
}
Make sure _teacherService and UserManager uses same DB Context. No need to create a TransactionScope.

ajax with WCF work. but few miniute after, doesn't work

I am a new to WCF. I have written ajax to use a web service before, but on this project I am trying to use ajax to WCF.
After I build the project and wcf using ajax, I receive the return successfully. But, 10 or more minutes later I don't get a return, the ajax calls the error function, and the fiddler returns nothing.
If I rebuild the project without any source modifying, I receive the return successfully again.
Is their anybody who has experienced this or knows why this might be?
Thank You.
Most likely you're not closing the connections. You should wrap all your calls in Try/Catch/Finally blocks.
In C#:
ServiceClient service = GetService();
try
{
SomeRequest request = new SomeRequest();
SomeResponse response = service.GetSome(request);
return response.Result;
}
catch (Exception ex)
{
// do some error handling
}
finally
{
try
{
if (service.State != CommunicationState.Faulted)
{
service.Close();
}
}
catch (Exception ex)
{
service.Abort();
}
}
or VB
Dim service As ServiceClient = GetService()
Try
Dim request As New SomeRequest()
Dim response As SomeResponse = service.GetSome(request)
Return response.Result
Catch ex As Exception
' do some error handling
Finally
Try
If service.State <> CommunicationState.Faulted Then
service.Close()
End If
Catch ex As Exception
service.Abort()
End Try
End Try
Here is the best practice for calling WCF services:
public static void CallService<T>(Action<T> action) where T
: class, ICommunicationObject, new()
{
var client = new T();
try
{
action(client);
client.Close();
}
finally
{
if (client.State == CommunicationState.Opened)
{
try
{
client.Close();
}
catch (CommunicationObjectFaultedException)
{
client.Abort();
}
catch (TimeoutException)
{
client.Abort();
}
}
if (client.State != CommunicationState.Closed)
{
client.Abort();
}
}
}
Each WCF call should create a new instance of your service class. This code allows you to enforce that and just call the services like this:
CallService<MyService>( t => t.CallMyService());

Resources