EF ToTraceString SQL generation result columns order - c#

My question is if I can predict or choose the exact order of the columns the sql generation through ToTraceString() returns.
I use ToTraceString() for an IQueryable to get the resulted SQL command and then insert the results directly in a database table.
So, I kind of need the generated SQL to be consistent with my table structure...
string insertQuery = string.Format("INSERT INTO {0} {1}", sqlTableName ((System.Data.Objects.ObjectQuery<TRow>)results).ToTraceString());
Context.ExecuteStoreCommand(string.Format("TRUNCATE TABLE {0}", sqlTableName));
Context.ExecuteStoreCommand(insertQuery);
results = IQueryable<Row> where Row is a type with the same properties as the table columns
I choose to do a direct insert into a table because i see no point in getting a ToList() enumerable on the webserver, just to send it back to SQL through some kind of bulk insert (which EF doesn;t support for the moment...) My query returns a considerable amount of rows and I do not want to use Stored Procedures.
Hope I make sense...thanks

I had this issue but the answer here still required a fair amount of work to get going. I used the part of How does Entity Framework manage mapping query result to anonymous type? to get the order and returned the names, then a simple parsing to extract the field names.
I made an extension method that pulls everything together:
public static string ToWrappedString(this ObjectQuery query, out ObjectParameterCollection parameters)
{
var trace = query.ToTraceString();
parameters = query.Parameters;
var positions = query.GetPropertyPositions();
// the query should be SELECT\n
// Column AS NNN
// FROM
// so we regex this out
var regex = new Regex("^SELECT(?<columns>.*?)FROM", RegexOptions.Multiline);
var result = regex.Match(trace.Replace(Environment.NewLine, ""));
var cols = result.Groups["columns"];
// then we have the columns so split to get each
const string As = " AS ";
var colNames = cols.Value.Split(',').Select(a => a.Substring(a.IndexOf(As, StringComparison.InvariantCulture) + As.Length)).ToArray();
var wrapped = "SELECT " + String.Join(Environment.NewLine + ", ", colNames.Select((a, i) => string.Format("{0}{1} [{2}]", a, As, positions[i]))) + " FROM (" + trace
+ ") WrappedQuery ";
return wrapped;
}
This is the code from the other link, updated to EF6 internals and returning the name in column order rather than the indexes.
public static string[] GetPropertyPositions(this ObjectQuery query)
{
// get private ObjectQueryState ObjectQuery._state;
// of actual type internal class
// System.Data.Objects.ELinq.ELinqQueryState
object queryState = GetProperty(query, "QueryState");
AssertNonNullAndOfType(queryState, "System.Data.Entity.Core.Objects.ELinq.ELinqQueryState");
// get protected ObjectQueryExecutionPlan ObjectQueryState._cachedPlan;
// of actual type internal sealed class
// System.Data.Objects.Internal.ObjectQueryExecutionPlan
object plan = GetField(queryState, "_cachedPlan");
AssertNonNullAndOfType(plan, "System.Data.Entity.Core.Objects.Internal.ObjectQueryExecutionPlan");
// get internal readonly DbCommandDefinition ObjectQueryExecutionPlan.CommandDefinition;
// of actual type internal sealed class
// System.Data.EntityClient.EntityCommandDefinition
object commandDefinition = GetField(plan, "CommandDefinition");
AssertNonNullAndOfType(commandDefinition, "System.Data.Entity.Core.EntityClient.Internal.EntityCommandDefinition");
// get private readonly IColumnMapGenerator EntityCommandDefinition._columnMapGenerator;
// of actual type private sealed class
// System.Data.EntityClient.EntityCommandDefinition.ConstantColumnMapGenerator
var columnMapGeneratorArray = GetField(commandDefinition, "_columnMapGenerators") as object[];
AssertNonNullAndOfType(columnMapGeneratorArray, "System.Data.Entity.Core.EntityClient.Internal.EntityCommandDefinition+IColumnMapGenerator[]");
var columnMapGenerator = columnMapGeneratorArray[0];
// get private readonly ColumnMap ConstantColumnMapGenerator._columnMap;
// of actual type internal class
// System.Data.Query.InternalTrees.SimpleCollectionColumnMap
object columnMap = GetField(columnMapGenerator, "_columnMap");
AssertNonNullAndOfType(columnMap, "System.Data.Entity.Core.Query.InternalTrees.SimpleCollectionColumnMap");
// get internal ColumnMap CollectionColumnMap.Element;
// of actual type internal class
// System.Data.Query.InternalTrees.RecordColumnMap
object columnMapElement = GetProperty(columnMap, "Element");
AssertNonNullAndOfType(columnMapElement, "System.Data.Entity.Core.Query.InternalTrees.RecordColumnMap");
// get internal ColumnMap[] StructuredColumnMap.Properties;
// array of internal abstract class
// System.Data.Query.InternalTrees.ColumnMap
Array columnMapProperties = GetProperty(columnMapElement, "Properties") as Array;
AssertNonNullAndOfType(columnMapProperties, "System.Data.Entity.Core.Query.InternalTrees.ColumnMap[]");
int n = columnMapProperties.Length;
string[] propertyPositions = new string[n];
for (int i = 0; i < n; ++i)
{
// get value at index i in array
// of actual type internal class
// System.Data.Query.InternalTrees.ScalarColumnMap
object column = columnMapProperties.GetValue(i);
AssertNonNullAndOfType(column, "System.Data.Entity.Core.Query.InternalTrees.ScalarColumnMap");
string colName = (string)GetProperty(column, "Name");
// can be used for more advanced bingings
// get internal int ScalarColumnMap.ColumnPos;
object columnPositionOfAProperty = GetProperty(column, "ColumnPos");
AssertNonNullAndOfType(columnPositionOfAProperty, "System.Int32");
propertyPositions[(int)columnPositionOfAProperty] = colName;
}
return propertyPositions;
}
static object GetProperty(object obj, string propName)
{
PropertyInfo prop = obj.GetType().GetProperty(propName, BindingFlags.NonPublic | BindingFlags.Instance);
if (prop == null) throw EFChangedException();
return prop.GetValue(obj, new object[0]);
}
static object GetField(object obj, string fieldName)
{
FieldInfo field = obj.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
if (field == null) throw EFChangedException();
return field.GetValue(obj);
}
static void AssertNonNullAndOfType(object obj, string fullName)
{
if (obj == null) throw EFChangedException();
string typeFullName = obj.GetType().FullName;
if (typeFullName != fullName) throw EFChangedException();
}
static InvalidOperationException EFChangedException()
{
return new InvalidOperationException("Entity Framework internals has changed, please review and fix reflection code");
}

