LINQ Deffered Execution Subtlety - c#

All, I have recently localised the entire aplication and I am faced with the following problem;I have the following LINQ query in my application
var ccsum = from a in dtSumGL2.AsEnumerable()
group Math.Abs(a.Field<double>(strpcPSPassBegCost)) by new
{
BaseCC = a.Field<string>(strpcBaseCC)
}
into g
select new
{
g.Key.BaseCC,
PSPassBegCost = g.Sum()
};
This is creating a new object ccsum which we use to create a DataTable and subsequently populate an SQL Server database.
The problem is that each of the new items being created in the DataTable with column names BaseCC and PSPassBegCost, but these names do not matched the German version of these names. Now for my question: is there a way to do something like:
var ccsum = from a in dtSumGL2.AsEnumerable()
group Math.Abs(a.Field<double>(strpcPSPassBegCost)) by new
{
BaseCC = a.Field<string>(strpcBaseCC)
}
into g
select new
{
g.Key.BaseCC as Resources.BaseCC,
PSPassBegCost = g.Sum() as Resources.PSPassBegCost
};
so that I can name the tables according to their localised names?
Edit. The code that retrieve a DataTable from ccsum is
fooDt = Utils.LINQToDataTable(ccsum);
and
public static DataTable LINQToDataTable<T>(IEnumerable<T> varlist)
{
DataTable dtReturn = new DataTable();
PropertyInfo[] oProps = null;
if (varlist == null)
return dtReturn;
foreach (T rec in varlist)
{
// Use reflection to get property names, to create
// table, Only first time, others will follow.
if (oProps == null)
{
oProps = ((Type)rec.GetType()).GetProperties();
foreach (PropertyInfo pi in oProps)
{
Type colType = pi.PropertyType;
if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition() == typeof(Nullable<>)))
{
colType = colType.GetGenericArguments()[0];
}
dtReturn.Columns.Add(new DataColumn(pi.Name, colType));
}
}
DataRow dr = dtReturn.NewRow();
foreach (PropertyInfo pi in oProps)
{
pi.GetValue(rec, null) == null ? DBNull.Value : pi.GetValue(rec, null);
}
dtReturn.Rows.Add(dr);
}
return dtReturn;
}
Thanks for your time.
Another approach might be to rename the 'columns' in the ccsum object as a post-processing step although the values of sumcc are not populated until they are requested at run-time - any other ideas?

Create class or enum which will map column indexes to some readable names:
public static class SumGLColumn
{
public const int BaseCC = 0;
public const int PSPassBegCost = 1;
}
And use column indexes instead of column names to query your datatable:
var ccsum = from a in dtSumGL2.AsEnumerable()
group Math.Abs(a.Field<double>(SumGLColumn.PSPassBegCost))
by a.Field<string>(SumGLColumn.BaseCC) into g
select new {
BaseCCg = g.Key,
PSPassBegCost = g.Sum()
};

Attempting to localize components of your system for greater human comprehension is a laudable goal, but it will give you challenges in using the the vast majority of tools and libraries: generally database tooling expects these things to be constant and ignorant of localization.
If you wish your database tables to be easier to understand, perhaps a more practical solution would be to produce localized views instead? The views could live in a de schema and be one-to-one translations of your tables. This would allow you to leverage a lot of the standard tooling, keeping your system in a consistent "neutral" culture internally (whatever your development culture is) and providing translations over the top of these wherever required.
I think trying to embed this kind of localization into the heart of your system is likely to not be worth the cost of working around the expectations of most developers and toolsets and you're better providing a façade.

This is not possible. In the select statement you define an anonymous type. This is not a language feature, but a compiler-feature, which means that the compiler creates a class for this type with the properties you define.
This means that the compiler must know the names at compile time. If you want something more dynamic, I recommend you to use a dictionary:
select new Dictionary<string, object>
{
{ Resources.BaseCC, g.Key.BaseCC },
{ Resources.PSPassBegCost , g.Sum() }
};

Related

C#: Mapping a database to class properties when DB names and column names will only be known at run time

