I have an engine (ABBYY FlexiCapture Engine), and I want to use it with REST service ( WebAPI or WCF).
Engine start initialization takes 30 seconds. It is too long for every request. I want engine to be loaded with service start and stay in "warmup" state.
How can I do it with WebAPI or WCF? (what is the best approach keep thread with engine alive).
PS: sorry for my bad English.
-- from ABBYY Example library
private void ProcessImages()
{
trace("Loading FlexiCapture Engine...");
IEngine engine = LoadEngine();
try
{
string samplesFolder = FceConfig.GetSamplesFolder();
trace("Creating and configuring the FlexiCapture Processor...");
IFlexiCaptureProcessor processor = engine.CreateFlexiCaptureProcessor();
processor.AddDocumentDefinitionFile(samplesFolder + "\\SampleProject\\Templates\\Invoice_eng.fcdot");
// ....
trace("Adding images to process...");
processor.AddImageFile(samplesFolder + "\\SampleImages\\Invoices_1.tif");
// ....
trace("Recognizing the images and exporting the results...");
while (true)
{
// Recognize next document
IDocument document = processor.RecognizeNextDocument();
// processing recognized document...
}
}
finally
{
UnloadEngine(ref engine);
}
}
private IEngine LoadEngine()
{
// FlexiCapture Engine can be loaded in three diffrent ways:
// 1) Directly, as in this sample
// 2) As an inproc server (using COM infrastructure)
// 3) As an out-of-proc server in a worker process (using COM infrastructure)
IEngine engine;
int hResult = InitializeEngine(FceConfig.GetDeveloperSN(), null, null, out engine);
Marshal.ThrowExceptionForHR(hResult);
return engine;
}
I want create engine. After that, I will create and configure several FlexiCapture Processors. (it takes long time, i want to do it on service startup)
Than, when user add images - i will use one of the Processors to recognize them.
on startup:
Create engine -> Create several Processors (+configure them);
on method invoke:
Take one of free processors -> recognize user images.
Maybe, you need to implement static singleton instance of your Engine and use context
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.Single)]
public static class EngineContainer
{
private static Engine _engine { get; set; }
public static Engine GetEngine
{
get { if (_engine == null) Init(); return _engine; }
}
}
If your engine don't have any user interface(UI) then you can make use of Windows Services with WCF (self host). Let Windows services to take care of your engine (ie. live object) and handle to user request via WCF services
Related
I have a requirement where we need a plugin to retrieve a session id from an external system and cache it for a certain time. I use a field on the entity to test if the session is actually being cached. When I refresh the CRM form a couple of times, from the output, it appears there are four versions (at any time consistently) of the same key. I have tried clearing the cache and testing again, but still the same results.
Any help appreciated, thanks in advance.
Output on each refresh of the page:
20170511_125342:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
20170511_125358:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
20170511_125410:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
20170511_125342:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
20170511_125437:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
20170511_125358:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
20170511_125358:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
20170511_125437:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
To accomplish this, I have implemented the following code:
public class SessionPlugin : IPlugin
{
public static readonly ObjectCache Cache = MemoryCache.Default;
private static readonly string _sessionField = "new_sessionid";
#endregion
public void Execute(IServiceProvider serviceProvider)
{
var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
try
{
if (context.MessageName.ToLower() != "retrieve" && context.Stage != 40)
return;
var userId = context.InitiatingUserId.ToString();
// Use the userid as key for the cache
var sessionId = CacheSessionId(userId, GetSessionId(userId));
sessionId = $"{sessionId}:{Cache.Select(kvp => kvp.Key == userId).ToList().Count}:{userId}";
// Assign session id to entity
var entity = (Entity)context.OutputParameters["BusinessEntity"];
if (entity.Contains(_sessionField))
entity[_sessionField] = sessionId;
else
entity.Attributes.Add(new KeyValuePair<string, object>(_sessionField, sessionId));
}
catch (Exception e)
{
throw new InvalidPluginExecutionException(e.Message);
}
}
private string CacheSessionId(string key, string sessionId)
{
// If value is in cache, return it
if (Cache.Contains(key))
return Cache.Get(key).ToString();
var cacheItemPolicy = new CacheItemPolicy()
{
AbsoluteExpiration = ObjectCache.InfiniteAbsoluteExpiration,
Priority = CacheItemPriority.Default
};
Cache.Add(key, sessionId, cacheItemPolicy);
return sessionId;
}
private string GetSessionId(string user)
{
// this will be replaced with the actual call to the external service for the session id
return DateTime.Now.ToString("yyyyMMdd_hhmmss");
}
}
This has been greatly explained by Daryl here: https://stackoverflow.com/a/35643860/7708157
Basically you are not having one MemoryCache instance per whole CRM system, your code simply proves that there are multiple app domains for every plugin, so even static variables stored in such plugin can have multiple values, which you cannot rely on. There is no documentation on MSDN that would explain how the sanboxing works (especially app domains in this case), but certainly using static variables is not a good idea.Of course if you are dealing with online, you cannot be sure if there is only single front-end server or many of them (which will also result in such behaviour)
Class level variables should be limited to configuration information. Using a class level variable as you are doing is not supported. In CRM Online, because of multiple web front ends, a specific request may be executed on a different server by a different instance of the plugin class than another request. Overall, assume CRM is stateless and that unless persisted and retrieved nothing should be assumed to be continuous between plugin executions.
Per the SDK:
The plug-in's Execute method should be written to be stateless because
the constructor is not called for every invocation of the plug-in.
Also, multiple system threads could execute the plug-in at the same
time. All per invocation state information is stored in the context,
so you should not use global variables or attempt to store any data in
member variables for use during the next plug-in invocation unless
that data was obtained from the configuration parameter provided to
the constructor.
Reference: https://msdn.microsoft.com/en-us/library/gg328263.aspx
I want to create a timer job or workflow which runs once a month and exports sharepoint list data to excel and stores this file in a document library.
I have downloaded the code to create timer job from below link but dont know how to include the above requirement
http://code.msdn.microsoft.com/SharePoint-2010-Custom-416cd3a1
//Create class derived from SPJonDefinition Class
class ListTimerJob : SPJobDefinition
{
public ListTimerJob()
: base()
{
}
public ListTimerJob(string jobName, SPService service, SPServer server, SPJobLockType targetType)
: base(jobName, service, server, targetType)
{
}
public ListTimerJob(string jobName, SPWebApplication webApplication)
: base(jobName, webApplication, null, SPJobLockType.ContentDatabase)
{
this.Title = "List Timer Job";
}
public override void Execute(Guid contentDbId)
{
// get a reference to the current site collection's content database
SPWebApplication webApplication = this.Parent as SPWebApplication;
SPContentDatabase contentDb = webApplication.ContentDatabases[contentDbId];
// get a reference to the "ListTimerJob" list in the RootWeb of the first site collection in the content database
SPList Listjob = contentDb.Sites[0].RootWeb.Lists["ListTimerJob"];
// create a new list Item, set the Title to the current day/time, and update the item
SPListItem newList = Listjob.Items.Add();
newList["Title"] = DateTime.Now.ToString();
newList.Update();
}
}
//Add Event receiver at Feature Level
[Guid("9a724fdb-e423-4232-9626-0cffc53fb74b")]
public class Feature1EventReceiver : SPFeatureReceiver
{
const string List_JOB_NAME = "ListLogger";
// Uncomment the method below to handle the event raised after a feature has been activated.
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
SPSite site = properties.Feature.Parent as SPSite;
// make sure the job isn't already registered
foreach (SPJobDefinition job in site.WebApplication.JobDefinitions)
{
if (job.Name == List_JOB_NAME)
job.Delete();
}
// install the job
ListTimerJob listLoggerJob = new ListTimerJob(List_JOB_NAME, site.WebApplication);
SPMinuteSchedule schedule = new SPMinuteSchedule();
schedule.BeginSecond = 0;
schedule.EndSecond = 59;
schedule.Interval = 5;
listLoggerJob.Schedule = schedule;
listLoggerJob.Update();
}
// Uncomment the method below to handle the event raised before a feature is deactivated.
public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
SPSite site = properties.Feature.Parent as SPSite;
// delete the job
foreach (SPJobDefinition job in site.WebApplication.JobDefinitions)
{
if (job.Name == List_JOB_NAME)
job.Delete();
}
}
I would also advice you not to use SharePoint timer jobs engine.
It's definitely not stable.
Sometimes jobs simply don't trigger, they are difficult and slow to instantiate.
Of course you could always spend time tweaking SharePoint to achieve stability, but there is no guarantee. I know it sounds imperative, but trust me, I can't remember all the problems we had with this engine, but we lost much time on it.
I recommend you Quartz.NET or Windows Scheduler, as mentionned earlier.
These are well proven solutions, used by many people, also for SharePoint.
We implemented Quartz.Net for SharePoint at my company, all our Timer Jobs run on this engine.
We had no glitch for two years.
Best Regards.
You should change the SPMinuteSchedule to SPMonthlyByDaySchedule, see http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spschedule.aspx.
But, my recommendation is to use windows server scheduler and console application. Easy to change, easy to maintain (not iisreset!!) , and easy to log everything. We use console applications for various scheduled jobs, varying from 1 hour to 1 day.
the .net Windows Form application we developed (vb.net but c# answer is ok too) has some APIs (edit: yes, our own) to allow users to automate some tasks.
Everything is fine when the application is started through APIs by, say, Visual Studio. What we cannot get to work though is to assign an already running instance of our application to a new application object in visual studio.
We have seen there are some methods available for COM objects (getojbect) to access a running instance of an application but how about .net applications?
Rephrasing the question, we would like that, when a user calls the New() constructor of our application, the new object points to the running instance of our application (if any) instead of trying to create a new one (which is not possible by the way because we have made it single instance by checking through Mutex that no other instance of our application is running).
EDIT:
Sample code in the user application to automate some tasks
Imports TheApplication
Public Class WinFormByUser
Private ApplicationObject As TheApplication.MainForm
Public Sub OpenTheApplication()
ApplicationObject = New TheApplication.MainForm
Rem here theapplication should create a new instance if no instance of TheApplication is running. BUT, if an instance of the application
Rem is already running (in a different process, maybe started directly from the user), the ApplicationObject should point to the running
Rem instance from now on, instead of trying to create a new instance
ApplicationObject.DoSomething()
End Sub
End Class
Sample code inside TheApplication
Imports System.Threading
Public Class MainForm
Private ApplicationOpenedThroughAPI As Boolean = False
Private Shared mtx As Mutex
Private firstInstance As Boolean = False
Dim AppName As String = "TheApplicationName"
Public Sub New()
If Application.ProductName.ToString() <> AppName Then
Rem if TheApplication is opened externally through API the name is different therefore we can determine the boolean value
ApplicationOpenedThroughAPI = True
End If
mtx = New Mutex(True, AppName, firstInstance)
If firstInstance Then
InitializeComponent()
DoAllTheNecessaryStuff()
Else
If ApplicationOpenedThroughAPI = False Then
MsgBox("Application is running, can't open second instance")
Else
ReturnTheRunningInstance()
End If
End If
End Sub
Private Sub ReturnTheRunningInstance()
Rem please help here. what to do?
End Sub
Public Sub DoSomething()
Rem this does something and can be called by API user
End Sub
End Class
Please note that the solution could either be adding some code inside the application in the Sub ReturnTheRunningInstance() or in the user code, maybe checking if the application is running through something like Process.GetProcessesByName("TheApplicationName").Length and then do something in case.
Thanks!
We have seen there are some methods available for COM objects
(getojbect) to access a running instance of an application but how
about .net applications?
Let's start with this part. You essentially need to have one process access another process. .Net provides a variety of forms of cross-process communication. WCF seems the most appropriate here.
WCF is a large subject, but here's a basic architecture that might accomplish your goals.
Step 1
Have your application host a service, available to local callers over TCP.
Consider this pseudocode; there is plenty of documentation available on WCF once you know what to search for.
// the contract
[ServiceContract]
public interface IMyService
{
[OperationContract]
int Foo( int bar );
}
// the implementation
public MyService : IMyService
{
public int Foo( int bar ){ return bar * 100; }
}
// hosting the service within your application
var baseUri = new Uri( "net.tcp://localhost:59999/" );
var serviceHost = new ServiceHost( typeof( MyService ), baseUri );
// many options can/should be set here, e.g. throttling, security, and serialization behavior
var binding = new NetTcpBinding();
var endpoint = serviceHost.AddServiceEndpoint( typeof( IMyService ), binding, baseUri );
This is all you need for a caller to interface with an existing instance of the application, but it doesn't address the need to ensure that the app is running.
Step 2
A wrapper class may make it easier to locate/launch your application.
public sealed class MyWrapper
{
public IMyService GetService()
{
// TODO: perform appropriate OS-wide locking here
// TODO: see if app is running
// TODO: if not, launch it in a new process
// create a channel to connect the WCF endpoint we just defined
var channel = GetChannel();
// TODO: release lock
// return the channel to the caller
return channel;
}
public GetChannel( Binding binding, EndpointAddress endpointAddress )
{
var channelFactory = new ChannelFactory<IMyService>( binding, endpointAddress );
return _channelFactory.CreateChannel();
}
}
Step 3
Your callers can connect to your application from anywhere on the machine (or beyond, if you wish):
var wrapper = new Wrapper();
var service = wrapper.GetService();
int result = service.Foo( 123 );
While a bit unusual, your service code could also manipulate the GUI. For example:
var wrapper = new Wrapper();
var service = wrapper.GetService();
// call a method, the implementation of which launches a "contact form"
// with data preloaded for the specified contact ID
service.ShowContactForm( 1 );
Cleanup
Note that this syntax I've shown so far is elegant, but it doesn't handle closing the channel or channel factory. There are a variety of ways to do this; I've used a pattern like this:
public sealed class ServiceClient
{
private readonly ChannelFactory<IMyService> _channelFactory;
public ServiceClient( Binding binding, EndpointAddress endpointAddress )
{
_channelFactory = new ChannelFactory<IMyService>( binding, endpointAddress );
Channel = _channelFactory.CreateChannel();
}
public IMyService Channel { get; private set; }
public void Dispose()
{
if( Channel != null )
{
// TODO: check the state of the channel and close/abort appropriately
}
if( _channelFactory != null )
{
_channelFactory.Close();
}
}
}
public sealed class MyWrapper
{
public ServiceClient GetClient()
{
// Similar setup to the previous example, except the service client wraps
// the channel factory.
}
}
var wrapper = new Wrapper();
using( var client = wrapper.GetClient() )
{
client.Channel.Foo( 123 );
}
It's a bit more verbose, but it gives you much more control over cleanup and any other options you wish to control.
Solution Structure
All of this code can potentially live in one assembly. However, it may be cleaner to place the wrapper in a separate assembly and the service contract(s) interfaces into another assembly referenced by the wrapper and the main application.
Assembly 1: service contracts (interfaces)
Assembly 2: GUI application, references assembly 1 and implements its service contracts
Assembly 3: wrapper class, references assembly 1
Code Details:
// Singleton class CollectionObject
public class CollectionObject
{
private static CollectionObject instance = null;
// GetInstance() is not called from multiple threads
public static CollectionObject GetInstance()
{
if (CollectionObject.instance == null)
CollectionObject.instance = new CollectionObject();
return CollectionObject.instance;
}
// Dictionary object contains Service ID (int) as key and Service object as the value
// Dictionary is filled up during initiation, before the method call ReadServiceMatrix detailed underneath
public Dictionary<int, Service> serviceCollectionDictionary = new Dictionary<int,Service>();
public Service GetServiceByIDFromDictionary(int servID)
{
if (this.serviceCollectionDictionary.ContainsKey(servID))
return this.serviceCollectionDictionary[servID];
else
return null;
}
}
DataTable serviceMatrix = new DataTable();
// Fill serviceMatrix data table from the database
private int ReadServiceMatrix()
{
// Access the Singleton class object
CollectionObject collectionObject = CollectionObject.GetInstance();
// Parallel processing of the data table rows
Parallel.ForEach<DataRow>(serviceMatrix.AsEnumerable(), row =>
{
//Access Service ID from the Data table
string servIDStr = row["ServID"].ToString().Trim();
// Access other column details for each row of the data table
string currLocIDStr = row["CurrLocId"].ToString().Trim();
string CurrLocLoadFlagStr = row["CurrLocLoadFlag"].ToString().Trim();
string nextLocIDStr = row["NextLocId"].ToString().Trim();
string nextLocBreakFlagStr = row["NextLocBreakFlag"].ToString().Trim();
string seqStr = row["Seq"].ToString().Trim();
int servID = Int32.Parse(servIDStr);
int currLocID = Int32.Parse(currLocIDStr);
int nextLocID = Int32.Parse(nextLocIDStr);
bool nextLocBreakFlag = Int32.Parse(nextLocBreakFlagStr) > 0 ? true : false;
bool currLocBreakFlag = Int32.Parse(CurrLocLoadFlagStr) > 0 ? true : false;
int seq = Int32.Parse(seqStr);
// Method call leading to the issue (definition in Collection Object class)
// Fetch service object using the Service ID from the DB
Service service = collectionObject.GetServiceByIDFromDictionary(servID);
// Call a Service class method
service.InitLanes.Add(new Service.LaneNode(currLoc.SequentialID, currLocBreakFlag, nextLoc.SequentialID, nextLocBreakFlag, seq));
}
Issue that happens is:
In the code above for all the Service objects in the dictionary, the subsequent method call is not made, leading to issues in further processing. It has to o with fetching the Service object from the dictionary in parallel mode
The db an dictionary contains all the Ids /Service objects, but my understanding is when processing in Parallel mode for the Singleton class, few of the objects are skipped leading to the issue.
In my understanding the service id passed and service object created is local to a thread, so there shouldn't be an issue that I am facing. This kind of issue is only possible, when for a given method call one thread replace service id value of another thread by its, thus both end up with Service object and few are thus skipped, which is strange in my view until and unless I do not understand the Multi threading in this case correctly
Currently I am able to run the same code in non threaded mode by using the foreach loop instead of Parallel.ForEach / Parallel.Invoke
Please review and let me know your view or any pointer that can help me resolve the issue
In my understanding the service id passed and service object created
is local to a thread
Your understanding is incorrect, if two threads request the same service id the two threads will be both working on the same singular object. If you wanted separate objects you would need to put some kind of new Service() call in GetServiceByIDFromDictionary instead of a dictionary of existing values.
Because multiple threads could be using the same service objects I think your problem lies from the fact that service.InitLanes.Add is likely not thread safe.
The easiest fix is to just lock on that single step
//...SNIP...
Service service = collectionObject.GetServiceByIDFromDictionary(servID);
// Call a Service class method, only let one thread do it for this specific service instance,
// other threads locking on other instances will not block, only other threads using the same instance will block
lock(service)
{
service.InitLanes.Add(new Service.LaneNode(currLoc.SequentialID, currLocBreakFlag, nextLoc.SequentialID, nextLocBreakFlag, seq));
}
}
This assumes that this Parallel.Foreach is the only location collectionObject.GetServiceByIDFromDictionary is used concurrently. If it is not, any other locations that could potentially be calling any methods on returned services must also lock on service.
However if Service is under your control and you can somehow modify service.InitLanes.Add to be thread safe (perhaps change InitLanes out with a thread safe collection from the System.Collections.Concurrent namespace) that would be a better solution than locking.
1.Implementing singleton always think about using of it in mulithreaded way. Always use multithreaded singleton pattern variant, one of them - lazy singleton. Use Lazy singleton using System.Lazy with appropriate LazyThreadSafeMode consturctor argument:
public class LazySingleton3
{
// static holder for instance, need to use lambda to enter code here
//construct since constructor private
private static readonly Lazy<LazySingleton3> _instance
= new Lazy<LazySingleton3>(() => new LazySingleton3(),
LazyThreadSafeMode.PublicationOnly);
// private to prevent direct instantiation.
private LazySingleton3()
{
}
// accessor for instance
public static LazySingleton3 Instance
{
get
{
return _instance.Value;
}
}
}
Read about it here
2.Use lock-ing of your service variable in parallel loop body
// Method call leading to the issue (definition in Collection Object class)
// Fetch service object using the Service ID from the DB
Service service = collectionObject.GetServiceByIDFromDictionary(servID);
lock (service)
{
// Call a Service class method
service.InitLanes.Add(new Service.LaneNode(currLoc.SequentialID,
currLocBreakFlag, nextLoc.SequentialID,
nextLocBreakFlag, seq));
}
3.Consider to use multithreading here. Using lock-ing code make your code not so perfomant as synchronous. So make sure you multithreaded/paralelised code gives you advantages
4.Use appropriate concurrent collections instead of reinventing wheel - System.Collections.Concurrent Namespace
I'm building a T4 template that will help people construct Azure queues in a consistent and simple manner. I'd like to make this self-documenting, and somewhat consistent.
First I made the queue name at the top of the file, the queue names have to be in lowercase so I added ToLower()
The public constructor uses the built-in StorageClient API's to access the connection strings. I've seen many different approaches to this, and would like to get something that works in almost all situations. (ideas? do share)
I dislike the unneeded HTTP requests to check if the queues have been created so I made is a static bool . I didn't implement a Lock(monitorObject) since I don't think one is needed.
Instead of using a string and parsing it with commas (like most MSDN documentation) I'm serializing the object when passing it into the queue.
For further optimization I'm using a JSON serializer extension method to get the most out of the 8k limit. Not sure if an encoding will help optimize this any more
Added retry logic to handle certain scenarios that occur with the queue (see html link)
Q: Is "DataContext" appropriate name for this class?
Q: Is it a poor practice to name the Queue Action Name in the manner I have done?
What additional changes do you think I should make?
public class AgentQueueDataContext
{
// Queue names must always be in lowercase
// Is named like a const, but isn't one because .ToLower won't compile...
static string AGENT_QUEUE_ACTION_NAME = "AgentQueueActions".ToLower();
static bool QueuesWereCreated { get; set; }
DataModel.SecretDataSource secDataSource = null;
CloudStorageAccount cloudStorageAccount = null;
CloudQueueClient cloudQueueClient = null;
CloudQueue queueAgentQueueActions = null;
static AgentQueueDataContext()
{
QueuesWereCreated = false;
}
public AgentQueueDataContext() : this(false)
{
}
public AgentQueueDataContext(bool CreateQueues)
{
// This pattern of setting up queues is from:
// ttp://convective.wordpress.com/2009/11/15/queues-azure-storage-client-v1-0/
//
this.cloudStorageAccount = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
this.cloudQueueClient = cloudStorageAccount.CreateCloudQueueClient();
this.secDataSource = new DataModel.SecretDataSource();
queueAgentQueueActions = cloudQueueClient.GetQueueReference(AGENT_QUEUE_ACTION_NAME);
if (QueuesWereCreated == false || CreateQueues)
{
queueAgentQueueActions.CreateIfNotExist();
QueuesWereCreated = true;
}
}
// This is the method that will be spawned using ThreadStart
public void CheckQueue()
{
while (true)
{
try
{
CloudQueueMessage msg = queueAgentQueueActions.GetMessage();
bool DoRetryDelayLogic = false;
if (msg != null)
{
// Deserialize using JSON (allows more data to be stored)
AgentQueueEntry actionableMessage = msg.AsString.FromJSONString<AgentQueueEntry>();
switch (actionableMessage.ActionType)
{
case AgentQueueActionEnum.EnrollNew:
{
// Add to
break;
}
case AgentQueueActionEnum.LinkToSite:
{
// Link within Agent itself
// Link within Site
break;
}
case AgentQueueActionEnum.DisableKey:
{
// Disable key in site
// Disable key in AgentTable (update modification time)
break;
}
default:
{
break;
}
}
//
// Only delete the message if the requested agent has been missing for
// at least 10 minutes
//
if (DoRetryDelayLogic)
{
if (msg.InsertionTime != null)
if (msg.InsertionTime < DateTime.UtcNow + new TimeSpan(0, 10, 10))
continue;
// ToDo: Log error: AgentID xxx has not been found in table for xxx minutes.
// It is likely the result of a the registratoin host crashing.
// Data is still consistent. Deleting queued message.
}
//
// If execution made it to this point, then we are either fully processed, or
// there is sufficent reason to discard the message.
//
try
{
queueAgentQueueActions.DeleteMessage(msg);
}
catch (StorageClientException ex)
{
// As of July 2010, this is the best way to detect this class of exception
// Description: ttp://blog.smarx.com/posts/deleting-windows-azure-queue-messages-handling-exceptions
if (ex.ExtendedErrorInformation.ErrorCode == "MessageNotFound")
{
// pop receipt must be invalid
// ignore or log (so we can tune the visibility timeout)
}
else
{
// not the error we were expecting
throw;
}
}
}
else
{
// allow control to fall to the bottom, where the sleep timer is...
}
}
catch (Exception e)
{
// Justification: Thread must not fail.
//Todo: Log this exception
// allow control to fall to the bottom, where the sleep timer is...
// Rationale: not doing so may cause queue thrashing on a specific corrupt entry
}
// todo: Thread.Sleep() is bad
// Replace with something better...
Thread.Sleep(9000);
}
Q: Is "DataContext" appropriate name for this class?
In .NET we have a lot of DataContext classes, so in the sense that you want names to appropriately communicate what the class does, I think XyzQueueDataContext properly communicates what the class does - although you can't query from it.
If you want to stay more aligned to accepted pattern languages, Patterns of Enterprise Application Architecture calls any class that encapsulates access to an external system for a Gateway, while more specifically you may want to use the term Channel in the language of Enterprise Integration Patterns - that's what I would do.
Q: Is it a poor practice to name the Queue Action Name in the manner I have done?
Well, it certainly tightly couples the queue name to the class. This means that if you later decide that you want to decouple those, you can't.
As a general comment I think this class might benefit from trying to do less. Using the queue is not the same thing as managing it, so instead of having all of that queue management code there, I'd suggest injecting a CloudQueue into the instance. Here's how I implement my AzureChannel constructor:
private readonly CloudQueue queue;
public AzureChannel(CloudQueue queue)
{
if (queue == null)
{
throw new ArgumentNullException("queue");
}
this.queue = queue;
}
This better fits the Single Responsibility Principle and you can now implement queue management in its own (reusable) class.