0

Quite a few questions/answers on this topic (only listing a couple that I found. There were many more).

  1. C# Parallel - Adding items to the collection being iterated over, or equivalent?
  2. ConcurrentQueue with multithreading

Thanks to many of them I've come up with what I'm hoping is a possible solution for my problem. I may also be overthinking it. I have an api that needs to write to a text file for logging purposes. Now the api is called N+ times and during each call, it needs to log the request. What I don't want to do is to stop the request from having to wait on the log to be recorded before returning the requested data. Now, the logs cannot just be dropped so it must also stack up on each request if the file is currently in use, using ReaderWriterLock for this. Then when the file isn't locked, I want to write the stacked logs.

I have come up with this in the hopes that it would satisfy the requirements but I think it will still cause a wait.

var wid = WindowsIdentity.GetCurrent().Token;
//add new log items
logs.Enqueue(helpers.createNewLog(requests));
string op;
while (logs.TryDequeue(out op))
{
    using (WindowsIdentity.Impersonate(wid))
    {
        //write to text file, location on shared drive
        var wrote = writers.WriteLog(op);
        //item cannot be written since file locked, add back to queue to try again
        if (!wrote)
        {
            logs.Enqueue(op);
        }
    }
 }

Logs is a global like so

private static ConcurrentQueue<string> logs = new ConcurrentQueue<string>();

I feel like something isn't right but I'm struggling with what it is and which would be the best way in order for the requirements to be meet and still work in a web farm.

Micah Montoya
  • 757
  • 1
  • 8
  • 24

1 Answers1

1

In my opinion, you should use a BlockingCollection instead of the ConcurrentQueue, here is an example of how you can use it as a Producer-Consumer is the same thing you are trying to do.

Now with ASP.Net you can insert modules to intercept every request, if you want to save a log, I suggest you register a module instead of going with your approach. On your Global.asax.cs you have a Register method

public class MvcApplication : System.Web.HttpApplication
{
    public static void Register()
    {
        //registering an HttpModule
        HttpApplication.RegisterModule(typeof(LogModule));
    }

    ....
}


public class LogModule: IHttpModule
{
    public void Dispose()
    {

    }

    public void Init(HttpApplication context)
    {
        context.LogRequest += LogEvent;
    }

    private void LogEvent(object src, EventArgs args)
    {
        if (HttpContext.Current.CurrentNotification == RequestNotification.LogRequest)
        {
            if ((MvcHandler)HttpContext.Current.Handler != null)
            {
                Debug.WriteLine("This was logged!");
                //Save the information to your file
            }
        }
    }
}

Hope this helps

Zinov
  • 3,817
  • 5
  • 36
  • 70
  • I'd already looked at the BlockingCollection and felt it was a bit overkill. I was probably wrong so I'll take another look. Thanks for the info on the module. – Micah Montoya Jul 24 '17 at 17:26
  • BlockingCollection uses ConcurrentQueue as underlying store by default. See https://msdn.microsoft.com/en-us/library/dd267312(v=vs.110).aspx – Tanveer Badar Jul 30 '17 at 19:15
  • @TanveerBadar yes you are right, in my opinion, is better to manipulate, but you are correct. – Zinov Jul 30 '17 at 19:55