I currently have a program which interacts with and queries data from a database. However, I want to figure out how I could possibly take a database and a config file and map the table contents to my existing contents.
For example:
I have the class Person. A person has a ID(long), accountName(string), Title(string), firstName(string) and lastName(string).
The new connected database has the same properties for Person but stores them in a different way. They might call the table Individual, with columns UniqueID(long), accName(string), personalTitle(string), fName(string), lName(string).
I know from the config file the names of each column that are the equivalents to the properties, but I'll only be able to read that at run time. Is there a way I could read the titles of what I am looking for and load them into these classes so that I don't have to hard code it every time?
Thanks in advance!
The best thought that comes to my mind is skipping ORM of any kind and just using query strings:
Assuming we are using just plain ADO this way
your query string would look something like:
string queryString =
$"SELECT {ConfigurationManager.AppSettings[productColumn]}
, {ConfigurationManager.AppSettings[PriceColumn]}
, {ConfigurationManager.AppSettings[productNameColumn]}
from dbo.{ConfigurationManager.AppSettings[ProductTableName]} "
+ "WHERE {ConfigurationManager.AppSettings[UnitPriceColumn]} > #pricePoint "
+ "ORDER BY {ConfigurationManager.AppSettings[UnitPriceColumn]} DESC;";
It feels sort of weird, but so are the requirements.
Is it really always random new databases or is it a finite amount ?
You could have a class which includes all of those possible case but will check "on the fly" which one should be used or not.
I haven't tryed that with any ORM, but I remember the "bool ShouldSerializeXXXXXX" prefix when dealing with XML serialization/deserialization, maybe there is an equivalent for your case.
This is pretty easy to implement. When your procedure returns a datatable, you read the mapping from the configuration file and set the value for the class property based on what field name is mapped to the property using reflection.
Here is some code that you can put to work.
/// Your result objects list
List<DBObject> result = new List<DBObject>();
System.Reflection.ConstructorInfo ci = objectType.GetConstructor(new Type[] { });
System.Reflection.PropertyInfo[] props = objectType.GetProperties();
if ((table != null) && (table.Rows.Count > 0) && (ci != null) && (props != null) && (props.Length > 0))
{
for (int i = 0; i < table.Rows.Count; i++)
{
DBObject dob = (DBObject)ci.Invoke(new object[] { });
if (dob != null)
{
foreach (System.Reflection.PropertyInfo pi in props)
{
/// This is your method to get DB column name from config file
string dbColumnName = GetDBColumnFromMappingConfigFile(pi.Name);
object value = null;
if (table.Columns.Contains(dbColumnName))
{
if (table.Rows[i][dbColumnName] != DBNull.Value)
{
value = table.Rows[i][dbColumnName];
}
}
if (value != null)
{
if ((pi.PropertyType.IsGenericType) && (pi.PropertyType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))))
{
Type t = pi.PropertyType.GetGenericArguments()[0];
if (t != null)
{
object val = Convert.ChangeType(value, t);
pi.SetValue(dob, val, null);
}
}
else
pi.SetValue(dob, Convert.ChangeType(value, pi.PropertyType), null);
}
}
}
}
}
I usually use PropertyAttributes instead of Property names, because attributes allow you to define default values in case if DB has it NULL, define setters, etc.
A couple of ways this could be done, you could do this either application, database, or a combination
Application Side
Hard-code your program classes and build your application on that.
Build the queries to populate the classes based on known queries.
Build a configuration section in the app for selecting which query for which class
Use that configuration to populate your model
Database Side is a little more complicated, but ends up being cleaner in the application. You still create the classes as normal.
On the DB server, there are a multitude of options.
You still are creating your classes and queries in the application
You would do all of your CRUD ops in Stored Procedures, which have the ability to determine if a table exists or not; and then you would simply go through a stack of IF Exists (select tablename...) to determine which query to run
You have a "setup" stored procedure which converts the tables present into a structure that aligns with your apps classes
and many other options

Choose type in foreach loop

