Click here to Skip to main content
15,886,724 members
Please Sign up or sign in to vote.
5.00/5 (1 vote)
See more:
I have a situation whereby my web application will call an external assembly which contains all the business logic. I have no access to the web application other than it will call an appropriate method in the external assembly as and when needed.

Example:
When user click on post invoice from the web interface, the following will be executed.
C#
InvoiceHelper.Post(invoice);

Within the Post method, it will make numerous call to a local / remote database depending on the provided invoice.

Example:
foreach (var invoice in invoices) {
  if (invoice.Country == "USA") {
    InvoiceMgr.PostLocal(invoice); // Contains many calls to a local database
  }
  else if (invoice.Country == "China") {
    InvoiceMgr.PostForeign(invoice); // Contains many calls to a remote database
  }
}

The performance when invoice.Country = China is so bad that it take about 3 minutes before the entire operation is done.

To fix this, I make a WCF to be installed in China so that when invoice.Country = China when user post invoice from USA, I will instead call the WCF at China to do a InvoiceMgr.PostLocal(invoice); instead.
Example:
  foreach (var invoice in invoices) {
  if (invoice.Country == "USA") {
    InvoiceMgr.PostLocal(invoice); // Contains many calls to a local database
  }
  else if (invoice.Country == "China") {
    new InvoiceServiceClient().Post(invoice); // Make call to InvoiceMgr.PostLocal
  }
}

This cut down the processing to 10 seconds.

However right now, my business logic is referencing my WCF and my WCF is referencing my business logic.

Is there any way I can restructure the logic in the external assembly so that the circular reference is negated?
Posted

1 solution

You've a problem with your architecture somewhere. My initial thought is that the database access code being called inside InvoiceMgr.Post doesn't belong in a helper in the business logic - if it adds stuff to the database it would be better in a separate data access layer .
You can then hide the DAL behind the service or access directly.

As for the WCF service itself, unless you have other reasons for a service you can do other things to help the situation that might make more sense - effectively you are spinning the database call off to a thread that this code doesn't care about. One option is to queue the invoice for posting, and let the queue processing code handle the DB call (options include RabbitMQ[^]or MSMQ). Note that you need to deal with fault tolerance, but you have that problem with WCF too.
The other thing you could consider is batch processing - if it is establishing the connection to the foreign database which takes the time - batch up Chinese invoices so several updates are done at once, depending on the nature of the business this might not be possible.
 
Share this answer
 
Comments
yong2579 6-Aug-14 0:27am    
Hello Kieth, sincerely appreciate your time to help out here and massive thanks for your proposed solution. In a nutshell I understand these are what you are proposing:

1) Rather than helper to call manager that contains data access codes, have helper to call manager which in turn call data access objects (DAO) within DAL to make changes to the database.

2) Rather than posting immediately, setup some queue mechanism so that another thread which poll this queue and do the posting by itself.

3) Rather than posting one by one, batch up invoices and do a single post (E.g. InvoiceMgr.PostForeign(invoices);)

Hmm I do have some comments for each of the points:

1) Actually within InvoiceMgr.Post, it's calling a 3rd party integration API to process the provided invoice. Through profiling, I know this API is making lots of retrieval and some insertion/updating of data for each post operation. (E.g. IntegratorAPI.Post(invoice);)

2) The queue strategy looks feasible but this will impact the system on how invoices are posted. Example, a new invoice status "Pending Post" needs to be introduced and invoice having this status needs to have certain controls disabled while it's waiting to be posted.
The bad news is the web application is not within our control as it is done by another vendor. They hire us to build this posting mechanism via InvoiceHelper.Post
I think while it looks good, the additional cost to change the web application may not be too acceptable by them.

3) For posting by batch, it's doesn't quite solve the situation as a single invoice already take 2-3 minutes via the API.

Hmm I think I'm still kind of stuck in the situation, do you have another advices? Thanks!
Keith Barrow 6-Aug-14 4:47am    
I've had a good night's sleep, so I can better answer your question.
On your summary point 1 - this was my answer to your question about circular dependency, it doesn't matter whether it calls an API or the DB - the solution is to abstract the code calling code into a separate assembly. Better separation of concerns to do this anyhow - the OM shouldn't be concerned with how it is stored generally.

Fundamentally you have a problem with a long lived process blocking your main thread - the solution is to spin this process off to another thread - you are effectively doing this with your WCF call. You can do this internally within app, but you'd also want to make the thread call asynchronous so the main thread isn't hanging around waiting for the "PostToChina" thread to complete. The problem with this is that, as you have a web app, the main thread will respond and spin down before the posting is complete. This is why I suggested the queue - the message gets enqueued somewhere off the ASP.NET stack.
It's hard to give a one-size fits all answer, without knowing the detail of your app, but fundamentally you need to take the call taking the 2-3 mins off the "main" thread and call it in such a way that it is fire-and-forget. This will probably involve an intermediary - such as your WCF service or a message queue as there is a danger of the main thread being destroyed by the ASP.NET framework after response.
yong2579 6-Aug-14 21:16pm    
Hmm pardon me but for summary point 1, if I were to implement a possible solution, do you mean something like:

Original Scenario:

At [WebApp], invoke [Assembly1]InvoiceHelper.Post
At [Assembly1], invoke [WCF]new InvoiceServiceClient().Post
At [WCF], invoke [Assembly1]InvoiceMgr.PostLocal

Possible Solution:

At [WebApp], invoke [Assembly1]InvoiceHelper.Post
At [Assembly1], invoke [Assembly2]RemoteHelper.Post
At [Assembly2], invoke [WCF]new InvoiceServiceClient().Post
At [WCF], invoke [Assembly1]InvoiceMgr.PostLocal

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900