Transaction with WCF Async calls - wcf

I am trying to create a method as shown bellow.
private async void SaveAllData(Data1 data1, Data2 data2)
{
using (var transaction = new TransactionScope())
{
var task1 = _client.SaveData1Async(data1);
var task2 = _client.SaveData2Async(data2);
await Task.WhenAll(task1, task2);
transaction.Complete();
}
}
I am bit doubt that whether this transaction will work?
(I already have configured wcf to support transaction)
Could you please advice me...

Related

Testing Activity Compensate with MassTransit

I'm trying to test the compensation logic in my Activity, but I can't figure out how to kick off the Compensate method in a test. I have an activity that should throw an exception if something isn't found in the DB. I've figured out how to ensure that the activity in faulted in this case, but I can't figure out the compensation part.
My test class is below. Note that awaiting on the RoutingSlipActivityCompensated handler throws a TaskCanceledException. How do I test the compensation part of the activity?
[TestClass]
public class DisableTeamCheckInsActivityTests
{
Mock<ILogger<DisableTeamCheckInsActivity>> _logger;
CheckInsDbContext _db;
InMemoryTestHarness _harness;
ActivityTestHarness<DisableTeamCheckInsActivity, DisableTeamCheckIns, DisableTeamCheckInsLog> _activity;
[TestInitialize]
public async Task Initialize()
{
_logger = new Mock<ILogger<DisableTeamCheckInsActivity>>();
_db = CheckInDbContextFactory.Create();
_harness = new InMemoryTestHarness
{
TestTimeout = TimeSpan.FromSeconds(5)
};
_activity = _harness.Activity<DisableTeamCheckInsActivity, DisableTeamCheckIns, DisableTeamCheckInsLog>(
_ => new DisableTeamCheckInsActivity(_logger.Object, _db),
_ => new DisableTeamCheckInsActivity(_logger.Object, _db)
);
await _db.Database.EnsureCreatedAsync();
await _harness.Start();
}
[TestCleanup]
public async Task Cleanup()
{
await _db.Database.EnsureDeletedAsync();
await _harness.Stop();
}
[TestMethod]
public async Task Missing_Team_Throws()
{
var teamId = Guid.NewGuid();
var builder = new RoutingSlipBuilder(Guid.NewGuid());
builder.AddSubscription(_harness.BusAddress, RoutingSlipEvents.All);
var faulted = _harness.SubscribeHandler<RoutingSlipActivityFaulted>();
var compensated = _harness.SubscribeHandler<RoutingSlipActivityCompensated>();
builder.AddActivity(_activity.Name, _activity.ExecuteAddress, new
{
TeamId = teamId
});
await _harness.Bus.Execute(builder.Build());
var faultContext = await faulted;
Assert.AreEqual("System.InvalidOperationException", faultContext?.Message?.ExceptionInfo?.ExceptionType);
await compensated; // <-- This throws a TaskCanceledException
}
}
A routing slip with a single activity will never compensate. If the activity faults, the routing slip faults, but there isn't any compensation information in the routing slip because there were not any previous activities.
If the activity completed, and a subsequent activity faulted, then the compensate method would be called, and the ActivityCompensated event would be published.

NHibernate.LazyInitializationException .Net Web API

I am trying to use NHibernate in my project, i am getting the following error
"Initializing[BO.Job#34543]-failed to lazily initialize a collection of role: BO.Job.bInterview, no session or session was closed". can someone help me.
public HttpResponseMessage GetbyId(int Id)
{
Job job = new Job();
try
{
using (ISession session = NHibernateSession.OpenSession()) // Open a session to conect to the database
{
// books = session.Query<Book>().ToList(); // Querying to get all the jobs
JobRepo = new Repo<Job>(session);
job = JobRepo.GetById(Id, "Job_selectbyId");
}
return Request.CreateResponse(HttpStatusCode.OK, job);
}
catch (Exception exp)
{
Log.Error(exp);
return Request.CreateResponse(HttpStatusCode.ExpectationFailed, job);
}
}
public T GetById(int id,string SPName)
{
T result;
//return await Task.Run(() =>
//{
IQuery query = _session.GetNamedQuery(SPName);
query.SetParameter("job_id", id);
//book = _session.Query<Book>().Where(b => b.Id == id).FirstOrDefault();
result = query.UniqueResult<T>();
return result;
//});
}
This is probably because you're trying to access a property that's been marked as 'lazy' after the session has been closed.
My guess is that you're returning job and then the session is being disposed. Some other code is then attempting to access the Interview property of the returned job, which needs to session to implement the lazy loading feature but the session's been disposed.
You can change you query so that Interview is 'pre-fetched' or you can change your mapping so that there's no lazy initialisation on the class or the property.