in my program, I use 4 stored procedures, depending on fields that were selected in the form.
ctx = new DataClassesDataContext();
items = (from prjs in ctx.sp_Select_Stuknummers(prjnr) select prjs).ToList();
or
ctx = new DataClassesDataContext();
items = (from prjs in ctx.sp_Select_Stuknummers_Scanner(prjnr, scanner) select prjs).ToList();
and so on ...
I use LINQ to SQL, and for each of these 4 queries, I have a different result class:
sp_Select_StuknummersResult
sp_Select_Stuknummers_ScannerResult
sp_Select_Stuknummers_Scanner_WPSResult
sp_Select_Stuknummers_StuknummerResult
they all have the same fields and definitions.
Now, when I iterate the result:
foreach (sp_Select_StuknummersResult x in items)
{
WPSitems ding = new WPSitems();
ding.ID = x.ID;
ding.Naam = x.Naam;
......
}
I need to pass the type to use (in this example: sp_Select_StuknummersResult )
Is there a way to either
A. easily convert to a new type, so that one can be used every time
or
B. dynamically set the type in the foreach loop ?
maybe there is even a C that I am not aware of...
Any help is highly appreciated !
By default, L2S auto-generates a separate type for each individual stored procedure. However, you can easily change that return type in the Linq-2-Sql Designer.
In the designer, click on the stored procedure. In the Properties window, there's an entry 'Return Type': change it from 'Auto-generated type' to the type you want.
If the stored procedure returns rows from an already mapped table, select that type in the drop-down. If not, you can manually add a class to the designer and configure that type to be returned from the stored procedure.
You can do this using generics/reflection.
public static WPSitems MapObjectWithIdenticalProperties<T>(T itemOfUnknownType) where T : new()
{
var inputType = typeof(T);
var outputType = typeof(WPSitems);
var outputItem = new WPSitems();
foreach (var inputProperty in inputType.GetProperties())
{
var matchingOutputProperty = outputType.GetProperties().FirstOrDefault(x => x.Name == inputProperty.Name);
if(matchingOutputProperty != null)
matchingOutputProperty.SetValue(outputItem, inputProperty.GetValue(itemOfUnknownType));
}
return outputItem;
}
The function above can be called like this:
var items = GetYourDataThatCanBeDifferentTypes();
var mappedItems = new List<WPSitems>();
foreach(var item in items)
mappedItems.add(MapObjectWithIdenticalProperties(item));
If I understand your problem correctly this might help you out:
1.) Create an interface for the result classes (e.g.) I_SP:
sp_Select_StuknummersResult
sp_Select_Stuknummers_ScannerResult
sp_Select_Stuknummers_Scanner_WPSResult
sp_Select_Stuknummers_StuknummerResult
add common variables and methods that you need into that interface.
2.) Make sure your items is List<I_SP>
3.) Create a generic method for your foreach loop
public void MyMethod<T>(List<I_SP> items) where T:I_SP {
foreach (var x in items.OfType<T>)
{
WPSitems ding = new WPSitems();
ding.ID = x.ID;
ding.Naam = x.Naam;
......
}
}
4.) Then just call this like this:
MyMethod<*sp_Select_StuknummersResult result class*>(items);
Hope it's clear enough.

Get and save enum using reflection

