Enum Bound to Database - c#

When writing code for existing applications, often times the development database environment does not match the production environment - and even worse, there are times still where overlaying the environments is just not an option.
One idea I had in mind to code for all environments would be to use a databound enum, whose values would be bound to the ID of the data item they represent. I couldn't get that to work with an Enum but I was able to solve it via abstract classes. For example:
public abstract class Colors
{
private static readonly string c_red = "red";
private static readonly string c_blue = "blue";
private static readonly string c_yellow = "yellow";
private static readonly string c_green = "green";
private static int? _red = null;
private static int? _blue = null;
private static int? _yellow = null;
private static int? _green = null;
public static int Red
{
get
{
if (_red == null)
_red = GetColorID(c_red);
return (int)_red;
}
}
public static int Blue
{
get
{
if (_blue == null)
_blue = GetColorID(c_blue);
return (int)_blue;
}
}
public static int Yellow
{
get
{
if (_yellow == null)
_yellow = GetColorID(c_yellow);
return (int)_yellow;
}
}
public static int Green
{
get
{
if (_green == null)
_green = GetColorID(c_green);
return (int)_green;
}
}
private static int GetColorID(string identifier)
{
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Demo"].ConnectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand("spGetColorId", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("Name", identifier);
return Convert.ToInt32(cmd.ExecuteScalar());
}
}
}
}
By doing it this way, I'm able to call Colors.Red in this example to get the ID of Red regardless of if I'm in Dev, Testing, or Production.
My question is: is this really the ideal method of accomplishing this? Is there a native-to-C# method of databinding enums, or something equivalent to what I'm doing above?

Having an enum implies that the values are rarely, if ever, changes. you can think of it as a closed list of values (like days of the week etc.).
Because of this nature of enums, I find it acceptable to have this redundancy of having the enum underlying value being specified twice (once in the DB and the other in the enum itself).
If you are worried about discrepancies, you can run a validation for the values when your application starts and check that the values has the correct corresponding ids and that the number of values in the enum matches the number of values in the DB.

Related

How do I access List<int> value during reflection of a class?

I am fairly new to Reflection, but have been able to retrieve all fields of my passed class. Now I am trying to retrieve the values of each field, but I'm having an issue with List<T>.
I have a simple class for testing:
public class MyTestClass
{
public string Name;
public int Age;
public bool Alive;
public List<int> Counters;
public List<string> People;
public List<Tool> Tools;
public string[] Stuff;
public Tool[] NeededTools;
public MyTestClass(string name, int age, bool alive = true)
{
Name = name;
Age = age;
Alive = alive;
Counters = new List<int>();
Counters.Add(7);
People = new List<string>();
People.Add("Seven");
Tools = new List<Tool>();
Stuff = new string[2];
NeededTools = new Tool[3];
}
}
Here is the code I am using:
private void AttachControl(object source, FieldInfo fi, Control control)
{
switch (fi.FieldType.Name)
{
case "Boolean":
(control.Controls[fi.Name] as ComboBox).SelectedIndex = (fi.GetValue(source).ToString().ToUpper() == "TRUE") ? 1 : 0;
break;
case "List`1":
Control listControl = control.Controls[fi.Name];
var listType = fi.FieldType.GetGenericArguments();
var listFields = listType[0].GetFields(
BindingFlags.Public |
BindingFlags.Instance
);
if (listFields.Length > 0)
{
AttachToControls(listFields, listControl.Controls.Cast<Control>().ToArray());
}
else
{
// *** Here is the issue ***
var values = fi.GetValue(source);
listControl.Controls[fi.Name].Text = values[0].ToString();
}
break;
default:
control.Controls[fi.Name].Text = fi.GetValue(source).ToString();
break;
}
}
When I get to Counters I can retrieve the value var values = fi.GetValue(source); and during debug I can see the List with the value 7 in it, but it states
cannot apply indexing with [] to an expression of type object on the line:
listControl.Controls[fi.Name].Text = values[0].ToString();
I assume I need to cast it, but it will not always be an int type. Do I need to write a section for every type or is there an easier way to accomplish what I need?
FYI - I am writing a Class Library that will allow me to pass any class in and auto create a form to edit all fields.
I'd suggest something along the lines of:
var bob = values as IEnumerable;
listControl.Controls[fi.Name].Text = bob?.Cast<object>()?.FirstOrDefault()?.ToString();
Since the thing you want is a string (not a specific type) then the above code will work fine (assuming values is some form of an enumerable, like a list or an array).
Note, in particular, that IEnumerable interface is this one, not the more commonly used IEnumerable<T>. This allows you to use it without a specific type.

