Get a string representation of a property in C# at run-time - c#

I have seen the reverse of this question quite a few times, but have not seen how to do what I would like.
Suppose I have the following code:
var myNewData = from t in someOtherData
select new
{
fieldName = t.Whatever,
fieldName2 = t.SomeOtherWhatever
};
If I wish to data bind to this class, my column definition would have to include hard-coded strings like "fieldName" and "fieldName2".
Is there any way to call reflection or something else so that I can do something equivelent to the code below (I know the code below is not valid, but am looking for a valid solution).
string columnName = GetPropertyName(myNewData[0].fieldName);
My goal is that if the variable name changes on the anonymous class, a compile-time error would come up until all references were fixed, unlike the current data binding which relies on strings that are not checked until runtime.
Any help would be appreciated.

string columnName = GetPropertyName(() => myNewData[0].fieldName);
// ...
public static string GetPropertyName<T>(Expression<Func<T>> expr)
{
// error checking etc removed for brevity
MemberExpression body = (MemberExpression)expr.Body;
return body.Member.Name;
}

You get your property names like this:
using System.Reflection;
var myNewData = from t in someOtherData
select new
{
fieldName = t.Whatever,
fieldName2 = t.SomeOtherWhatever
};
foreach (PropertyInfo pinfo in myNewData.FirstOrDefault()
.GetType().GetProperties())
{
string name = pinfo.Name;
}
// or if you need all strings in a list just use:
List<string> propertyNames = myNewData.FirstOrDefault()
.GetType().GetProperties().Select(x => x.Name).ToList();

Related

How to transform string to entity object?