I have a project in which I have some assemblies which implement an abstract class.
Each assembly has a public enum called ResultEnum.
This ResultEnum's value is stored in a database as an int.
I have another web project which displays some info, and I want it to also display this int's string representation - the name of the corresponding value from the ResultEnum.
What I want to do is, using MEF, load all the relevant assemblies (no problem here), search for this enum using reflection (no problem here also) and then to store the enum in some way, and cache it in order to avoid all this process the next time I want to convert the int from the database to the string representation (and the other way around if necessary) since I have several thousands of records in my db table.
AggregateCatalog catalog = new AggregateCatalog();
catalog.Catalogs.Add(new DirectoryCatalog(path));
_container = new CompositionContainer(catalog);
try
{
_container.ComposeParts(this);
}
catch (CompositionException compositionException)
{
Console.WriteLine(compositionException.ToString());
}
foreach (var task in myTasks)
{
TaskAbstract instance = (TaskAbstract)task.CreateExport().Value;
MemberInfo[] infos = instance.GetType().GetMembers(BindingFlags.Public | BindingFlags.Static);
foreach (MemberInfo member in infos.Where(x => x.Name.Equals("ResultEnum")))
{
Console.WriteLine(member);
}
}
What do you suggest the next move should be?
How should I store/cache it?
Thanks
In addition to #Thomas's answer:
As using reflection you can get exact int value from a property, the name for that concrete value could be gotten using the next expression:
var enumValueName = Enum.GetName(member.GetType(), member.GetValue(instance));
UPD
I really missed that you reflect MemberInfos. To apply my solution you can update you reflection this way:
foreach (var task in myTasks)
{
TaskAbstract instance = (TaskAbstract)task.CreateExport().Value;
// Reflect properties, not all members
PropertyInfo[] infos = instance.GetType().GetProperties(BindingFlags.Public | BindingFlags.Static);
foreach (PropertyInfo prop in infos.Where(x => x.Name.Equals("ResultEnum")))
{
var enumValueName = Enum.GetName(prop.GetType(), prop.GetValue(instance));
}
}
Or you could cast MemberInfo to PropertyInfo.
One approach to solving this problem is to consider using a subclassable enums technique (also sometimes referred to as a polymorphic enum).
I wrote a couple of generic classes specifically to support these kinds of types which you can read about here. Also, a proposal has been submitted to the Roslyn compiler team on Github to add support for these types of enums to C#.
Here is an example of a set of subclassable enums that have two underlying types, string and integer, using the classes from my project:
public sealed class Status : StringIntegerEnum<Status>
{
public static readonly Status Active = new Status("active", 1);
public static readonly Status Inactive = new Status("inactive", 0);
private Status(string status, int statusCode) : base(status, statusCode) {}
}
Note that the string value is not the same as the constant name itself, which allows you to have underlying string values with characters that violate the normal naming conventions in C#.
The StringIntegerEnum<tStringIntegerEnum> base class provides .AllValues, .AllNaturalValues and .AllStringValues static methods that you can use to enumerate the list of enum values or both types of their underlying values.
From your comment:
I agree, But question is how do I iterate over the values and names of the enumerator
I assume you mean "enumeration", not "enumerator". You can use the Enum.GetValues method:
var valuesToNames =
Enum.GetValues(enumType)
.Cast<object>()
.ToDictionary(o => (int)o, o => Enum.GetName(enumType, o));
And, is there a better solution than a dictionary
Better how? I think a dictionary is a fine solution; is there any reason why you would want something else?
This is how i've written the code eventually:
resultEnumsForTasks = new Dictionary<string, Dictionary<UInt16, string>>();
foreach (var task in myTasks)
{
Dictionary<UInt16, string> _enum = new Dictionary<UInt16,string>();
TaskAbstract instance = (TaskAbstract)task.CreateExport().Value;
MemberInfo resultEnum = instance.GetType().GetMember("ResultEnum").FirstOrDefault();
if (resultEnum == null)
continue;
string[] names = Enum.GetNames(resultEnum as Type);
IList<int> vals = (IList<int>)Enum.GetValues(resultEnum as Type);
for (int i = 0; i < names.Length; i++)
{
_enum.Add(Convert.ToUInt16(vals[i]), names[i]);
}
resultEnumsForTasks.Add(instance.GetType().Name, _enum);
}
It's very similar to #n.turakulov 's solution, however his solution didn't work for me since I got an empty list of PropertyInfo for some reason...
Thanks for everyone who assisted!

Datatable to List and vice versa

