Link to home
Start Free TrialLog in
Avatar of woodje
woodjeFlag for United States of America

asked on

Error using readonly static classes in C# to connect to database

Hello Experts,

I am trying to setup in connection to my SQL2005 database. The error I am getting is "A static readonly field cannot be assigned to (except in a static constructor or a variable initializer)" I have tried to research this but all I am finding is an examples of the differences between static and const. I have included the part of my code that is getting errors.

Jeff
// Store the name of your application
    private static readonly string siteName;
    // Caches the connection string
    private static readonly string dbConnectionString;
    // Caches the data provider name 
    private static readonly string dbProviderName;    
    
    static void fudconfig()
    {        
        dbConnectionString = ConfigurationManager.ConnectionStrings["fss_cao_SqlServer"].ConnectionString;
        dbProviderName =  ConfigurationManager.ConnectionStrings["fss_cao_SqlServer"].ConnectionString;
        siteName = ConfigurationManager.AppSettings["SiteName"];
    }

    // Returns the connection string for the database
    public static string DbConnectionString
    {
        get
        {
            return dbConnectionString;
        }
    }

    // Returns the data provider name
    public static string DbProviderName
    {
        get
        {
            return dbProviderName;
        }
    }

Open in new window

Avatar of ToddBeaulieu
ToddBeaulieu
Flag of United States of America image

Is "static void fudconfig()" your static constructor? Hard to tell from the snippet.

That looks right, but if it's not the constructor, then it won't work.

A static constructor is just like a non-static, but with the modifier. In fact, you can have both at the same time.
Avatar of woodje

ASKER

Todd,

Sorry for the confusion. I guess I should explain more. I am using three classes. One that has the connection information, the second with all the query constructs and the third has all the data for all of the select, insert, and delete functions. I am enclosing the second class this is the constructor as you call it. I hope this helps.


using System;
using System.Data;
using System.Data.Common;
using System.Configuration;

/// <summary>
/// Class contains generic data access functionality to be accessed from 
/// the business tier
/// </summary>
public static class genericdataaccess
{
  // static constructor 
  static genericdataaccess()
  {
    //
    // TODO: Add constructor logic here
    //
  }

  // execute a command and return the results as a DataTable object
  public static DataTable ExecuteSelectCommand(DbCommand command)
  {
    // The DataTable to be returned 
    DataTable table;
    // Execute the command making sure the connection gets closed in the end
    try
    {
      // Open the data connection 
      command.Connection.Open();
      // Execute the command and save the results in a DataTable
      DbDataReader reader = command.ExecuteReader();
      table = new DataTable();
      table.Load(reader);
      // Close the reader 
      reader.Close();
    }
    catch (Exception ex)
    {
      Utilities.LogError(ex);
      throw ex;
    }
    finally
    {
      // Close the connection
      command.Connection.Close();
    }
    return table;
  }

  // execute an update, delete, or insert command 
  // and return the number of affected rows
  public static int ExecuteNonQuery(DbCommand command)
  {
    // The number of affected rows 
    int affectedRows = -1;
    // Execute the command making sure the connection gets closed in the end
    try
    {
      // Open the connection of the command
      command.Connection.Open();
      // Execute the command and get the number of affected rows
      affectedRows = command.ExecuteNonQuery();
    }
    catch (Exception ex)
    {
      // Log eventual errors and rethrow them
      Utilities.LogError(ex);
      throw ex;
    }
    finally
    {
      // Close the connection
      command.Connection.Close();
    }
    // return the number of affected rows
    return affectedRows;
  }

  // execute a select command and return a single result as a string
  public static string ExecuteScalar(DbCommand command)
  {
    // The value to be returned 
    string value = "";
    // Execute the command making sure the connection gets closed in the end
    try
    {
      // Open the connection of the command
      command.Connection.Open();
      // Execute the command and get the number of affected rows
      value = command.ExecuteScalar().ToString();
    }
    catch (Exception ex)
    {
      // Log eventual errors and rethrow them
      Utilities.LogError(ex);
      throw ex;
    }
    finally
    {
      // Close the connection
      command.Connection.Close();
    }
    // return the result
    return value;
  }