I need to dynamically get an entity object value from string.
something like this :
string s = "UserMaster";
string param = "MyUser";
object o = database.s.Find(param);
//I need o to become like object o = db.UserMaster.Find(MyUser);
Sorry I don't know the name if there's already a function to do this.
Thank you for your guidance :)
Edited :
Ok so here is the bigger picture :
string myString = "Hi my name is [UserMaster.Name] and my school is [SchoolMaster.SchoolName]";
Let's say I got string "[UserMaster.Name]" & "[SchoolMaster.SchoolName]",
UserMaster and SchoolMaster is an entity name.
UserMaster has 'Name' property and SchoolMaster has 'SchoolName' property.
I need to transform "[UserMaster.Name]" to its value, let's say "MyName"
and "SchoolMaster.SchoolName" to "MySchoolName".
You can use Expression to dynamically create code:
static object DynamicallyGet(string name, params object[] key) {
var entityName = Expression.Parameter(typeof(string), "entityName");
var keyValue = Expression.Parameter(typeof(object[]), "keyValue");
var db = Expression.Variable(typeof(RainDB), "database");
IList<Expression> procedures = new List<Expression>();
procedures.Add(Expression.Assign(db, Expression.New(typeof(RainDB))));
var entityType = typeof(RainDB).GetProperty(name);
var callMethod = Expression.Call(Expression.MakeMemberAccess(db, entityType), entityType.PropertyType.GetMethod("Find"), keyValue);
procedures.Add(callMethod);
var body = Expression.Block(new[] { db }, procedures);
var lambda = Expression.Lambda<Func<string, object[], object>>(body, entityName, keyValue).Compile();
return lambda(name , key);
//Call Function:
DynamicallyGet("UserMaster","MyUser")
Dynamically, when you have the DbSet as string (your case):
DbSet mySet = context.Set(Type.GetType("<Your Entity Name>"));
Source:
https://entityframeworkcore.com/knowledge-base/33940507/find-a-generic-dbset-in-a-dbcontext-dynamically
Besides all that, building dynamically a string that has parameters inside is common practice.
It is usually achieved by using curly brackets inside the string, e.g.:
this is in a database column:
"Hello, my name is {User.FirstName}, I come from {User.Country}"
These are usually predefined values and in code you can replace them because you expect them.
Then you do:
var userId = ...get this from session or from whatever you have in your context.
var dbUser = db.User.FirstOrDefault(x => x.UserId == userId);
var template = db.Templates.FirstOrDefault(x => x.TemplateName = "userTemplate");
// template is the Db table and TemplateValue is the column
var text = template.TemplateValue;
text = text.Replace("{User.FirstName}", dbUser.Firstname);
text = text.Replace("{User.Country}", dbUser.Country);
That works, but you have to know beforehand the parameters (they should be predefined).
If you have no idea what parameter should be sent (at least the sender should know that these exist as DB tables/columns), then yeah, it's more tricky and you should go with the dynamic approach that we suggested.
Good luck!

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.

Attempting to obtain properties / fields from an (anonymous class) object linq is creating

I'm having trouble figuring out what I'm doing wrong here. I have some LINQ that returns an IQuery object, and later in the code, I'm attempting to list out the attributes returned. This is best explained by this abbreviated code (the actual LINQ is a lot more complex and involves joins - the LINQ itself works fine):
public IQueryable<Object> FindAll()
{
var query = from user in _db.Users
select new
{
id = user.Id,
is_active = user.IsActive,
email = user.Email,
dob = user.Dob,
user.user_type,
};
return query;
}
Elsewhere in the code I have:
query.ConvertToCsv();
Although I have attempted to insert a .ToList() in that call as well.
The ConvertToCsv has:
public static string ConvertToCSV<TSource>(this IEnumerable<TSource> source)
{
StringBuilder sb = new StringBuilder();
var properties = typeof(TSource).GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
var enumerable = source as IList<TSource> ?? source.ToList();
if (!enumerable.Any()) return "";
string headerString = "";
foreach (var prop in properties)
{
headerString += (headerString.Length > 0 ? "," : "") + prop.Name;
}
sb.AppendLine(headerString);
foreach (TSource item in enumerable)
{
string line = string.Join(",", properties.Select(p => p.GetValue(item).ToCsvValue()).ToArray());
sb.AppendLine(line);
}
return sb.ToString();
}
Note I have also tried to pull out the property names with this code:
PropertyInfo[] pi = typeof(TSource).GetProperties(BindingFlags.Public | BindingFlags.Instance);
var properties = pi.OrderBy(x => x.MetadataToken);
foreach (PropertyInfo p in properties)
{ etc etc }
In all cases, the property or field list returns an empty list, and as such, I can't iterate through the object to spit out a header row or data rows. Tracing through all the code and inspecting the variables indicates that everything is fine until I get to the GetProperties/GetFields line and the code fails.
What rookie mistake am I making? Should I be replacing <Object> with something else?
To pass an anonymous type, or a collection that contains anonymous
types, as an argument to a method, you can declare the parameter as
type object. However, doing this defeats the purpose of strong typing.
If you must store query results or pass them outside the method
boundary, consider using an ordinary named struct or class instead of
an anonymous type.
by Anonymous Types (C# Programming Guide)
Create your own class and change method declaration to be IQueryable<MyClass> instead of object
Did you consider doing something like: db.Users.Select(u => new UserDto() { Id = user.Id, Name = ..., where UserDto is dedicated class that has all the properties you'll need in the future? I think you lose information about properties when you cast from anonymous class to an Object. Although, I never tried to obtain member info from anonymous class

Extension method with dynamic named parameters

I'm currently writing an extension to replace the normal string.Format with my FormatNamed-function.
So far I have got this code, but I want to change the way to input the parameters
void Main()
{
string sql = "SELECT {fields} FROM {table} WHERE {query}"
.FormatNamed(new { fields = "test", table = "testTable", query = "1 = 1" });
Console.WriteLine(sql);
}
public static class StringExtensions
{
public static string FormatNamed(this string formatString, dynamic parameters)
{
var t = parameters.GetType();
var tmpVal = formatString;
foreach(var p in t.GetProperties())
{
tmpVal = tmpVal.Replace("{" + p.Name + "}", p.GetValue(parameters));
}
return tmpVal;
}
}
Not the prettiest of replaces, but it does the job.
Anyway. I want to change so I can execute it with
.FormatName(field: "test", table: "testTable", query: "1 = 1");
Is there any way I can do this? I have tried googling for dynamic named parameters with no good results.
You won't be able to specify an arbitrary number of dynamic, named parameters. that's just not something that C# supports. Your existing code seems okay to me, although I don't see the need for the dynamic parameter. This will work just as well:
public static string FormatNamed(this string formatString, object parameters)
{
var t = parameters.GetType();
var tmpVal = formatString;
foreach(var p in t.GetProperties())
{
tmpVal = tmpVal.Replace("{" + p.Name + "}", p.GetValue(parameters));
}
return tmpVal;
}
And then calling it as:
string sql = "SELECT {fields} FROM {table} WHERE {query}"
.FormatNamed(new { fields = "test", table = "testTable", query = "1 = 1" });
Although I really wouldn't advise using this sort of method for constructing SQL (it won't save you from SQL injection attacks at all), the method itself is sound.
I have tried googling for dynamic named parameters with no good results
That's because the capability does not exist. Think about it - how would the function know what to do if the parameters and their names were not known at compile time? The closest thing I can think of is using params which gives you an array of values, but they must all be the same type, and you can still access them by a given name (and index value).
I'd stick with the method you're currently using:
.FormatName(new {field = "test", table = "testTable", query = "1 = 1"});
That creates an anonymous type with the properties specified, which should work fine with your existing code. Plus it's only a few extra characters to type.
Also note that dynamic doesn't buy you anything here since it's used to access properties directly without using reflection. Since you're using reflection to get the propeties you can just use object.

Dynamic LINQ: Specifying class name in new clause

With Dynamic LINQ, what changes need to be done to have fields of the given class?
For example, how can the following C# query be reproduced in DLinq:
var carsPartial = cars.Select(c => new {c.year, c.name, make = new maker() {name = c.make.name} }).ToList();
I have applied the changes mentioned in this answer https://stackoverflow.com/a/1468357/288747 to allow the return type to be the calling type rather than an anonymous type.
With the class definition is as follows (if it helps):
class car
{
public int vid;
public int odo;
public int year;
public string name;
public maker make;
}
class maker
{
public string name;
public int firstYear;
}
The following doesn't work (but I think is close, but still doesn't work as I don't have the changes necessary to the dynamic linq library, which is what I need):
var carsPartial = cars.Select("new(name, year, new maker() {name = make.name})").ToList();
But it fails at the new maker() { (as expected).
I'm sure I need to change the DynamicLibrary.cs to get this working and could do with some direction on how to alter it to achieve this.
UPDATE: I have turned my answer into a little bit more extensive blog post.
I have not really ever used Dynamic Linq library, but I have taken a look at the DynamicLibrary.cs code and the change to support generating type classes provided in another stackoverflow question you provided link to in your question. Analyzing them all, it seems that the nested new-s should work out of the box in your configuration.
However, it seems your query is not the correct Dynamic Linq's language query. Note, that the query string for DLinq is not equivalent to C# and has its own grammar.
The query should read out, I believe, the following:
var carsPartial = cars.Select("new(name, year, new maker(make.name as name) as make)").ToList();
EDIT:
Rereading this stackoverflow question more carefully, I realizes, that it actually does not extend the Dynamic Linq's language with the possibility for creating new strong-typed classes. They just put the result to the class specified as a generic parameter of Select() instead of specifying it in the query string.
To obtain what you need you will need to revert their changes (get generic DLinq) and apply my changes, I have just verified to work:
Locate the ParseNew method of ExpressionParser class and change it to the following:
Expression ParseNew() {
NextToken();
bool anonymous = true;
Type class_type = null;
if (token.id == TokenId.Identifier)
{
anonymous = false;
StringBuilder full_type_name = new StringBuilder(GetIdentifier());
NextToken();
while (token.id == TokenId.Dot)
{
NextToken();
ValidateToken(TokenId.Identifier, Res.IdentifierExpected);
full_type_name.Append(".");
full_type_name.Append(GetIdentifier());
NextToken();
}
class_type = Type.GetType(full_type_name.ToString(), false);
if (class_type == null)
throw ParseError(Res.TypeNotFound, full_type_name.ToString());
}
ValidateToken(TokenId.OpenParen, Res.OpenParenExpected);
NextToken();
List<DynamicProperty> properties = new List<DynamicProperty>();
List<Expression> expressions = new List<Expression>();
while (true) {
int exprPos = token.pos;
Expression expr = ParseExpression();
string propName;
if (TokenIdentifierIs("as")) {
NextToken();
propName = GetIdentifier();
NextToken();
}
else {
MemberExpression me = expr as MemberExpression;
if (me == null) throw ParseError(exprPos, Res.MissingAsClause);
propName = me.Member.Name;
}
expressions.Add(expr);
properties.Add(new DynamicProperty(propName, expr.Type));
if (token.id != TokenId.Comma) break;
NextToken();
}
ValidateToken(TokenId.CloseParen, Res.CloseParenOrCommaExpected);
NextToken();
Type type = anonymous ? DynamicExpression.CreateClass(properties) : class_type;
MemberBinding[] bindings = new MemberBinding[properties.Count];
for (int i = 0; i < bindings.Length; i++)
bindings[i] = Expression.Bind(type.GetProperty(properties[i].Name), expressions[i]);
return Expression.MemberInit(Expression.New(type), bindings);
}
Then, find the class Res and add the following error message:
public const string TypeNotFound = "Type {0} not found";
Et voilĂ , you will be able to construct queries like:
var carsPartial = cars.Select("new(name, year, (new your_namespace.maker(make.name as name)) as make)").ToList();
Make sure, you include the full type name including the whole namespace+class path.
To explain my change, it just checks if there is some identifier between new and opening parenthesis (see the added "if" at the begging). If so we parse full dot-separated class name and try to get its Type through Type.GetType instead of constructing own class in case of anonymous news.
If I've understood you correctly you want to make a plain anonymous class that contains fields from both class car and class maker. If it's the case you can just provide new names in that class, something like the following:
var carsPartial = cars.Select(c => new { year = c.year, name = c.name, make_name = c.make.name });
Or even provide names only to conflicting fields:
var carsPartial = cars.Select(c => new { c.year, c.name, make_name = c.make.name });

Categories

Resources