I created an extension method to convert a datatable to list and list to datatable. I have multiple problems with this. can someone help me fix the issues please:
Both datatable columns and the generic class property names needs to be the same even and case sensitive. I need to modify this to handle the situation where case is not taken into consideration ex: EmployeeName = employeename.
If the generic class has a complex type as a property then my function does not seem to work. ex: if i have Public string EmployeeName {get; set;} my code works, but if i have Public Department DepartmentDetails {get; set;} ( know this one is a bit tricky but if someone can give me a suggestion how to handle this at all, i shall be glad.)
Please find below my Extension method.
public static List<T> ToList<T>(this DataTable table) where T : new()
{
try
{
var dataList = new List<T>();
const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic;
var propertyList = (from PropertyInfo property in typeof(T).GetProperties(flags)
select new
{
Name = property.Name,
Type = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType
}).ToList();
var dataTableFieldNames = (from DataColumn columnHeader in table.Columns
select new { Name = columnHeader.ColumnName, Type = columnHeader.DataType }).ToList();
var commonProperties = propertyList.Intersect(dataTableFieldNames).ToList();
foreach (DataRow dataRow in table.AsEnumerable().ToList())
{
var templateType = new T();
foreach (var field in commonProperties)
{
PropertyInfo propertyInfos = templateType.GetType().GetProperty(field.Name);
propertyInfos.SetValue(templateType, dataRow[field.Name], null);
}
dataList.Add(templateType);
}
return dataList;
}
catch (Exception ex)
{
throw;
}
}
Any help is highly appreciated. Cheers!!!
Both datatable columns and the generic class property names needs to be the same even and case sensitive. I need to modify this to handle the situation where case is not taken into consideration ex: EmployeeName = employeename.
That part is easy. You can compare the Name's case-insensitively:
var commonProperties = propertyList
.Where(p => dataTableFieldNames
.Any(d => string.Equals(d.Name, p.Name, StringComparison.OrdinalIgnoreCase) &&
d.Type == p.Type).ToList();
If the generic class has a complex type as a property then my function does not seem to work.
Now that is a bit hard and depends on what would you like to do in that situation? Does your DataTable also contains columns that belongs the other types (e.g. Departmant) ? If so you will need to determine which types of properties you have (additionally,other than the built-in types) and which additional columns exists in your DataTable for those types.Then you can get the properties of each type and map them to the columns in your DataTable.

How to determine database table field and get the value with Entity Framework

I'm looking for a method to getdatabase table's field with variable thing.
I wrote a stupid and unworking method to explain what I need:
using (var dbContext = new db_ReadyEngine_MSSQL())
{
string nameOfField = "UserName";
var table = dbContext.tbl_User;
foreach (var x in table)
{
string fieldValue = x.nameOfField;
}
}
Here, I'm trying to determining column name which it nameOfField...
You may call data from DataTable by using name of column, as example:
Object o = dataTable.Rows[0][nameOfField];
try this:
List<string>values = new List<string>();
using (var dbContext = new db_ReadyEngine_MSSQL())
{
values = (from s in dbContext.tbl_User select s.Username).ToList();
}
return values
Assuming I am reading your question correctly, you want to get the value of a column, whose name is only known at runtime?
If so, have a look at the code below. It will pull the properties from the object type, search for the one that matches the nameOfField value, and then pull attempt to pull a value from it.
foreach (var x in table)
{
var fieldValue = x.GetType().GetProperties().Where(a => a.Name == nameOfField).Select(p => p.GetValue(x, null)).FirstOrDefault();
}
U can use Reflection to get value of Property using its String Name
using (var dbContext = new db_ReadyEngine_MSSQL())
{
string nameOfField = "UserName";
var table = dbContext.tbl_User;
foreach (var x in table)
{
string fieldValue = typeof(x).GetProperty(nameOfField ).GetValue(x, null) as string;
}
}
You can use Entity SQL for this without typing the query itself:
IEnumerable<object> GetFieldValues<T>(DbContext context, string fieldName)
where T : class
{
var oc = ((IObjectContextAdapter)context).ObjectContext;
ObjectQuery<T> q = oc.CreateObjectSet<T>();
return q.Select("it." + fieldName)
.AsEnumerable()
.Select(x => x[0]);
}
The trick is that an ObjectSet (the predecessor, sort of, or DbSet) can easily be cast to an ObjectQuery, which is the base of Entity SQL. By default, the command text uses "it" as alias for the table in the query, so if you want the value of one specific field, you must prefix it by the alias, and there you go.
The Select returns a DbDataRecord. The first value from this record is returned.
The advantage of this method over others is that it only queries the requested field from the database.
Of course, if you know the type of the field in question up front, you can make a strong-typed version of this method.

Categories

Resources