  // creates and prepares a new DbCommand object on a new connection
  public static DbCommand CreateCommand()
  {
    // Obtain the database provider name
    string dataProviderName = Utilities.DbProviderName;
    // Obtain the database connection string
    string connectionString = Utilities.DbConnectionString;
    // Create a new data provider factory
    DbProviderFactory factory = DbProviderFactories.GetFactory(dataProviderName);
    // Obtain a database specific connection object
    DbConnection conn = factory.CreateConnection();
    // Set the connection string
    conn.ConnectionString = connectionString;
    // Create a database specific command object
    DbCommand comm = conn.CreateCommand();
    // Set the command type to stored procedure
    comm.CommandType = CommandType.StoredProcedure;
    // Return the initialized command object
    return comm;
  }
}

Open in new window

Hmmm. I still don't understand. This class has no static fields and hence no assignments that could fail.

The first example shows static fields and assignments from a static method (which looks fine), but because they're readonly, that method has to be the static constructor for the class. Because you didn't show the entire class, especially the name of it, I can't tell if "fudconfig" is the constructor. That would require the first class to be called "fudconfig". Is it?

If the first class is the problem post the whole thing and tell me exactly which line you're getting the exception.

I'm guessing it's the dbConnectionString assignment below?

    static void fudconfig()
    {        
        dbConnectionString = ConfigurationManager.ConnectionStrings["fss_cao_SqlServer"].ConnectionString;

One final note ... why do you need this field to be readonly? If it's private to the class, code outside the class can't access it anyway.
Avatar of woodje

ASKER

Thanks Todd,

Here is the full class.


using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Net.Mail;

/// <summary>
/// Class contains miscellaneous functionality 
/// </summary>
/// 
    
public static class Utilities
{
    // Store the name of your application
    private static readonly string siteName;
    // Caches the connection string
    private static readonly string dbConnectionString;
    // Caches the data provider name 
    private static readonly string dbProviderName;

    // Initialize various properties in the constructor
    static void fudconfig()
    {        
        dbConnectionString = ConfigurationManager.ConnectionStrings["fss_cao_SqlServer"].ConnectionString;
        dbProviderName =  ConfigurationManager.ConnectionStrings["fss_cao_SqlServer"].ConnectionString;
        siteName = ConfigurationManager.AppSettings["SiteName"];
    }

    // Returns the connection string for the database
    public static string DbConnectionString
    {
        get
        {
            return dbConnectionString;
        }
    }

    // Returns the data provider name
    public static string DbProviderName
    {
        get
        {
            return dbProviderName;
        }
    }

    // Send error log emails?
    public static bool EnableErrorLogEmail
    {
        get
        {
            return bool.Parse(ConfigurationManager.AppSettings["EnableErrorLogEmail"]);
        }
    }

    // Returns the email address where to send error reports
    public static string ErrorLogEmail
    {
        get
        {
            return ConfigurationManager.AppSettings["ErrorLogEmail"];
        }
    }

    // Returns the length of product descriptions in products lists
    public static string SiteName
    {
        get
        {
            return siteName;
        }
    }    

    // Generic method for sending emails
    public static void SendMail(string from, string to, string subject, string body)
    {
        // Configure mail client (may need additional
        // code for authenticated SMTP servers)
        SmtpClient mailClient = new SmtpClient(ConfigurationManager.AppSettings["SMTP"],25);
        // Create the mail message
        MailMessage mailMessage = new MailMessage(from, to, subject, body);
        /*
            // For SMTP servers that require authentication
            message.Fields.Add("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate", 1);
            message.Fields.Add("http://schemas.microsoft.com/cdo/configuration/sendusername", "SmtpHostUserName");
            message.Fields.Add("http://schemas.microsoft.com/cdo/configuration/sendpassword", "SmtpHostPassword");
        */
        // Send mail
        mailClient.Send(mailMessage); 
    }

    // Send error log mail
    public static void LogError(Exception ex)
    {
        // get the current date and time
        string dateTime = DateTime.Now.ToLongDateString() + ", at "
                    + DateTime.Now.ToShortTimeString();
        // stores the error message
        string errorMessage = "Exception generated on " + dateTime;
        // obtain the page that generated the error
        System.Web.HttpContext context = System.Web.HttpContext.Current;
        errorMessage += "\n\n Page location: " + context.Request.RawUrl; 
        // build the error message
        errorMessage += "\n\n Message: " + ex.Message; 
        errorMessage += "\n\n Source: " + ex.Source;
        errorMessage += "\n\n Method: " + ex.TargetSite; 
        errorMessage += "\n\n Stack Trace: \n\n" + ex.StackTrace; 
        // send error email in case the option is activated in Web.Config
        if (Utilities.EnableErrorLogEmail)
        {
            string from = "noreply@verizonwireless.com";
            string to = Utilities.ErrorLogEmail;
            string subject = Utilities.SiteName + " error report";
            string body = errorMessage;
            SendMail(from, to, subject, body);      
        }
    }

      // Configures what button to be clicked when the uses presses Enter in a 
      // textbox. The text box doesn't have to be a TextBox control, but it must 
      // be derived from either HtmlControl or WebControl, and the HTML control it 
      // generates should accept an 'onkeydown' attribute. The HTML generated by 
      // the button must support the 'Click' event
      public static void TieButton(Page page, Control TextBoxToTie, Control ButtonToTie)
      {
        // Init jscript
        string jsString = "";

        // Check button type and get required jscript
        if (ButtonToTie is LinkButton)
        {
          jsString = "if ((event.which && event.which == 13) || (event.keyCode && event.keyCode == 13)) {"
              + page.ClientScript.GetPostBackEventReference(ButtonToTie, "").Replace(":", "$") + ";return false;} else return true;";
        }
        else if (ButtonToTie is ImageButton)
        {
          jsString = "if ((event.which && event.which == 13) || (event.keyCode && event.keyCode == 13)) {"
              + page.ClientScript.GetPostBackEventReference(ButtonToTie, "").Replace(":", "$") + ";return false;} else return true;";
        }
        else
        {
          jsString = "if ((event.which && event.which == 13) || (event.keyCode && event.keyCode == 13)) {document."
              + "forms[0].elements['" + ButtonToTie.UniqueID.Replace(":", "_") + "'].click();return false;} else return true; ";
        }

        // Attach jscript to the onkeydown attribute - we have to cater for HtmlControl or WebControl
        if (TextBoxToTie is HtmlControl)
        {
          ((HtmlControl)TextBoxToTie).Attributes.Add("onkeydown", jsString);
        }
        else if (TextBoxToTie is WebControl)
        {
          ((WebControl)TextBoxToTie).Attributes.Add("onkeydown", jsString);
        }
      }
}

Open in new window

Avatar of woodje

ASKER

No it is called Utilities.
Avatar of woodje

ASKER

Todd,

You are correct that line of code is where I am getting the error. The "dbConnectionString" is what is highlighted. To the question of why is it static. That is the way the book I was using had these classes built. I don't understand it all 100% yet. So goes the learning.
ASKER CERTIFIED SOLUTION
Avatar of ToddBeaulieu
ToddBeaulieu
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I'm not asking why it's static, but rather readonly. These are different concepts. Keep reading about this stuff and it will start to make sense.

readonly - once the field is assigned a value (constructor or inline initializer) it can't be changed. Personally, I don't use this very often at all.

static, it's created ONCE for the application, regardless of how many instances of the class you create. If you only have a single instance of this class .. you can just NOT MESS with static and be happy as a lark.
Avatar of woodje

ASKER

That worked perfectly. I was trying to rename to the application functions and injected this problem. Thanks for the lesson I will keep learning.