The accepted answer to this question contains two links which describe how to figure out what order the various properties of the entity type appear in the SQL generated by ToTraceString(). With that information, you can do some simple parsing/restructuring of the raw SQL to replace the weird column names EF uses (e. g. C1, C2, etc) with the property column names. Then, you could wrap the generated SQL in a subquery that selects the relevant columns in the order you want:
SELECT prop1, prop2
FROM
(
// the result of ToTraceString(), with EF's generated column names replaced by the property names of the query type
) x

Related

Generic query in EntityFramework with 'where' statement

I need to create a generic method to fetch some data related to another entity.
This is how my code looks like at the moment:
public static void InvalidateData<TEntity>(string foreignKey, int valueFK)
{
try
{
var key = typeof(TEntity).Name;
var adapter = (IObjectContextAdapter)EntityModelDataProvider.Database;
var objectContext = adapter.ObjectContext;
var container = objectContext.MetadataWorkspace.GetEntityContainer(
objectContext.DefaultContainerName, System.Data.Entity.Core.Metadata.Edm.DataSpace.CSpace);
var name = container.BaseEntitySets.Where((s) => s.ElementType.Name.Equals(key)).FirstOrDefault().Name;
string sqlQuery = String.Format(#"SELECT VALUE entity FROM [VW_{0}] WHERE entity.{1} = #foreignkey", name, foreignKey);
var query = objectContext.CreateQuery<TEntity>(sqlQuery, new ObjectParameter("foreignkey", valueFK));
var tmpResult = query.ToList();
}
catch (Exception)
{
throw;
}
}
And the error:
'entity.Average_FK' could not be resolved in the current scope or context. Make sure that all referenced variables are in scope, that required schemas are loaded, and that namespaces are referenced correctly. Near member access expression, line 1, column 58.
Generated query:
SELECT VALUE entity FROM [VW_Average_Client] WHERE entity.Average_FK = #foreignkey
I checked the Properties of the Entity and I do have one called "Average_FK".
Any idea how to achieve my goal?
UPDATE
I am trying to implement a generic where like this:
Expression<Func<TEntity, bool>> customFilter = entity => ComparePropertyValue<TEntity>(entity, foreignKey, valueFK);
var query = objectContext.CreateQuery<TEntity>("[" + name + "]").Where(customFilter);
....
private static bool ComparePropertyValue<TEntity>(TEntity entity, string property, object value)
{
try
{
PropertyInfo propertyInfo = typeof(TEntity).GetProperty(property);
return propertyInfo.GetValue(entity) == value;
}
catch (Exception)
{
throw;
}
}
And this exception:
LINQ to Entities does not recognize the method 'Boolean ComparePropertyValue[VW_Average_Client](XXX.EntityModel.VW_Average_Client, System.String, System.Object)' method, and this method cannot be translated into a store expression.
Finally I found a solution.
(In case someone wants to post a better/cleaner answer I will set yours as the valid one)
public static void InvalidateDataGeneric<TEntity>(string foreignKey, int valueFK)
{
try
{
var key = typeof(TEntity).Name;
var adapter = (IObjectContextAdapter)EntityModelDataProvider.Database;
var objectContext = adapter.ObjectContext;
var container = objectContext.MetadataWorkspace.GetEntityContainer(
objectContext.DefaultContainerName, System.Data.Entity.Core.Metadata.Edm.DataSpace.CSpace);
var name = container.BaseEntitySets.Where((s) => s.ElementType.Name.Equals(key)).FirstOrDefault().Name;
Func<TEntity, bool> filter = algo => ComparePropertyValue<TEntity>(algo, foreignKey, valueFK);
var query = objectContext.CreateQuery<TEntity>("[" + name + "]").Where(filter);
foreach (var element in query)
{
// whatever
}
objectContext.SaveChanges();
}
catch (Exception)
{
throw;
}
}
private static bool ComparePropertyValue<TEntity>(TEntity entity, string property, object value)
{
try
{
PropertyInfo propertyInfo = typeof(TEntity).GetProperty(property);
return propertyInfo.GetValue(entity).Equals(value);
}
catch (Exception)
{
throw;
}
}
The place where entity is used is wrong, it should be:
string sqlQuery = String.Format(#"SELECT VALUE FROM [VW_{0}] entity WHERE entity.{1} = #foreignkey", name, foreignKey);
or even omit entity:
string sqlQuery = String.Format(#"SELECT VALUE FROM [VW_{0}] WHERE {1} = #foreignkey", name, foreignKey);
(note - I agree with the comment above that for several reasons this is not a recommended approach; there are better & cleaner solutions to achieve the same. But that would not answer the question...)

Get List of Objects Added to Entity Framework 6 Include List

Background
I have created object graphs in Entity Framework where any given object A will have a table Ac that tracks changes for it. These objects may also connect to each other, such as A being 1-many to B. Here is an example graph:
A -> Ac
/ \
Bc <- B \
/ \
Cc <- C D -> Dc
I want to be able to load an object and specific connected objects at a point in time by using the change tables to pull those records and apply them. Ideally, I'd like to be able to either use or mimic the .Include function from Entity Framework.
The Issue
Pulling out which objects are already included in an IQueryable is not as easy as I guessed it would be. Looking at an IQueryable<T> with a child object of T Include()-ed, I can see that these relationships are stored in some sort of Span object within an Arguments property - but these are both internal classes and trying to retrieve this information has a lot of steps.
Here is what I have so far:
public static void LoadVersion<T>( this IQueryable<T> query, DateTime targetDateTime )
{
//grab the value of the "Arguments" property on query.Expression
//this has to be done through reflection because "Arguments" is not accessible otherwise
PropertyInfo argumentsPropertyInfo = query.Expression.GetType().GetProperties().FirstOrDefault( x => x.Name == "Arguments" );
dynamic argumentsPropertyValue = argumentsPropertyInfo.GetValue( query.Expression );
for (int i = 0; i < argumentsPropertyValue.Count; i++)
{
//This gets me a System.Data.Entity.Core.Objects.Span, but that class is internal
//In the watch, I can see span -> SpanList[0].Navigations[0] gives me the name of the class in the .Include()
// This is the value I need
dynamic span = argumentsPropertyValue[i].Value;
//So if I try to pull it out using the same reflection trick as before, I get
// a dynamic {System.Reflection.PropertyInfo[0]} (not a list, as you would normally expect),
// and accessing those values & methods makes the debugger exit without an exception
dynamic spanPropertyInfo = argumentsPropertyValue[i].Value.GetType().GetProperties();
//this makes the debugger exit without an exception
dynamic spanPropertyValue = spanPropertyInfo[0].GetValue(span);
//this also makes the debugger exit without an exception (with the above line commented out, of course)
dynamic spanPropertyValue2 = spanPropertyInfo.GetValue( span );
}
}
Based on how difficult it is for me to find what is Included in a Query, I can't help but think that I am doing this entirely the wrong way. Digging through some of the Entity Framework 6.1.3 source code hasn't shed much light on this.
Edit
I've been playing around with the code provided by Alex Derck, but I realized I still need a few pieces to make this work the way I want.
Here is the version of VisitMethodCall I implemented:
protected override Expression VisitMethodCall( MethodCallExpression node )
{
if (node.Method.Name != "Include" && node.Method.Name != "IncludeSpan") return base.VisitMethodCall(node);
try
{
string includedObjectName = (string) node.Arguments.First().GetPrivatePropertyValue( "Value" );
if (includedObjectName != null)
{
_includes.Add(includedObjectName);
}
}
catch (Exception e ){ }
return base.VisitMethodCall( node );
}
I'm able to construct a query with includes and get the names of the objects I included using the IncludeVisitor, but the main goal to use these was to be able to find the related tables and add them to the include.
So when I have the equivalent of this:
var query = ctx.Persons.Include(p => p.Parents).Include(p => p.Children);
// includes[0] = "Parents"
// includes[1] = "Children"
var includes = IncludeVisitor.GetIncludes(query.Expression);
I am successfully grabbing the includes, and I can then find the related tables (Parents -> ParentsChanges, Children -> ChildrenChanges), but I'm not 100% sure how to add these back to the include.
The main problem here is when it's a nested statement:
context.A.Include(x => x.B).Include(x => x.C).Include(x => x.B.Select(y => y.D))
I can successfully traverse that whole graph and get the names of A, B, C, and D, but I need to be able to add a statement like this back to the include:
[...].Include(x => x.B.Select(y => y.D.Select(z => z.DChanges)))
I can find DChanges just fine, but I don't know how to build that include back up because I don't know how many steps are between DChanges and the original item (A).
After looking a bit in the source code of Entity Framework I noticed the includes are not part of the Expression, but rather part of the IQueryable. If you think about it, it's pretty obvious it should be that way. Expressions can't actually execute code themselves, they are translated by a provider (which is also part of the IQueryable), and not all providers should know how to translate an Include method. In the source code you can see the IQueryable.Include method calls the following small method:
public ObjectQuery<T> Include(string path)
{
Check.NotEmpty(path, "path");
return new ObjectQuery<T>(QueryState.Include(this, path));
}
The query (casted to an ObjectQuery) simply gets returned and only it's internal QueryState gets changed, nothing at all happens to the expression. In the debugger you can see the EntitySets that will be included if you look in the IQueryable, but I haven't been able to put them into a list (_cachedPlan is always null when I try to access it through reflection).
I think after seeing this, the thing you're trying to do is not possible, so I would keep a static list of strings in my dbContext and implement a custom Include extension method:
public partial class TestDB
{
public static ICollection<Expression> Includes { get; set; } = new List<Expression>();
public TestDB() : base()
{
Includes = new List<Expression>();
}
...
}
public static class EntityExtensions
{
public static IQueryable<T> CustomInclude<T, TProperty>(this IQueryable<T> query,
Expression<Func<T,TProperty>> include) where T : class
{
TestDB.Includes.Add(include);
return query.Include(include);
}
}
You could also 'override' the normal Include method from System.Data.Entity.
I say 'override', because technically it's not really possible to override an extension method, but you can just create an extension method called Include with the same parameters yourself, and if you don't include System.Data.Entity where you use it, there's no ambiguity between your own method and the one from System.Data.Entity:
public static class EntityExtensions
{
public static IQueryable<T> Include<T, TProperty>(this IQueryable<T> query,
Expression<Func<T,TProperty>> include) where T : class
{
TestDB.Includes.Add(include);
var method = typeof(QueryableExtensions)
.GetMethods(BindingFlags.Public | BindingFlags.Static)
.Where(m => m.Name == "Include")
.First(m => m.GetParameters().All(p => p.ParameterType.IsGenericType));
var generic = method.MakeGenericMethod(typeof(T), typeof(TProperty));
return (IQueryable<T>)generic.Invoke(query, new object[] { query, include });
}
}
I write here another answer to your question (but not the solution you need).
You can retrieve the objects added to entity framework 6 include list from an already retrieved entity in the same way the entity proxy does.
The property to retrieve if a property should be lazy loaded on access (so not already loaded and not in include list) is Relationship.IsLoaded. You can find the list of relationships in YourEntityWithProxy._entityWrapper.Relationships.
_entityWrapper and other properties are private so you need to use reflection to read them.
With the help of Alex, I was able to get what I wanted.
First, to get the name of the includes, I used a small variation of one of the earlier versions of the answer Alex posted:
internal static class IncludeVisitorExtensions
{
public static object GetPrivatePropertyValue( this object obj, string propName )
{
PropertyInfo propertyInfo = obj.GetType().GetProperty( propName, BindingFlags.Public
| BindingFlags.NonPublic | BindingFlags.Instance );
return propertyInfo.GetValue( obj, null );
}
public static object GetPrivateFieldValue( this object obj, string fieldName )
{
FieldInfo fieldInfo = obj.GetType().GetField( fieldName, BindingFlags.Public
| BindingFlags.NonPublic | BindingFlags.Instance );
return fieldInfo?.GetValue( obj );
}
}
internal class IncludeVisitor : ExpressionVisitor
{
private static readonly IncludeVisitor Visitor;
private static List<string> _includes;
private IncludeVisitor() { }
static IncludeVisitor()
{
Visitor = new IncludeVisitor();
}
public static ICollection<string> GetIncludes( Expression expr )
{
_includes = new List<string>();
Visitor.Visit( expr );
return _includes;
}
protected override Expression VisitMethodCall( MethodCallExpression node )
{
if (node.Method.Name != "Include" && node.Method.Name != "IncludeSpan")
return base.VisitMethodCall( node );
//"Include" == .Where() is present in the query
//"IncludeSpan" == no .Where() in the query
try
{
if (node.Method.Name == "Include")
{
string includedObjectName = (string) node.Arguments.First().GetPrivatePropertyValue("Value");
if (includedObjectName != null)
{
_includes.Add(includedObjectName);
}
}
else if (node.Method.Name == "IncludeSpan")
{
var spanList =
node.Arguments.First().GetPrivatePropertyValue("Value").GetPrivatePropertyValue("SpanList");
var navigations = ((IEnumerable<object>) spanList).Select(s => s.GetPrivateFieldValue("Navigations"));
foreach (var nav in navigations)
_includes.Add(string.Join(".", (IEnumerable<string>) nav));
}
}
catch (Exception e) { }
return base.VisitMethodCall( node );
}
}
One little detail I found when testing his code is the difference in how the included tables are found in the expression based on the presence of a .Where() in the IQueryable<>. Thankfully, this can be checked based on the method name, and while the code is a bit ugly, it does dance around the vastly different structures to return the correct name of the table.
Now I have the name of the table, pluralized, as a string. This is because the name is from the DbContext, so I can reflect over the properties and get the Type of the table:
List<PropertyInfo> contextProperties = typeof( TContext ).GetProperties().ToList();
PropertyInfo prop = contextProperties.First( x => x.Name == s );
With the Type, I can accurately find the table I need via Navigation Properties, then I can build a string to send into my new .Include:
ICollection<string> includes = IncludeVisitor.GetIncludes( query.Expression );
foreach (string include in includes)
{
//sometimes the returned include string will be two tables joined with a '.'; these need to be split and each one checked independently
List<string> split = include.Split( '.' ).ToList();
foreach (string s in split)
{
//using .First here because we expect the property to exist
PropertyInfo prop = contextProperties.First( x => x.Name == s );
//the property will be of type DbSet<ObjectType>, so grab the first generic argument (in this case, the object type)
Type dbSetPropertyType = prop.PropertyType.GetGenericArguments().First();
//Get the type we're looking to add in the .Include
var targetTable = GetTargetTableBasedOnTypeViaNavigation(dbSetPropertyType);
//get the name of the property based on the type of the table we just looked up
PropertyInfo contextProperty = contextProperties.SingleOrDefault(x => x.PropertyType.IsGenericType && x.PropertyType.GetGenericArguments().First().Name == targetTable.Name );
string includeString = "";
//build the string and add it to the query
includeString += include + "." + contextPropertyForChangeTracker.Name;
query = query.Include(includeString);
}
}
This worked on my initial test data sets, though I'm not sure how well it will handle more complex graphs.

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

Dynamic query using LINQ to SQL

I need to figure out if it is possible to dynamically build a query with LINQ, dynamically selecting the table in which to perform the query.
This is an example of what I would do:
//Not working,just for example
public List<dynamic> _getGenericList(String tableName)
{
var l = from a in db.//I need to use here tableName
select a;
return l.ToList<dynamic>();
}
Is there a way to make this possible?
If the query is this simple you can dynamically create a standard sql statement and execute it, this is the most simplest way without using processor heavy reflection and complex code?
var query = "SELECT * FROM " + tableName;
var res = context.ExecuteQuery<dynamic>(query).ToList();
I've found a way to do it, but I'm not sure if I'd use this code. If you have a DataContext that contains two tables:
PrimaryTable
ID,
FirstValue,
SecondValue
SecondaryTable
ID,
FirstSecondaryValue
You could use the following DataHelper class:
class DataHelper
{
public MyDatabaseDataContext db = new MyDatabaseDataContext();
List<dynamic> GetDynamicList<T>() where T : class
{
System.Data.Linq.Table<T> table = db.GetTable<T>();
var result = from a in table select a;
return result.ToList<dynamic>();
}
public List<dynamic> GetWhatIWant(string tableName)
{
Type myClass = Type.GetType("DynamicLinqToSql." + tableName);
MethodInfo method = typeof(DataHelper).GetMethod("GetDynamicList", BindingFlags.NonPublic | BindingFlags.Instance);
method = method.MakeGenericMethod(myClass);
return (List<dynamic>)method.Invoke(this, null);
}
}
Then you can create an instance of your DataHelper and call the GetWhatIWant method, passing in the table name.
var dataHelper = new DataHelper();
List<dynamic> myFirstList = dataHelper.GetWhatIWant("PrimaryTable");
for (int i = 0; i < 5 && i < myFirstList.Count; i++)
{
System.Console.WriteLine(String.Format("{0} - {1}", myFirstList[i].FirstValue.ToString(), myFirstList[i].SecondValue.ToString()));
}
List<dynamic> mySecondList = dataHelper.GetWhatIWant("SecondaryTable");
for (int i = 0; i < 5 && i < mySecondList.Count; i++)
{
System.Console.WriteLine(mySecondList[i].FirstSecondaryValue.ToString());
}
System.Console.ReadKey();
I know this is old, but if you are here looking for answers like I was, then maybe this will help. I'm using a .NET ObjectContext directly instead of a DataContext data source. If you are using the DataContext version then you can simply (I hope) use queryResults = myGlobalContext.ExecuteQuery<dbGenericData>(query).ToList(); instead and I'm pretty sure it will work the same way.
Your tables will be a lot easier to work with if you have standards in naming and design like
the ID field for the table is always X type (INT, GUID, etc)
the ID field is always named tableNameID, the 'table name' with ID tagged on.
etc,
This will allow you to easily build the ID field by simply appending the 'ID' string onto the table name and will allow you to use a reliable CAST, if needed.
Speaking of CAST you will notice one in the query string. You will need to modify the use of the SQL string using CAST, changing field lengths like my nvarChar(50) example, etc, to overcome getting various TYPES of data from your database.
Final note: In the query string you will see I use the 'AS' key word to cast the DB field to a new name. I cast the 'tableIDField' into the name 'id' and I cast the 'requestedField' into the name 'dbData'. This allows the system to match up the renamed fields from the DB into the STRUCT object container we dump the data into. This allows you to construct generic containers to hold the data returned without having to worry about matching up with the DB field names.
I'm not a guru at this stuff, but I hope this helps somebody out.
private void testMethod(string requestedField, string tableName)
{
var tableIDField = tableName + "ID";
var query = "select " + tableIDField + " as id, CAST(" + requestedField + "as nvarchar(50)) as dbData from " + tableName;
List<dbGenericData> queryResults = null;
try
{
queryResults = myGlobalContext.ExecuteStoreQuery<dbGenericData>(query).ToList();
}
catch (Exception ex)
{
//Simply ignore any exceptions.
//These will need examined to determine best solution to unexpected results.
}
}
private struct dbGenericData
{
public dbGenericData(int id, string dbData)
{
this = new dbGenericData();
ID = id;
DBData = dbData;
}
public int ID { get; set; }
public string DBData { get; set; }
}
you can Generic Method and use db.Set<T> that return a DbSet Based on T
var esql = "select t from TypeName as t"
var q = db.CreateQuery(esql);
Use entity sql for linq to sql, http://esql.codeplex.com

Categories

Resources