views:

468

answers:

3

I'm trying to get a randomly picked background image (from a selection of 4 images) to appear as the background image for a asp.net panel.

The problem I have is that the code works when stepping through the code in debug mode. Once you run the code on the website without debugging, all the images are the same. Its almost as if the random number is not getting picked up quick enough.

The usercontrol is inside of a datalist.

The usercontrol is this:

<asp:Panel ID="productPanel" CssClass="ProductItem" runat="server">

<div class="title" visible="false">
    <asp:HyperLink ID="hlProduct" runat="server" />
</div>
<div class="picture">
    <asp:HyperLink ID="hlImageLink" runat="server" />
</div>
<div class="description" visible="false">
    <asp:Literal runat="server" ID="lShortDescription"></asp:Literal>
</div>
<div class="addInfo" visible="false">
    <div class="prices">
        <asp:Label ID="lblOldPrice" runat="server" CssClass="oldproductPrice" />
        <br />
        <asp:Label ID="lblPrice" runat="server" CssClass="productPrice" /></div>
    <div class="buttons">
        <asp:Button runat="server" ID="btnProductDetails" OnCommand="btnProductDetails_Click"
            Text="Details" ValidationGroup="ProductDetails" CommandArgument='<%# Eval("ProductID") %>'
            SkinID="ProductGridProductDetailButton" /><br />
        <asp:Button runat="server" ID="btnAddToCart" OnCommand="btnAddToCart_Click" Text="Add to cart"
            ValidationGroup="ProductDetails" CommandArgument='<%# Eval("ProductID") %>' SkinID="ProductGridAddToCartButton" />
    </div>
</div>

and the code behind is this:

protected void Page_Load(object sender, EventArgs e)
    {

            // Some code here to generate a random number between 0 & 3
            System.Random RandNum = new System.Random();
            int myInt = RandNum.Next(4);

            if (productPanel.BackImageUrl != null)
            {
                switch (myInt)
                {
                    case 0:
                        productPanel.BackImageUrl = "../App_Themes/emmaharris/images/frame1.gif";
                        break;
                    case 1:
                        productPanel.BackImageUrl = "../App_Themes/emmaharris/images/frame2.gif";
                        break;
                    case 2:
                        productPanel.BackImageUrl = "../App_Themes/emmaharris/images/frame3.gif";
                        break;
                    case 3:
                        productPanel.BackImageUrl = "../App_Themes/emmaharris/images/frame4.gif";
                        break;
                }

            }
           // End of new code to switch background images

    }

T

A: 

Are you sure your page isn't cached? Do a view source on page.

Oh and there should be some function like urand or srand to make random more random.

Pavels
view source shows the 4 panels having the same backgroundimage url. refreshing the page sometimes replaces all 4 images with another set of 4, or sometimes it keeps the same.
Ian Houghton
A: 

I wonder if you have some level of caching operating on the panel which is causing it to not run through the server side processing in production mode.

Michael Gattuso
the server side code must be working as the images do change, although on the production site it changes all 4 panel images. Running in debug mode ensures that each panel gets its own random image....
Ian Houghton
Exactly - it almost sounds like the panel runs through the server-side processing on the first instance and then uses a cached version of the control for iterations 2 - 4. I am wondering if this is due to some production mode optimizations.Metro Smurf had a good question about the random generation - were you able to eliminate that issue? If so, I would look further into any .net caching that might be going on at the page or application level
Michael Gattuso
still looking into the random number code
Ian Houghton
+1  A: 

Sometimes, Random isn't really Random...

Jon Skeet has a good article on the topic: Why am I getting the same numbers out of Random time and time again?

To quote directly what Jon had told me one time:

A pseudo-random number generator (like System.Random) isn't actually random - it will always produce the same sequence of results when initialised with the same data. The data that's used for initialisation is a number called the seed.

