Monday, November 22, 2021

A tiny and very handy CachedValue class

In server-side programming it happens very often that getting a value takes a lot of time, but the result can be stored for some time. A classical answer to this is of course caching. The class below allows to wrap this in a very simple model.

using System;

namespace IBSoft.Helpers
{
    public class CachedValue<T>
    {
        private T        value;
        private double   ttlInSeconds;
        private Func<T>  getValue;
        private DateTime expireAt = DateTime.MinValue;

        public CachedValue(int ttlInSeconds, Func<T> getValue)
        {
            this.ttlInSeconds = ttlInSeconds;
            this.getValue = getValue;
        }

        public T Value
        {
            get
            {
                if (DateTime.UtcNow > this.expireAt)
                {
                    T oldValue = this.value;

                    this.value = getValue();

                    var now = DateTime.UtcNow;
                    this.expireAt = now.AddSeconds(ttlInSeconds);

                    if (!this.value.Equals(oldValue))
                        this.LastValueUpdate = now;
                }

                return this.value;
            }
        }

        /// <summary>
        /// set when the value changes
        /// </summary>
        public DateTime LastValueUpdate { get; private set; }

        /// <summary>
        /// invalidates the value and causes reload on the next call
        /// </summary>
        public void Invalidate()
        {
            this.expireAt = DateTime.MinValue;
        }

        /// <summary>
        /// add implicit type conversion to T so that it's possible to use CacheValue<T> without the need to do cv.Value
        /// </summary>
        /// <param name="cv"></param>
        /// <returns></returns>
        public static implicit operator T(CachedValue<T> cv)
        {
            return cv.Value;
        }
    }
}

Monday, October 27, 2014

MSB3268 while targeting ASP.NET web site project to framework 4.5

Just spent 2 days with trying to understand why on Earth my ASP.NET web site project stopped compiling when I re-targeted it to framework 4.5.

My dependent projects were all re-compiled under framework 4.5 without any issue.

Visual Studio 2012 compiled the web site without any issue either.

While the build server, working under Jenkins had its MSBuild job failing with MSB3268 warnings saying that the System.Threading.Tasks and System.Runtime assemblies could not be found under the framework version of "v4.5".

A deeper inspection revealed the following interesting fact: aspnet_compiler for some reason does not take into account the .dll-s that reside under the Facade directory of 4.5 assemblies (C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Facades).
It looks only under
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5

As a result the whole thing failed since both System.Threading.Tasks and System.Runtime .dll-s were under the Facades directory and not inside the v4.5.


Now the solutions:

  1. Just simply copy the missing .dll-s from Facade to the v4.5 directory.
  2. Set the TargetFrameworkMoniker to 4.5.1 in the .sln file. The exact syntax is as follows: TargetFrameworkMoniker = ".NETFramework,Version%3Dv4.5.1".
          What happens in this case is that the aspnet_compiler does not recognize the exact version of the required framework and tries to use the GAC wherever it can. If 4.5 is the highest version installed on the build machine I believe it should work.

Very tiring.

Wednesday, February 12, 2014

Static CacheItemPolicy in .NET

An interesting riddle came today that took me some time to analyze. I was using the .NET 4 MemoryCache object to cache application entities and wanted to expire them with sliding expiration, i.e. after some time of inactivity. What I cached was dictionaries of objects. And naturally when releasing the objects held in these dictionaries some work is needed to make sure these objects are not referenced anywhere.

My first bet was using the RemovedCallback data member of the CacheItemPolicy to do the job there. Then it turned out that this is not optimal since upon expiration of the object it might happen that the object still needs to stay in the cache. The documentation suggests that UpdateCallback may be better suited here since it allows to tell the MemoryCache to keep the object. I changed the implementation and it seemed that it fits the application logic quite well.

Testing the application revealed some weird InvalidOperation exception with the message of "The method has already been invoked, and can only be invoked once." At the times that a various cache entries were added to cache.

In the end it took me quite some time to discover that the CacheItemPolicy that I was using to set the UpdateCallback was a static object. And it seems like .NET Framework internally makes use of ChangeMonitors in this case. Naturally using a static CacheItemPolicy was a very bad idea here since this object became a monster.

Adding a new CacheItemPolicy for each of the added cache items solved the problem of course.

So don't use static CacheItemPolicy. At least not if you use UpdateCallback.

Monday, February 18, 2013

Additional indexes help optimize DNN performance

When dealing with the slow performance of a DNN site I quickly analyzed it in the test environment with the ANTS Performance Profiler and understood that most of the time the system spent inside the DB querying some data.

The analysis moved then to SQL Performance Tuning tools. The Activity Monitor in the Server Management Studio comes in very handy. With enough server roles you can get to this screen that shows you the "Recent Expensive Queries" window where you can see the top queries and their execution plans.

I added just 2 indexes:

  • An index on ModuleId, IsPublished and LastModifiedOnDate for the HtmlText table
  • An index on ModuleId and PortalId on the Modules table
As a result the average execution time on the 2 top queries with hundreds of executions went down from 3ms to 0ms with logical reads greatly reduces.

I wonder why these indexes do not come right out of the box.


Wednesday, January 30, 2013

ORA-06502 with RAW OUT parameter called from ODP.NET

An interesting problem popped up today while calling a PL/SQL stored procedure from ODP.NET. It turns out that when you have an output RAW parameter you MUST provide some buffer space for it while adding this parameter, even though the parameter is OUTPUT only.

Here's a part of the definition of the stored procedure in Oracle:

PROCEDURE bla-bla(o_user_guid OUT RAW(16)) ...

Here's how it's called from C#:

using (OracleCommand cmd = con.CreateCommand())
{
     ...
     cmd.AddParameter(new OracleParameter("o_user_guid", 
                                                                   OracleDbType.Raw,  
                                                                   ParameterDirection.Output));
}

Strangely enough this code returns the following error:

ORA-06502: PL/SQL: numeric or value error: raw variable length too long

Adding the size of 16 to the creation of the parameter does not help.


What helps though is adding a dummy buffer while creation of the parameter as follows:

using (OracleCommand cmd = con.CreateCommand())
{
     ...
     byte[] userGuidPlaceholder = new byte[16];

     cmd.AddParameter(new OracleParameter("o_user_guid", 
                                                                  OracleDbType.Raw,  
                                                                  16, 
                                                                  userGuidPlaceholder, 
                                                                  ParameterDirection.Output);
}

Weird.