Converting object returned from Linq.Dynamic - c#

I am using the Linq.Dynamic Library and EF6. I am attempting to select only the fields from a database that my user selects.
However, all that is returned is a List<object>, I have attempted to Cast<dynamic>, and every way I can think of, but no matter what the object has 0 fields.
I have also tried explicitly declaring as an IEnumerable and that too was unsuccessful, and was unable to call .ToList(), without first calling Cast<T> which too was unsuccessful.
When converting one of the objects to string I get: "{filenumber=345400, custom2=, custom3=, custom6=4076995332, custom8=4072121417}".
The data is being returned I simply cannot cast it to the appropriate type.
var query = cmax.dbases
.Where(w => statuses.Any(a => w.statusname == a) && portfolios.Any(a => w.portfolio == a))
.Select(string.Format("new ({0})", string.Join(",", fields)))
.Take(Math.Min((int) takeAmount, count - taken));
var take = await query.ToListAsync();
take.ForEach(data => {
var type = take.GetType();
var properties = type.GetProperties();
var propCount = properties.Count();
properties.ToList().ForEach(prop => {
var name = prop.Name;
});
});

In one of my use cases I have converted the results to List<string[]> through an extension for IQueryable. As I know my column order in Select("New (columnNames...) "), I could easily figure out which one is which.
so here is the code of the extension
public static IList<string[]> ToStringArray(this IQueryable queryFinal)
{
var query = (IQueryable<dynamic>)queryFinal;
IList<dynamic> data = query.ToList();
System.Reflection.PropertyInfo[] props = null;
if (data.Count > 0) props = data[0].GetType().GetProperties();
if (props == null) return new List<string[]>();
/*I do other things using reflection here*/
return data.Select(d => props.Select(p => (p.GetValue(d, null) ?? string.Empty).ToString()).OfType<string>().ToArray()).ToList();
}
use it as
var result = query.ToStringArray();