Rebus Send in transactionscope

It was possible in previous (<=0.84.0) versions of Rebus to Send message in TransactionScope and it was sent only if scope is completed
using (var scope = new TransactionScope())
{
var ctx = new AmbientTransactionContext();
sender.Send(recipient.InputQueue, msg, ctx);
scope.Complete();
}
Is it possible to achive the same behaviour in Rebus2
As you have correctly discovered, Rebus version >= 0.90.0 does not automatically enlist in ambient transactions.
(UPDATE: as of 0.99.16, the desired behavior can be had - see the end of this answer for details on how)
However this does not mean that Rebus cannot enlist in a transaction - it just uses its own ambient transaction mechanism (which does not depend on System.Transactions and will be available when Rebus is ported to .NET core).
You can use Rebus' DefaultTransactionContext and "make it ambient" with this AmbientRebusTransactionContext:
/// <summary>
/// Rebus ambient transaction scope helper
/// </summary>
public class AmbientRebusTransactionContext : IDisposable
{
readonly DefaultTransactionContext _transactionContext = new DefaultTransactionContext();
public AmbientRebusTransactionContext()
{
if (AmbientTransactionContext.Current != null)
{
throw new InvalidOperationException("Cannot start a Rebus transaction because one was already active!");
}
AmbientTransactionContext.Current = _transactionContext;
}
public Task Complete()
{
return _transactionContext.Complete();
}
public void Dispose()
{
AmbientTransactionContext.Current = null;
}
}
which you can then use like this:
using(var tx = new AmbientRebusTransactionContext())
{
await bus.Send(new Message());
await tx.Complete();
}
or, if you're using it in a web application, I suggest you wrap it in an OWIN middleware like this:
app.Use(async (context, next) =>
{
using (var transactionContext = new AmbientRebusTransactionContext())
{
await next();
await transactionContext.Complete();
}
});
UPDATE: Since Rebus 0.99.16, the following has been supported (via the Rebus.TransactionScope package):
using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
scope.EnlistRebus(); //< enlist Rebus in ambient .NET tx
await _bus.SendLocal("hallÄ i stuen!1");
scope.Complete();
}

Do WCF support Asynchronously operations' invoke within TransactionScope?

I am trying out the WCF Transaction implementation and I come up with the idea that whether asynchronous transaction is supported by WCF 4.0.
for example,
I have several service operations with client\service transaction enabled, in the client side, I use a TransactionScope and within the transaction, I create Tasks to asynchronously call those operations.
In this situation, I am assuming that the transaction is going to work correctly, is that right?
I doubt that very much. It appears that you if you are starting an ascync operation you are no longer participating on the original transaction.
I wrote a little LINQPad test
void Main()
{
using (var scope = new TransactionScope(TransactionScopeOption.Required))
{
try
{
Transaction.Current.Dump("created");
Task.Factory.StartNew(Test);
scope.Complete();
}
catch (Exception e)
{
Console.WriteLine(e);
}
Thread.Sleep(1000);
}
Console.WriteLine("closed");
Thread.Sleep(5000);
}
public void Test()
{
using (var scope = new TransactionScope(TransactionScopeOption.Required))
{
Transaction.Current.Dump("test start"); // null
Thread.Sleep(5000);
Console.WriteLine("done");
Transaction.Current.Dump("test end"); // null
}
}
You'll need to set both the OperationContext and Transaction.Current in the created Task.
More specifically, in the service you'll need to do like this:
public Task ServiceMethod() {
OperationContext context = OperationContext.Current;
Transaction transaction = Transaction.Current;
return Task.Factory.StartNew(() => {
OperationContext.Current = context;
Transaction.Current = transaction;
// your code, doing awesome stuff
}
}
This gets repetitive as you might suspect, so I'd recommend writing a helper for it.

NHibernate transaction: Why does this result in committed data?

The following code demonstrates a misleading situation in which data
is committed to the database, even though commit is never called on a
transaction.
Could anyone explain why?
[TestFixture]
public class TestFixture
{
[Test]
public void Test()
{
var config = DoConfiguration();
using(var factory = config.BuildSessionFactory())
{
using (var session = factory.OpenSession())
{
CallSessionContext.Bind(session);
using(new TransactionScope())
{
using (session.BeginTransaction())
{
var myEntity = session
.CreateQuery("from myEntity")
.List<MyEntity>()[0];
myEntity.Name = "test name";
}
var myEntity2 = session
.CreateQuery("from myEntity")
.List<MyEntity>()[0];
myEntity2.Name = "test name";
session.Flush();
}
CallSessionContext.Unbind(factory);
}
}
}
}
Explicitly calling session.flush() is persisting your changes. Discussed in detail in this post