Creating a Session Helper Class that uses an array object C#

I am using a session helper class to track more than several variable. So far I have 30 that are needed from page to page, not all at once of course. I need to convert some of the values from single to array. The Session helper class I use is as follows. For brevity I have shown only two session variables we use for tracking tab index for two accordions.
using System;
using System.Globalization;
using System.Linq;
using System.Web;
public class SessionHelper
{
//Session variable constants
public const string AccordionTop = "#tabTop";
public const string AccordionBot = "#tabBot";
public static T Read<T>(string variable)
{
object value = HttpContext.Current.Session[variable];
if (value == null)
return default(T);
else
return ((T)value);
}
public static void Write(string variable, object value)
{
HttpContext.Current.Session[variable] = value;
}
public static int TabTop
{
get
{
return Read<int>(AccordionTop);
}
set
{
Write(AccordionTop, value);
}
}
public static int TabBot
{
get
{
return Read<int>(AccordionBot);
}
set
{
Write(AccordionBot, value);
}
}
}
So on each page I can work with variables easily as follows:
To Write:
SessionHelper.TabTop = 1; or SessionHelper.TabBot = 3
To Read:
If (SessionHelper.TabTop……….)
This all works fine. I now want to extend this to array values held in session. The array contains int, string and date time value.
For the array session object I have tried adding:
public class SessionHelper
{
public const string CompInfo = "CompAccInfo";
public static T ReadArray<T>(string variable)
{
object[] result = HttpContext.Current.Session[variable] as object[];
if (result == null)
{
return default(T);
//result = new object[30];
}
else
return ((T)(object)result);
}
public static void WriteArray(string variable, object[] value)
{
HttpContext.Current.Session[variable] = value;
}
public static object[] CompDetails
{
get
{
return ReadArray<object[]>(CompInfo);
}
set
{
WriteArray(CompInfo, value);
}
}
}
But then I get an “Object reference not set to…… error when I try to do this:
public void EGetCompanyInformation(MasterPage myMaster, int entityCode)
{
int prevEntity = 0;Using (sqlconnetiooo
.....
//I get values here this works fine
//Then:
sqlr = cmd.ExecuteReader();
sqlr.Read();
if (sqlr.HasRows)
{
//Calculate accounting period adjustment.
yearEndDiff = 12 - Convert.ToInt32(sqlr.GetDateTime(5).Month);
//Company Code.
SessionHelper.CompDetails[0] = sqlr.GetInt32(0);
//Company Name.
SessionHelper.CompDetails[1] = sqlr.GetString(1);
//Currency Unit.
SessionHelper.CompDetails[2] = sqlr.GetString(2);
//Base Currency Code.
SessionHelper.CompDetails[3] = sqlr.GetString(3);
//Reporting Currency Code.
SessionHelper.CompDetails[4] = sqlr.GetString(4);
//Company Year End.
SessionHelper.CompDetails[5] = yearEndDiff;
//Country Code.
SessionHelper.CompDetails[6] = sqlr.GetString(6);
//Country Name.
SessionHelper.CompDetails[7] = sqlr.GetString(7);
//Base Currency Name.
SessionHelper.CompDetails[8] = sqlr.GetString(8);
//Report Currency Name.
SessionHelper.CompDetails[9] = sqlr.GetString(9);
//ClientID.
SessionHelper.CompDetails[10] = sqlr.GetInt32(10);
Other code here
}
}
It seems any SessionHelper.CompDetails[i] does not work : Error Object reference not set to an instance of an object.
What will happen if ReadArray will return default(T)? It will return null. Than access to any object by index inside the array will cause the exception you face.
It is not quite obvious what your code is intended to do.
SessionHelper.CompDetails[0] = sqlr.GetInt32(0);
What do you want here? CompDetails itself should return an array. But you are trying to rewrite it immediately by some values.
If you want to access the CompDetails and rewrite it's objects than you have to instantiate it by
int n = 10;
SessionHelper.CompDetails = new CompDetails[n];
default(object[]) will always throw null. because the array of object is reference type and default value of any reference type is null. So accessing null value will get you Object reference not set to an instance of object.
You can change your old implementation like below:
public static T Read<T>(string variable, int arraySize=10)
{
object value = HttpContext.Current.Session[variable];
if(typeof(T).IsArray && value == null)
{
//array requires size I personally prefer to have
//differnt read method for array.
return ((T)Activator.CreateInstance(typeof(T),arraySize));
}
if(!typeof(T).IsValueType && value == null)
{
//if it is not value type you can return new instance.
return ((T)Activator.CreateInstance(typeof(T)));
}
else if (value == null)
return default(T);
else
return ((T)value);
}
And access SessionHelper as below:
var sessionarray = SessionHelper.Read<object[]>("myarray",15);
....
// then use that sessionarray here.
....
You have to instantiate the CompDetails array before you start assigning values to it.
if (sqlr.HasRows)
{
//Calculate accounting period adjustment.
yearEndDiff = 12 - Convert.ToInt32(sqlr.GetDateTime(5).Month);
// Instantiate array
SessionHelper.CompDetails = new object[11];
//Company Code.
SessionHelper.CompDetails[0] = sqlr.GetInt32(0);
// etc

How to use a getter with a nullable?

I am reading a bunch of queries from a database. I had an issue with the queries not closing, so I added a CommandTimeout. Now, the individual queries read from the config file each time they are run.
How would I make the code cache the int from the config file only once using a static nullable and getter.
I was thinking of doing something along the lines of:
static int? var;
get{ var = null;
if (var.HasValue)
...(i dont know how to complete the rest)
My actual code:
private object QueryMethod(string item)
{
using (SqlConnection connection = new SqlConnection(item))
{
connection.Open();
using (SqlCommand sql = new SqlCommand())
{
AddSQLParms(sql);
sql.CommandTimeout = 30;
sql.CommandText = _cmdText;
sql.Connection = connection;
sql.CommandType = System.Data.CommandType.Text;
sql.ExecuteNonQuery();
}
connection.Close();
}
return false;
}
First: don't call it var !
Let's call it cached_value.
static int? cached_value;
get { return cached_value ?? cached_value = your_logic_here }
This way, the first time you call it, if it's null, it'll initialize the field. Next time you call the getter, you'll just get the value you need.
You could try something like this utilizing the Lazy<T> class:
public static class ConfigCache
{
private static Lazy<int> connectionTimeout =
new Lazy<int>(() => int.Parse(
ConfigurationManager.AppSettings["connectionTimeout"]));
public static int ConnectionTimeout
{
get { return connectionTimeout.Value; }
}
}
Usage:
sqlCmd.CommandTimeout = ConfigCache.ConnectionTimeout;
var is a system keyword - don't use it
V1 - in this version you expect config to have a value, otherwise error will occur
static int? _timeout = null;
private static int GetTimeout()
{
if (_timeout != null) return (int)_timeout;
_timeout = GetTimeoutFromConfig();
return (int)_timeout;
}
V2 - in this version you will use default value if config is empty
static int? _timeout = null;
private const int def_timeout = 120;
private static int GetTimeout()
{
if (_timeout != null) return (int)_timeout;
int? to = GetTimeoutFromConfig();
_timeout = (to ?? def_timeout);
return (int)_timeout;
}
converting from config
private int? GetTimeoutFromConfig()
{
int val;
bool converted = int.TryParse(ConfigurationManager.AppSettings["TimeoutValue"], out val);
return (converted ? val : null);
}
It sounds to be like you're asking how to use a Nullable variable.
static int? val;
get{
if (var.HasValue)
{
return val.Value;
}
else {
val = GetValFromConfig();
return val.Value;
}
}
var is a keyword in C#

Can't add to a list within a class object

First, I'm going to apologize if this is a stupid question. I've been using C# for 16 hours after having not programmed anything since VB6. I'm just trying to hack together a small program for personal use that reads from an old access database and spits out a formatted report in Excel. I apologize for the messy/inefficient code.
Overview: I have two class types, "Zone" and "Device". Each "Zone" has a List of Devices in it. The main program has a List of Zones. Each database has a varying number of "zones" in it, and each "zone" has a varying number of devices assigned to it. I need to parse, sequentially, the zone list and the devices on each zone. I started with structs and arrays and popular opinion seems to be that those are both bad ways to do it, and I wasn't having much luck anyway, so I moved to lists and classes, and it was going well.
I can pull all the "zones" from the database, add them to the list, assign them their labels and IDs. The problem is when I go to read the "devices" from the database, I can't add them to the list within the Zone.
This is the error I get: "Object reference not set to an instance of an object." Which I gather means the object is null?
Here's the relevant code:
Device Class:
public class Device
{
public string Label;
public string Address;
public string Type;
public Device(string Label, string Address, string Type)
{
this.Address = Address;
this.Label = Label;
this.Type = Type;
}
}
Zone Class:
public class Zone
{
public string Label;
public short ID;
public List<Device> Devices;
public Zone(string Label, short ID) {
this.Label = Label;
this.ID = ID;
// ADDED AS PER SUGGESTIONS BELOW
this.Devices = new List<Device>();
}
// Added this to see if it would work, it would not.
public void AddDevice(string Label, string Address, string Type) {
Devices.Add(new Device(Label, Address, Type));
}
}
Initializing and populating Zone List (on button click) (completes successfully)
List<Classes.Zone> Zones = new List<Classes.Zone>();
dbZoneReader = myZoneSelect.ExecuteReader();
while (dbZoneReader.Read())
{
Classes.dbItem dbRow = new Classes.dbItem();
dbRow.Address = Convert.ToInt16(dbZoneReader["DeviceAddress"].ToString());
dbRow.DeviceType = Convert.ToInt16(dbZoneReader["DeviceType"].ToString());
dbRow.Label = dbZoneReader["DeviceLabel"].ToString();
if (dbRow.Label != "" && dbRow.Address > 0)
{
Zones.Add(new Classes.Zone(dbRow.Label,dbRow.Address));
}
}
Adding Devices to their respective Zones:
while (dbReader.Read()) {
Classes.dbItem dbRow = new Classes.dbItem();
string tempZones;
// Acquire/convert device information
dbRow.Node = Convert.ToInt16(dbReader["NodeAddress"].ToString());
dbRow.Loop = Convert.ToInt16(dbReader["LoopSelection"].ToString());
dbRow.Address = Convert.ToInt16(dbReader["DeviceAddress"].ToString());
dbRow.TypeID = Convert.ToInt16(dbReader["TypeID"].ToString());
dbRow.FlashScanID = Convert.ToInt16(dbReader["FlashScanID"].ToString());
dbRow.DeviceType = Convert.ToInt16(dbReader["DeviceType"].ToString());
dbRow.Label = dbReader["DeviceLabel"].ToString();
// Find "proper" zone ID (some zones have multiple IDs, only one is relevant)
tempZones = dbReader["DevicePointMappingList"].ToString();
tempZones = tempZones.Replace("Z", "");
var elements = tempZones.Split(new[] { ',' }, System.StringSplitOptions.RemoveEmptyEntries);
if (elements.Length >= 2) {
ZoneCheck z = new ZoneCheck();
foreach (string items in elements) { if (z.Check(items)) { dbRow.Zone = Convert.ToInt16(items); } }
} else {
if (elements.Length == 1) { dbRow.Zone = Convert.ToInt16(elements[0]); }
else { dbRow.Zone = 0; }
}
// Only add devices that aren't assigned to zone 0, which is non-existent
if (dbRow.Zone > 0) {
// Add new device to zone's device list [THIS IS WHERE IT FAILS]
Zones.Find(z => z.ID == dbRow.Zone).Devices.Add(new Classes.Device("Test", "test", "Test"));
}
}
I've gone through and found out exactly where it fails, and it's the last line where it tries to add the device. Searching here and on google has lead me to believe that I need to initialize the object list... which I believe I've done? I've tried initializing it within the Zone class constructor, and when the Zone is added (which is what it's set too now).
I've confirmed that the Zone object exists, and that the Detectors list within that Zone object isn't null. Kinda stumped, figure I'm doing something that I shouldn't be doing and just don't know better, or I'm missing something really obvious.
The problem is in your Zone class. You need to initialize the List<Device> as follows.
public class Zone
{
public string Label;
public short ID;
public List<Device> Devices;
public Zone(string Label, short ID) {
this.Label = Label;
this.ID = ID;
this.Devices = new List<Device>();
}
// Added this to see if it would work, it would not.
public void AddDevice(string Label, string Address, string Type) {
Devices.Add(new Device(Label, Address, Type));
}
}
The reason is that when you write public List<Device> Devices;, you're not actually creating an object. You're creating a variable that can hold an instance of the specified object. It's only when you pair the variable declaration up with object initialization ( = new List<Device>();) that you get a usable instance of the object.
Thinking of the same issue in terms of a simpler object may help:
public class Foo
{
public string bar; // bar isn't an actual instance of an object, it's just a spot that can hold a string
public void ManipulateBarWithRuntimeError()
{
bar.Substring(0, 1); // "bar" isn't actually set to anything, so how can we take a substring of it? This is going to fail at runtime.
}
public void ManipulateBarWithoutRuntimeError()
{
bar = "Hello, world!";
bar.Substring(0, 1); // bar is actually set to a string object containing some text, so now the Substring method will succeed
}
}
I think the problem is in your Zone class.
Here is my version of your Zone class:
public class Zone
{
public string Label;
public short ID;
public List<Device> Devices;
public Zone(string Label, short ID) {
this.Label = Label;
this.ID = ID;
this.Devices = new List<Device>();
}
// Added this to see if it would work, it would not.
public void AddDevice(string Label, string Address, string Type) {
Devices.Add(new Device(Label, Address, Type));
}
}
This is an only change that I made to your class;
this.Devices = new List<Device>();
Now it might work...
You can also initialize the list inside a getter
public class Zone
{
public string Label;
public short ID;
private List<Device> _devices;
public List<Device> Devices
{
get
{
return this._devices ?? (this._devices = new List<Device>());
}
}
public Zone(string Label, short ID)
{
this.Label = Label;
this.ID = ID;
}
// Added this to see if it would work, it would not.
public void AddDevice(string Label, string Address, string Type)
{
Devices.Add(new Device(Label, Address, Type));
}
}

How do I retrieve data from a list<> containing class objects

I want to retrieve data from a list I created that contains class objects via a foreach but I'm not able to.
Can somebody please tell me what's missing in my code?
I have a class Recipes.cs that contains the following code:
public class Recipe
{
string _oveskrift;
int _recipe_id;
string _opskrift;
int _kcal;
public Recipe(string overskrift, int recipe_id, string opskrift,int kcal)
{
_oveskrift = overskrift;
_recipe_id = recipe_id;
_opskrift = opskrift;
_kcal = kcal;
}
}
public class Recipes
{
public List<Recipe> CreateRecipeList()
{
Recipe opskrift1 = new Recipe("Cornflakes med Chili",1,"4 kg cornflakes bages", 420);
Recipe opskrift2 = new Recipe("Oksemørbrad",2,"Oksemørbrad steges i baconfedt", 680);
Recipe opskrift3 = new Recipe("Tun i vand",3,"Dåsen åbnes og tunen spises", 120);
List<Recipe> Recipelist = new List<Recipe>();
Recipelist.Add(opskrift1);
Recipelist.Add(opskrift2);
Recipelist.Add(opskrift3);
return Recipelist;
}
}
I call CreateRecipeList() from another class calculator.cs and the code looks like this:
private int FindRecipes()
{
List<Recipe> Rlist = new List<Recipe>();
// CREATE THE CLASS AND ADD DATA TO THE LIST
Recipes r = new Recipes();
Rlist = r.CreateRecipeList();
int test = 0; // used only for test purposes
foreach(var rec in Rlist)
{
rec.????
test++;
}
return test;
}
I would presume that I should be able to dot my way into rec."the class object name"."the value"
But nothing happens!.
All I get is the option to rec.Equals, rec.GetHashcod ect. which is clearly wrong.
For the record I have also tried:
foreach(Recipe rec in Rlist)
{
rec.????
test++;
}
But that doesn't work either.
The Int test are only there for test purposes.. and it return 3.. so the list does contain the correct information.
Please show us the code for the Recipe class. Besides that, you're most of the way there...
foreach(Recipe rec in Rlist)
{
string str = rec.<PropertyName>;
}
You need to set the proper access modifiers for the members in your Recipe class.
public : Access is not restricted.
protected : Access is limited to the containing class or types derived from the containing class.
Internal : Access is limited to the current assembly.
protected internal: Access is limited to the current assembly or types derived from the containing class.
private : Access is limited to the containing type.
By default, the members of your Recipe class will have the private access modifier.
string _oveskrift;
int _recipe_id;
string _opskrift;
int _kcal;
is:
private string _oveskrift;
private int _recipe_id;
private string _opskrift;
private int _kcal;
Maybe you want to modify your member access as follows, in order to set the values of the members only inside the class code. Any attempt to set their values outside the Recipe class will fail, as the set is private. The get remains public, which makes the value available for reading.
public class Recipe
{
string _oveskrift;
int _recipe_id;
string _opskrift;
int _kcal;
public string Oveskrift
{
get
{
return _oveskrift;
}
private set
{
_oveskrift=value;
}
}
public int RecipeId
{
get
{
return _recipe_id;
}
private set
{
_recipe_id = value;
}
}
public string Opskrift
{
get
{
return _opskrift;
}
private set
{
_opskrift = value;
}
}
public int Kcal
{
get
{
return _kcal;
}
private set
{
_kcal = value;
}
}
public Recipe(string overskrift, int recipe_id, string opskrift, int kcal)
{
_oveskrift = overskrift;
_recipe_id = recipe_id;
_opskrift = opskrift;
_kcal = kcal;
}
}
Also, please read as soon as possible the following MSDN article: Capitalization Conventions. And also, this one: C# Coding Conventions (C# Programming Guide).

Categories