The only viable solution I found was to manually parse the data in the string:
take.ForEach(data =>
{
var dbtrData = new DebtorData();
var cleaned = data.ToString().Replace("{", "").Replace("}", "");
var pairs = cleaned.Split(',');
pairs.ToList().ForEach(pair =>
{
var key = pair.Split('=')[0].Replace(" ", "");
var value = pair.Split('=')[1];
//USEDATA
}
});

Related

Create a dynamic select function in Enity Framework

I have a question for you regarding the creation of a Dynamic select query in Entity Framework.
I already have a dynamic query for the select based on rights etc. But for each table I get 30+ fields that I have to parse via the .GetType().GetProperties().
Its complex and its quite costly in terms of resource due to the amount of data we have.
I have a service that tells me which fields I should select for each table. I would like to find a way to transform that into the query but I can't find something that is really dynamic.
That is not dynamic but manual:
using (var context = new StackOverflowContext())
{
var posts = context.Posts
.Where(p => p.Tags == "<sql-server>")
.Select(p => new {p.Id, p.Title});
// Do something;
}
I need to say, select only those fields but only the fields with this names.
I have the field list in a list of string but that could be changed.
Could you please help me?
Here is a .Net Fiddle code (made by msbendtsen) that allows to dynamically select columns (properties).
https://dotnetfiddle.net/3IMR1r
The sample is written for linq to objects but it should work with entity frameworks.
The key section is:
internal static IQueryable SelectProperties<T>(this IQueryable<T> queryable, IEnumerable<string> propertyNames)
{
// get propertyinfo's from original type
var properties = typeof(T).GetProperties().Where(p => propertyNames.Contains(p.Name));
// Create the x => expression
var lambdaParameterExpression = Expression.Parameter(typeof(T));
// Create the x.<propertyName>'s
var propertyExpressions = properties.Select(p => Expression.Property(lambdaParameterExpression, p));
// Creating anonymous type using dictionary of property name and property type
var anonymousType = AnonymousTypeUtils.CreateType(properties.ToDictionary(p => p.Name, p => p.PropertyType));
var anonymousTypeConstructor = anonymousType.GetConstructors().Single();
var anonymousTypeMembers = anonymousType.GetProperties().Cast<MemberInfo>().ToArray();
// Create the new {} expression using
var anonymousTypeNewExpression = Expression.New(anonymousTypeConstructor, propertyExpressions, anonymousTypeMembers);
var selectLambdaMethod = GetExpressionLambdaMethod(lambdaParameterExpression.Type, anonymousType);
var selectBodyLambdaParameters = new object[] { anonymousTypeNewExpression, new[] { lambdaParameterExpression } };
var selectBodyLambdaExpression = (LambdaExpression)selectLambdaMethod.Invoke(null, selectBodyLambdaParameters);
var selectMethod = GetQueryableSelectMethod(typeof(T), anonymousType);
var selectedQueryable = selectMethod.Invoke(null, new object[] { queryable, selectBodyLambdaExpression }) as IQueryable;
return selectedQueryable;
}

Project a Query onto an anonymous Dictionary<string,int>

I am trying to check if an entity in the database has any foreign key relations, so that I can inform the user the entity can or cannot be deleted.
I understand this can be done in a rolled back transaction, however I would like to inform the user how many references and where they are to assist in their decision to delete the entity.
I am trying to avoid loading the entire navigation collection into memory to get this data as it may be large. So, in light of this, I can formulate this simple query to firstly determine if there are any references:
private bool CanDeleteComponent(int compId)
{
var query = _Context.Components.Where(c => c.ComponentID == compId)
.Select(comp => new
{
References = comp.Incidents.Any() &&
comp.Drawings.Any() &&
comp.Documents.Any() &&
comp.Tasks.Any() &&
comp.Images.Any() &&
comp.Instructions.Any()
});
var result = query.FirstOrDefault();
if (result != null)
{
return !result.References;
}
return true;
}
This performs a series of SELECT COUNT(*) FROM <TABLE> WHERE... queries.
Now, I would like to provide some further information on the number of references. Ideally I would like to return a Dictionary with the referenced data's name, and the associated count. This way I can loop through the result, rather than access individual properties of an anonymous type. However, what I have tried results in an exception:
var query = _Context.Components.Where(c => c.ComponentID == compId)
.Select(comp => new Dictionary<string, int>
{
{"Events", comp.Incidents.Count()},
{"Drawings", comp.Drawings.Count()},
{"Documents", comp.Documents.Count()},
{"Tasks", comp.Tasks.Count()},
{"Images", comp.Images.Count()},
{"Instructions", comp.Instructions.Count()},
});
var result = query.FirstOrDefault();
return query.Any(fk => fk.Value > 0);
The exception that is raised is:
A first chance exception of type 'System.NotSupportedException' occurred in EntityFramework.SqlServer.dll
Additional information: Only list initializer items with a single element are supported in LINQ to Entities.
Is there any way around this, such that I can return some sort of IEnumerable rather than an anonymous type?
Thanks
EDIT
I currently have lazy loading disabled on my context. If there is a solution without turning Lazy loading on that would be appreciated.
You can't build a Dictionary<K,V> in the SELECT statement, that's why you get System.NotSupportedException. You can get the single Component first by query, and build the dictionary in the memory.
var comp = _Context.Components.SingleOrDefault(c => c.ComponentID == compId);
var dict = new Dictionary<string, int>()
{
{ "Events", comp.Incidents.Count()},
{ "Drawings", comp.Drawings.Count()},
{ "Documents", comp.Documents.Count()},
{ "Tasks", comp.Tasks.Count()},
{ "Images", comp.Images.Count()},
{ "Instructions", comp.Instructions.Count()}
};
EDIT If you are not using lazy loading, you can explicitly .Include the properties in the query:
var comp = _Context.Components
.Include(c => c.Incidents)
...
.SingleOrDefault(c => c.ComponentID == compId);
Is there any way around this, such that I can return some sort of IEnumerable rather than an anonymous type?
Actually there is, although I'm not sure you'll like the generated SQL (compared to the one using anonymous type).
var query = _Context.Components.Where(c => c.ComponentID == compId)
.SelectMany(comp => new []
{
new { Key = "Events", Value = comp.Incidents.Count() },
new { Key = "Drawings", Value = comp.Drawings.Count() },
new { Key = "Documents", Value = comp.Documents.Count() },
new { Key = "Tasks", Value = comp.Tasks.Count() },
new { Key = "Images", Value = comp.Images.Count() },
new { Key = "Instructions", Value = comp.Instructions.Count() },
}.ToList());
var result = query.ToDictionary(e => e.Key, e => e.Value);
return query.Any(fk => fk.Value > 0);

Convert foreach to linq expression with select

I have expression:
var newValues = MetarDecoder.Decode(telegram)
.OfType<MeteoParameter<decimal>>()
.Select(parameter => MeteoParameterFactory
.Create(parameter.ParameterId, parameter.DateTime.ToLocalTime(), parameter.Status, parameter.Value))
.ToList();
MeteoParameterFactory cannot be changed for some reasons, just take it as it is.
MeteoParameter also have string Info property.
I need to copy Info from old parameter to MeteoParameterFactory.Create() result.
Without LINQ it looks like:
var val = MetarDecoder.Decode(telegram).OfType<MeteoParameter<decimal>>().ToList();
foreach (var param in val)
{
var parameter = MeteoParameterFactory.Create(param.ParameterId, param.DateTime.ToLocalTime(), param.Status, param.Value);
parameter.Info = param.Info;
newValues.Add(parameter);
}
So, is there any way to add this part in LINQ expression shown below?
In Select you can create an anonymous function that returns the parameter created inside of it.
var newValues = MetarDecoder.Decode(telegram)
.OfType<MeteoParameter<decimal>>()
.Select(param => {
var parameter = MeteoParameterFactory.Create(param.ParameterId, param.DateTime.ToLocalTime(), param.Status, param.Value);
parameter.Info = param.Info;
return parameter;
}).ToList();
var val = MetarDecoder
.Decode(telegram)
.OfType<MeteoParameter<decimal>>()
.ToList()
.ForEach(param =>
{
var parameter = MeteoParameterFactory.Create(param.ParameterId, param.DateTime.ToLocalTime(), param.Status, param.Value);
parameter.Info = param.Info;
newValues.Add(parameter);
});

How to access to a field of a dynamic type?

I'm trying to wrap the results of a query with a class called QueryResultViewModel from a list of dynamic objects retrieved by LINQ. These contain a integer field called Worked. I should not use a non-dynamic type because depending on the query it has other fields. I tried that:
var query = new HoursQuery( .. parameters .. );
this.Result = new ObservableCollection<QueryResultViewModel>(
query.Execute().Select( x => new QueryResultViewModel( x.Worked )));
But I got "'object' does not contain a definition for 'Worked'" and I don't know If it can be fixed without changing query's return type.
The Execute code may be useful too:
var res = some_list.GroupBy(a => new { a.Employee, a.RelatedTask, a.Start.Month })
.Select(g => new { K = g.Key, Worked = g.Sum(s => s.Duration.TotalHours) });
EDIT: This worked great but maybe it's not very elegant.
public class HQueryDTO
{
public double Worked;
public object K;
}
public IEnumerable<dynamic> Execute()
{
var list = base.Execute();
return res = list.GroupBy(a => new { a.Employee, a.RelatedTask } )
.Select(g => new HQueryDTO { K = g.Key, Worked = g.Sum(s => s.Duration.TotalHours) });
}
Now that the result has a type it can be returned dynamic.
I'm assuming you get that error at compile-time, in which case simply introduce dynamic via a cast:
.Select(x => new QueryResultViewModel( ((dynamic)x).Worked ))
I assume that the signature of Execute is something like object Execute(). If you return dynamic, it should work.

How can I solve the issue about using Moq with IDataReader

I got a problem with Moq and Automapper regarding IDataReader.
I found an example on stackoverflow and modified the code.
public static IDataReader MockIDataReader<T>(List<T> ojectsToEmulate) where T : class
{
var moq = new Mock<IDataReader>();
// This var stores current position in 'ojectsToEmulate' list
var count = 0;
moq.Setup(x => x.Read())
// Return 'True' while list still has an item
.Returns(() => count < ojectsToEmulate.Count)
// Go to next position
.Callback(() => count++);
var properties = typeof (T).GetProperties();
foreach (PropertyInfo t in properties)
{
var propName = t.Name;
moq.Setup(x => x[propName]).Returns(() => ojectsToEmulate[count].GetType().GetProperty(propName).GetValue(ojectsToEmulate[count],null));
}
return moq.Object;
}
}
My mapping:
Mapper.Configuration.CreateMap(typeof(IDataReader), typeof(IEnumerable<T>));
var result = Mapper.Map<IDataReader, IEnumerable<T>>(reader);
The problem I got here is that my result has 1 result a cityModel but all it's properties are null. If I check the value from my mocked reader like reader["name"] I got the "Alingsås" value so the mocking is correct but Automapper seams to have the problem.
I use a List of objects that I pass to my method that mocks it all.
var cityModel = new CityModel();
cityModel.Name = "Alingsås";
cityModel.Id = "SE";
cityModel.CountryId = "SE";
var cityModels = new List<CityModel>();
cityModels.Add(cityModel);
_fakeReader = MockTester.MockIDataReader(cityModels);
The code works fine, no exception is thrown, but the mapper gives me an object without the
valules. I can see in the debugger my reflection code works but it seams like my
x["Name"] aren't the method Automapper call from IDataReader? Or is it?
What can be wrong here?
Automapper internally uses the int indexer of IDataReader so you need to call Setup on that instead of Item[String].
Checking the Automapper's source you need to setup some additional methods to make it work:
//...
var index = 0;
foreach (PropertyInfo t in properties)
{
var propName = t.Name;
int index1 = index; // avoid access to modified closure
moq.Setup(x => x.GetFieldType(index1)).Returns(t.PropertyType);
moq.Setup(x => x.GetName(index1)).Returns(propName);
moq.Setup(x => x[index1])
.Returns(ojectsToEmulate[count]
.GetType()
.GetProperty(propName).GetValue(ojectsToEmulate[count], null));
index++;
}
moq.Setup(x => x.FieldCount).Returns(properties.Length);
//...

Categories