I read about the wcf transactions but not able to find out its use. Does anybody know any scenario where we should use wcf transaction. Realtime example would be helpful to understand.
A WCF Transaction allow us to make operations that consumers can use inside transactions. We can call a wcf service operation inside a transaction scope (for example) and ensure that our operation will be atomic. It's really helpful to ensure integrity in our operations.
For example, suppose that we have two wcf operations:
debit(double mont,string account);
deposit(double mont,string
If I've implemented wcf transactions I can build up a transfer operation mixing the two operations in a transaction and I'll be sure that there will not be any inconsistency. If any of service calls fail , the entire transaction will rollback.
try
{
using(TransactionScope scope = new TransactionScope())
{
IserviceClient client = new IserviceClient();
client.debit(499,"acdf5-sdsd-4546-223-2");
client.deposit(499,"45651-as4d-ghhd-222-1");
scope.Complete();
}
}
catch
{
Debug.WriteLine("Some error occurred...");
}
It can be helpful. http://www.codeproject.com/Articles/183708/WCF-Transactions-Brief-Introduction
EDIT: You should use transactions when the service operation changes any state (a database insert,update or delete, any file modification) although transactions are not useful when you do a simple read operation for example.
Related
According to this post, I am using a data context per call, so in each method of my WCF service, I use a using block to create a new data context.
But I have some doubts in the form to work in this way.
For example, I use a method getAllCLients() from my repository to get all the clients of the data base, then the service send to the client that call the method a list with all the clients. Then the user modify the information of some of them, three for example. The modify client perhaps I can add to a list that have the modified clients.
When I want to update this three clients, I can call a method updateClients() which receive a list of modified clients. How I am use a new data context per each method, in updateCients() get a new dataContext, without entities, so I think that I have to follow this steps:
1.- create a new data context which has the clients that I want to update. SO I need to specified the conditions for that. This is an extra operation (I get the clients before with the getAllClients() method), so I need to get again the clients.
2.- go throw the clients collection of the DBSet (I use EF 4.1) and change the information. This makes me to go throw the list that I receive from the client application too. So I must to go throw two lists. This needs resources.
3.- save the changes. This is needed anyway, so it has no required more work.
There is any way to make the step 2 easily? exist some method in dataContext to pass the values from my modified client to the client in the data context? I use POCO entities, perhaps it exists an easy way to do that.
Other question is about concurrency. If I control the concurrency with pesimistic concurrency that allow EF (with a timestamp field for example), is it better to call the updateClient() one for each client or better to pass a list with all the clients? I mean that if I use a list as parameter, if there is a concurrency issue with one client,the second for example, the first client will be update correctly, but the second not and the third neither. How can I notify to the user that there is problems with some clients?
To resume, I would like to know the best way to make updates when I have a short life datacontext.
Thanks.
Daimroc.
The service is disconnected scenario so when your client passes backs modified records you just need to process them as modified. You don't need to load all records from database for that.
public void SaveClients(List<Client> modifiedClients)
{
using (var context = new Context())
{
modifiedClients.ForEach(c =>
{
context.Entry(c).State = EntityState.Modified;
});
context.SaveChanges();
}
}
If you are using per call service and every service operation needs context you can move your context instancing to service constructor because service instance will live only to server single service call = you don't need using for every call. If you do that don't forget to implement IDisposable on your service to dispose context.
Other question is about concurrency. If I control the concurrency with
pesimistic concurrency that allow EF (with a timestamp field for
example), is it better to call the updateClient() one for each client
or better to pass a list with all the clients?
EF doesn't support pesimistic concurrency out of the box. Using timestamp is optimistic concurrency because it allows others to use the record. Pesimistic concurrency is application logic where other client is not able to select locked record for update.
The concurrency is resolved per record but the problem in this case is transaction. Each call to SaveChanges results in transaction used to process all changes in the database. So if any of your modified records is not up to date you will get concurrency exception and whole transaction is rolled back = no record is updated.
You can still overcome the issue by passing list of modified records to the service (reducing roundtrips between client and service is a best practice) but you can process each record separately by calling SaveChanges for every single record. Anyway this should be very carefully considered because each call to SaveChanges is like separate unit of work - is it really what you want?
Btw. the best practice is to make your service statless. You should avoid maintaining data between service calls and this example really doesn't need it.
Regarding transactions in WCF services, shall I use TransactionScope object in client side application that consumes the service or in the service code? Please explain why.
It depends on your usage scenario. If your service function executes multiple calls that should be grouped in a transaction you should use a Transaction in your service function
Using a Transaction in your client code is useful when your client calls multiple service functions that should be bundled together in one transaction.
So for example:
public void MyServiceFunction1()
{
using (TransactionScope transaction = new TransactionScope())
{
// execute some logic inside this service function that should be in a transaction
}
}
public void MyServiceFunction2()
{
using (TransactionScope transaction = new TransactionScope())
{
// execute some logic inside this service function that should be in a transaction
}
}
public void MyClientFunction()
{
using (TransactionScope transaction = new TransactionScope())
{
// Bundle both service calls in one transaction
MyService service = new MyService();
service.MyServiceFunction1();
service.MyServiceFunction2();
}
}
Be aware however that you need to configure your WCF bindings correctly to make sure that the transaction is passed from client to server. It will then automatically promote in a distributed transaction.
Making a service transaction aware means that if passed a transaction it will enlist in it
Whoever consumes the service, be it a client application or another service, must create a transaction (or already be running in a transaction flowed to them) for the service to enlist - hence they must call the service from within a TransactionScope (either explicitly or implicitly by having the transaction flowed to them).
If the operation is marked as TransactionFlow(TransactionFlowOption.Allowed) then the consumer does not have to have a transaction, but then the service will not execute in a flowed transation
If the operation is marked as TransactionFlow(TransactionFlowOption.Mandatory) then the client must flow a transaction, and assuming the other bits are aligned (OperationBehavior creates auto enlists, etc) then the operation will run in the same distributed transaction
I've been trying several different ways in order to get a simple set of transactions to work for a simple WCF client/server situation. My WCF server has a class level declaration of the Entity Framework class for my database access and several methods to modify data and a method to SaveChanges. I'm using the Oracle Data Access (ODP.NET).
For instance I want to call a modification from the client and then a separate call to save the changes in the WCF service. It doesn't work. Basically, everything executes fine, but when the second call to save the changes is made, the WCF service no longer has the original context and therefore no changes are saved (and, consequently, the previous call that made the changes was automatically rolled back).
I'm utilizing Transaction scope around both operations in my client and executing Complete() after done. My WCF services have OperationContract's that use [TransactionFlow(TransactionFlowOption.Mandatory)] and those method implementations use [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]. Finally, my web config is configured with a wsHttpBinding that has the transactionFlow property set to True.
I'm having no luck. No matter what I try, when I try hitting the service for the follow-up save, the EF context is already renewed.
This has nothing to do with transaction. Transaction works on transactional resource but without calling SaveChanges in the first request there was no transactional resource active because EF context is not part of the transaction - the database is and the database is affected only when you call SaveChanges. To make this work you don't need distributed transactions. You need session-full service and store the EF context in the service instance. It a client uses the same client proxy instance to communicate with the service for all requests the communication will be handled by the same service instance = same EF context instance which will remember changes from previous calls.
IMHO this is very bad architecture. Simply don't use it. Expose specialized methods on WCF service which will do changes and save them. If you need to execute these methods in transaction with other transactional resources use the real distributed transaction.
this might be a reason. Since your are making an update in the different context. context doesn't know that the object is update to have say the context that the object is modified and then you call savechnages(). See if it helps
I have client calls coming in to WCF Service and then through Fluent NHibernate querying the db.
At the moment WCF is left as it's default (i.e., per call).
And in my code I do something like this:
using (_repository.DbContext.BeginTransaction()) {
try {
_repository.SavePerson(object);
_repository1.SaveAddress(object1);
} catch {
_repository.DbContext.RollbackTransaction();
throw;
}
}
since DbContext is the same for both _repository and _repository1. Do I need to do a RollBack on _repository1?
Also now since the Save methods in the repositories,
the Session object is used to save the objects.
What I need to know is,
is this Session the same for both calls, or are they 2 different ones? I am assuming they to be the same since
I am grouping them in transaction scope as one unit of work.
Also How does this coordinate with WCF calls, do I need to handle transactions from WCF side as well?
Your question can not be answered by someone who does not have access to your code. NHibernate session are not created by WCF, you do have some custom code for creating a session. If you want do stuff to r1 and r2 at the same time and rollback only r1, you need to have to sessions with a transaction on each session. Now you can rollback r1 without touching r2.
The solution is to open a second session from your session factory.
I'm using a custom message that inherits the System.Servicemodel.Channels.Message.
My custom message get IEnumerable collection which pulled from a database.
The WCF service is transactional (which is already tested).
MS-DTC is enabled.
Problem is, that when the protected override void OnWriteBodyContents(XmlDictionaryWriter writer) method is executed at the custom message, there is no transaction.
The System.Transactions.Transaction.Current is null. while executing the service the transaction flow is works fine, but when the message is written by WCF mechanism, it's seems like it's out of a transaction which cause the DB command (of pulling the data) to be executed without transaction.
Please note, that if I'm passing simple array instead of IEnumerable the DB action is executed under transaction, but I want it to be executed parallelly with the response writing.
Any ideas please, why the transaction is missing and what can be done to activate it?
many thanks!
Tamir.
Transaction are generally attached to your current thread or calling context. So if you are initiating transaction on different thread and message serialization is happing on another thread then transaction won't be available on that thread. You should look at TransactionScope and DependentTransaction for supporting such scenarios.