The basic problem is that when you create a new instance of Random using the parameterless constructor (as we're doing here) it uses a seed taken from "the current time". The computer's idea of "the current time" may only change once every 15ms (which is an eternity in computing) - so if you create several new instances of Random in quick succession, they will all have the same seed.

What you usually want (assuming you don't care about being able to reproduce exact results, and you don't need a cryptographically secure random number generator) is to have a single Random used throughout your program, initialised the first time it's used. That sounds like you could just use a static field somewhere (exposed as a property) - basically a singleton. Unfortunately System.Random isn't thread-safe - if you call it from two different threads, you could get problems (including getting the same sequence of numbers in both threads).

This is why I created StaticRandom in my little utilities toolbox - it's basically a thread-safe way of getting random numbers, using a single instance of Random and a lock. See http://www.yoda.arachsys.com/csharp/miscutil/usage/staticrandom.html for a quick example, and http://pobox.com/~skeet/csharp/miscutil for the library itself.

Jon Skeet's Misc Utility Random Generator

using System;

namespace MiscUtil
{
    /// <summary>
    /// Thread-safe equivalent of System.Random, using just static methods.
    /// If all you want is a source of random numbers, this is an easy class to
    /// use. If you need to specify your own seeds (eg for reproducible sequences
    /// of numbers), use System.Random.
    /// </summary>
    public static class StaticRandom
    {
     static Random random = new Random();
     static object myLock = new object();

     /// <summary>
     /// Returns a nonnegative random number. 
     /// </summary>  
     /// <returns>A 32-bit signed integer greater than or equal to zero and less than Int32.MaxValue.</returns>
     public static int Next()
     {
      lock (myLock)
      {
       return random.Next();
      }
     }

     /// <summary>
     /// Returns a nonnegative random number less than the specified maximum. 
     /// </summary>
     /// <returns>
     /// A 32-bit signed integer greater than or equal to zero, and less than maxValue; 
     /// that is, the range of return values includes zero but not maxValue.
     /// </returns>
     /// <exception cref="ArgumentOutOfRangeException">maxValue is less than zero.</exception>
     public static int Next(int max)
     {
      lock (myLock)
      {
       return random.Next(max);
      }
     }

     /// <summary>
     /// Returns a random number within a specified range. 
     /// </summary>
     /// <param name="min">The inclusive lower bound of the random number returned. </param>
     /// <param name="max">
     /// The exclusive upper bound of the random number returned. 
     /// maxValue must be greater than or equal to minValue.
     /// </param>
     /// <returns>
     /// A 32-bit signed integer greater than or equal to minValue and less than maxValue;
     /// that is, the range of return values includes minValue but not maxValue.
     /// If minValue equals maxValue, minValue is returned.
     /// </returns>
     /// <exception cref="ArgumentOutOfRangeException">minValue is greater than maxValue.</exception>
     public static int Next(int min, int max)
     {
      lock (myLock)
      {
       return random.Next(min, max);
      }
     }

     /// <summary>
     /// Returns a random number between 0.0 and 1.0.
     /// </summary>
     /// <returns>A double-precision floating point number greater than or equal to 0.0, and less than 1.0.</returns>
     public static double NextDouble()
     {
      lock (myLock)
      {
       return random.NextDouble();
      }
     }

     /// <summary>
     /// Fills the elements of a specified array of bytes with random numbers.
     /// </summary>
     /// <param name="buffer">An array of bytes to contain random numbers.</param>
     /// <exception cref="ArgumentNullException">buffer is a null reference (Nothing in Visual Basic).</exception>
     public static void NextBytes(byte[] buffer)
     {
      lock (myLock)
      {
       random.NextBytes(buffer);
      }
     }
    }
}
Metro Smurf
i think your onto something !! the page quotes "If you use the same seed value twice, you'll get the same sequence of random numbers. Random uses the current time as the seed. The code above creates several instances in very quick succession, and "the current time" tends to have a granularity of at least 10ms, so many instances will share the same seed and thus create the same sequence of numbers."I believe the code is executing that quick its getting the same time code "seed" as the other panels and therefore the same image. I need a more robust random method.
Ian Houghton
@Ian - take a look at Jon's MiscUtility class that has a 'Robust' Random generator. (link in post). I've also edited the post to include his code.
Metro Smurf
@metro smurf, using the miscutility code has solved the problem. Thanks to everyone for your help.
Ian Houghton