There are tables with over 50 columns, I am using following code to loop through the dataReader for column existence.
If HasColumn(reader, "EmpCode") Then obj.OwningOfficeID = CType(reader("EmpCode"), Int32)
Protected Function HasColumn(ByRef reader As SqlDataReader, ByVal columnName As String) As Boolean
For i As Integer = 0 To reader.FieldCount - 1
If reader.GetName(i).Equals(columnName) Then
Return Not IsDBNull(reader(columnName))
End If
Next
Return False
End Function
I am wondering if there is any better way of checking the columns in the DataReader instead of looping through the DataReader each time i bind the object property?
SqlDataReader.GetSchemaTable Method will give the DataTable of the executed query, from there you can get all the columns
Returns a DataTable that describes the column metadata.
MSDN http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader.getschematable.aspx
currently I am using this extension method for this purpose
public static bool TryGetOrdinal(this IDataRecord dr, string column, out int ordinal)
{
try
{
ordinal = dr.GetOrdinal(column);
}
catch(Exception ex)
{
ordinal = -1; //Just setting a value that GetOrdinal doesn't return
return false;
}
return true;
}
so you use it as follows
int ordinal = 0;
if(dr.TryGetOrdinal("column",out ordinal))
string val = dr.GetValue(ordinal).ToString();
My VB is not so good. Sorry couldn't convert it to VB for you. But I think you get the basic idea and can recode it in VB.
Related
In a db table I have a string, such as...
Var1=0;CosType=1;DefaultType=US_Pass;DateYear=1;DateRange=1;ReportFormat=0
I want to create a VB.NET function that has 1 input var, the string (above) and the "token" to get the value for. (The return value is the value of the token.) For example, if I call it (LongString is the string above)....
txtValue.text = MyFunction(LongString,"DefaultType")
So, "US_Pass" would be returned.
What is the most efficient way to code MyFunction?
I've tried something like this...
return LongString.Substring(LongString.IndexOf(input_token) + 12)
I feel I'm close, but so far away.
Thanks!
This works as long as you know the key exists in your string:
public string MyFunction(string longString, string key)
{
return
longString
.Split(';')
.Select(x => x.Split('='))
.ToDictionary(x => x[0], x => x[1])[key];
}
With this code:
string longString = "Var1=0;CosType=1;DefaultType=US_Pass;DateYear=1;DateRange=1;ReportFormat=0";
Console.WriteLine(MyFunction(longString, "DefaultType"));
I get:
US_Pass
As VB.NET:
Public Function MyFunction(longString As String, key As String) As String
Return longString.Split(";"c).Select(Function(x) x.Split("="c)).ToDictionary(Function(x) x(0), Function(x) x(1))(key)
End Function
Split the string into parts at the semi-colons.
Dim parts As String() = LongString.Split(";")
Loop over the parts in a ForEach loop.
Find the part that StartsWith the the token value.
Find the equal sign (IndexOf) and take everything to the right of it (Substring).
That should give you enough to figure it out.
It's probably not a great idea to store data like this in your database. Hopefully you won't need to query these attributes from SQL.
In your case I would create a class to encapsulate the attributes. You pass in the string as a constructor parameter and let the class manage it.
Here's an example in C# that shouldn't be too hard to convert to VB:
public class AttributeCollection
{
private readonly Dictionary<string, string> _attrs;
public AttributeCollection(string values)
{
_attrs = (from v in values.Split(new[] {';'})
select v.Split(new[] {'='})).ToDictionary(i => i[0], i => i[1]);
}
public string this[string name]
{
get { return _attrs[name]; }
set { _attrs[name] = value; }
}
public override string ToString()
{
return string.Join(";", (from a in _attrs select a.Key + "=" + a.Value).ToArray());
}
}
I need to compare a current List of Objects from a DB against a new List of Objects. I want to compare them and highlight for the user those which have changed (in this case, return TRUE that they are different).
Since some of my objects are Nullable, this involves a lot of IF NOT IS Nothing on the side of the NewObj and the CurrentObj...I've trying to find a more efficient way of writing the below as I have to use it to compare about 30 objects of different types, IE Date, Decimal, Int, etc..
The below works until say either of the Obj has no Rank and is therefore Nothing
Suggestions?
Dim Rank As Boolean = CompareData(NewObj, CurrentObj, "Rank")
Dim Regiment As Boolean = CompareData(NewObj, CurrentObj, "Rank")
Dim DateofBirth As Boolean = CompareData(NewObj, CurrentObj, "DoB")
Private Function CompareData(NewObj As Business.Casualty, CurrentObj As Business.Casualty, FieldToComapre As String) As Boolean
Select Case FieldToComapre
Case "DateOfBirth"
Return (Nullable.Equals(NewCasualty.DateOfBirth, CurrentCasualty.DateOfBirth))
Case "Age"
Return (Nullable.Equals(NewCasualty.Age, CurrentCasualty.Age))
Case "Rank"
Return (Nullable.Equals(NewCasualty.Rank.ID, CurrentCasualty.Rank.ID))
Case "Regiment"
Return (Nullable.Equals(NewCasualty.Regiment.ID, CurrentCasualty.Regiment.ID))
Case Else
Return True
End Select
End Function
My first suggestion is to change the name of your function to something like AreFieldValuesTheSame. My second suggestion is to negate the purpose of the function. In other words, return True when the values are the same.
If you follow my first two suggestions, then for each one of your cases, instead of simply
Return (Nullable.Equals(a.ID, b.ID))
you need something more like
Return ((a Is Nothing And b Is Nothing) Or (a.ID Is Nothing And b.ID Is Nothing) Or (Nullable.Equals(a.ID, b.ID)))
For example, a represents NewCasualty.Rank and b represents CurrentCasualty.Rank. You need to check for the object being Nothing (null in other words) before you try to check properties of the object.
Also, at the beginning of the function, you need to check whether NewObj Is Nothing and whether CurrentObj Is Nothing:
If (NewObj Is Nothing) And (CurrentObj Is Nothing) Then
Return True
Else If (NewObj Is Nothing) Or (CurrentObj Is Nothing)
Return False
Please forgive me if my VB syntax is not perfect. (I'm much more fluent in C#.)
I actually ended up just going with an Extension method..it was simpler and cleaner IMO.
Dim Rank As Boolean = CompareData(NewObj, CurrentObj, "Rank")
Dim Regiment As Boolean = CompareData(NewObj, CurrentObj, "Regiment")
Dim Trade As Boolean = CompareData(NewObj, CurrentObj, "Trade")
Private Function CompareData(NewObj As Business.Casualty, CurrentObj As Business.Casualty, FieldToComapre As String) As Boolean
Select Case FieldToComapre
Case "Trade"
Return NewCasualty.Trade.NullableEquals(CurrentCasualty.Trade)
Case "Rank"
Return NewCasualty.Rank.NullableEquals(CurrentCasualty.Rank)
Case "Regiment"
Return NewCasualty.Regiment.NullableEquals(CurrentCasualty.Regiment)
Case Else
Return True
End Select
End Function
public static class NullableCompare
{
public static bool NullableEquals<T>(this T s1, T s2)
where T : class
{
if (s1 == null)
{
return s2 == null;
}
return s1.Equals(s2);
}
}
public partial class Rank
{
public override bool Equals(object obj)
{
var p2 = obj as Rank;
if (p2 == null)
{
return false;
}
if (this.ID != p2.ID)
{
return false;
}
return this.ID == p2.ID;
}
public override int GetHashCode()
{
return this.ID.GetHashCode();
}
}
I'm using an asp:ObjectDatasource to handle an insert (done through Linq to SQL) to my SQL database. The insert function returns an integer.
My problem is that how could I get the returned value of the function. Should I manually call the insert function through code behind or could I get it from the asp:ObjectDataSource as a key/value pair like how I send data through the asp:ObjectDataSource for the insert function to use?
Here's the insert function code:
public static int InsertSpotlightList(string spotlight_list_name)
{
ALI_DBDataContext ctx = new ALI_DBDataContext();
DAL.Plugin_Spotlight_List temp_spotlight_list = new Plugin_Spotlight_List();
temp_spotlight_list.Spotlight_List_Name = spotlight_list_name;
temp_spotlight_list.Spotlight_List_IsDeleted = false;
temp_spotlight_list.Spotlight_List_ForMall = false;
try {
ctx.Plugin_Spotlight_Lists.InsertOnSubmit(temp_spotlight_list);
ctx.SubmitChanges();
}
catch(Exception ex) { }
return temp_spotlight_list.Spotlight_List_Id;
}
Found the answer, you need to use: the OnInserted event of the asp:ObjectDataSource.
Here's an example:
protected void ObjDS_OnInserted(object sender, ObjectDataSourceStatusEventArgs e)
{
var ret_val = e.ReturnValue;
}
Sorry for the trouble. Should've researched more until I really can't think of a solution.
{
--
public static IEnumerable<Datarow> Codes(string topvalue)
{
DataTable itemCodes = new DataTable();
itemCodes.Columns.Add("itemId");
itemCodes.Columns.Add("itemCode");
itemCodes.Rows.Add(0, firstCallingCode);
DataTable Codes = GetAllItems().Tables[0];
foreach (DataRow item in Codes.Rows)
{
if (item["ItemCode"] != DBNull.Value)
{
itemCodes.Rows.Add(item.Field<int?>("itemId"), item.Field<string>("itemCode"));
}
}
return itemCodes.AsEnumerable();d
}
how can i bind it to dropdownlist: i tried this
ddcodes.datasource = codes.getenumerable();
ddcodes.databind();
when i do this i get error about typecast. i can not solve it tried a lot please help.
my method is actually this
public static IEnumerable"Datarow" Codes(string topvalue)
dont know why editor took that datarow off. bracket and datarow.
You just need to pass in the return value from the Codes method.
ddcodes.datasource = Codes();
ddcodes.databind();
You don't need to "get" an enumerable. The Codes method is already returning one.
I'm trying to create an export Excel/CSV function that will iterate through a custom object and first output the property names and then output the values. I want to use reflection only where necessary so I'm attempting to save the property names when I output the headers and then reuse them to print the values.
Is this possible? I'm a little weary of using reflection in a loop but is there a better way?
Psuedo Code:
Dim Cust1 = New Customer("Tom", "123 Main Street")
Dim Cust2 = New Customer("Mike", "456 Main Street")
Dim Cust3 = New Customer("Joe", "789 Main Street")
Dim CustList As New Arraylist()
CustList.Add(Cust1)
CustList.Add(Cust2)
CustList.Add(Cust3)
CSVExport(CustList, New Customer())
Function CSVExport(List As ArrayList, CustomObject as Object) As StringWriter
Dim sw as Stringwriter
dim proplist as arraylist
'output header
Foreach CustProperty as System.Reflection.PropertyInfo CustomObject.GetType().GetProperties()
proplist.add(CustProperty.Name)
sw.write(CustProperty + ",")
EndFor
'output body
'??
'?? Here I'd like to loop through PropList and List instead of using reflection
'??
Return Sw
End Function
Its all reflection regardless of whether or not you have the names stored in a list.
Do you have a degree of control over the CustomObject. You could store the info within the CustomObject and query that info instead without using reflection. For instance, this is the code I use for my basic domain objects.
public class DomainObject
{
private HashTable _values = new HashTable();
public HashTable Properties
{
get
{
return _values;
}
}
protected void SetValue<T>(string property, T value)
{
if (_values.ContainsKey(property))
{
_values[property] = value;
}
else
{
_values.Add(property, value);
}
}
protected T GetValue<T>(string property)
{
if (_values.ContainsKey(property))
{
return (T)_values[property];
}
else
{
return default(T);
}
}
}
public class TootsieRoll : DomainObject
{
public string Size
{
get { return GetValue<string>("Size"); }
set { SetValue<string>("Size",value); }
}
public string Flavor
{
get { return GetValue<string>("Flavor"); }
set { SetVlaue<string>("Flavor", value); }
}
public int Ounces
{
get { return GetValue<int>("Ounces"); }
set { SetValue<int>("Ounces", value); }
}
}
Now your CSV code would only need to access and loop through the Key=>Value pairs within the "Properties" HashTable it inherited from the DomainObject to get the names and values. But obviously this only works if you have a level of control over your objects necessry to make them inherit from the DomainObject, and it wouldnt involve 30 years of drugery to rewrite all your property accessors. If that is the case, then reflection is your way to go.
In your Pseudo Code you're already populating an arraylist using reflection. If all you want to do is loop through the ArrayList, you can have a look at the ArrayList Class MSDN entry. It shows how to implement IEnumerable to iterate your array list, e.g:
Dim obj As [Object]
For Each obj In CType(myList, IENumberable)
Console.Write(" : {0}", obj)
Next obj
That's untested as is, I'm not sure if it should be CType(myList, IENumberable) or DirectCast(myList, IENumberable).
There is another option, using Object Serialization in VB.Net, a road far less traveled (at least around our offices).