converting linq result to dictionary<string, int> - linq

I'm quite new to csharp and linq, so am having some trouble with a simple enough casting issue.
I have a simple object -
public class AskQuestionFormView
{
public string QuestionText { get; set; }
public Dictionary<string,int> Location { get; set; }
}
and a linq query
AskQuestionFormView aqfv = new AskQuestionFormView();
var result = from c in db.Countries
.ToDictionary(c => c.LocationName.ToString(),
c=>c.ID)
select c;
aqfv.Location = (Dictionary<string,int>)result;
but I'm getting the following error -
System.InvalidCastException: Unable to cast object of type 'WhereSelectEnumerableIterator`2[System.Collections.Generic.KeyValuePair`2[System.String,System.Int32],System.Collections.Generic.KeyValuePair`2[System.String,System.Int32]]' to type 'System.Collections.Generic.Dictionary`2[System.String,System.Int32]'.
What am I doing wrong?
Thanks in advance.

Your query is attempting to query from the dictionary, which is returning a KeyValuePair when you use select c. You just need to generate a Dictionary from the table, so try this instead:
var result = db.Countries.ToDictionary(c => c.LocationName, c => c.ID);
aqfv.Location = result;

Try it like this:
var result = (from c in db.Countries
select c).ToDictionary(c => c.LocationName.ToString(),
c=>c.ID);
aqfv.Location = result;

Related

The entity or complex type 'AdventureWorks2012Model.Product' cannot be constructed in a LINQ to Entities query

As you can see, I got this error when I built Data Gird using Kendo UI. Does anybody could point out where I'm wrong in my code below.
private IEnumerable<Product> GetSubProduct()
{
var context = new AdvenDBEntities();
var subcate = context.Products.Select(p => new Product
{
ProductID = p.ProductID,
Name = p.Name,
Color = p.Color,
ListPrice = p.ListPrice,
}).ToList();
return subcate;
}
Error:
The entity or complex type 'AdventureWorks2012Model.Product' cannot be constructed in a LINQ to Entities query.
Thank you so much for your time!
Since Product is an entity of model, you are creating new object of this entity while selecting the records, which is NOT good idea, I am NOT sure how model will handle this kind of behaviour that is why it is preventing you to do so, (I guess). Anyway you can change the code to this,
private IEnumerable<Product> GetSubProduct()
{
var context = new AdvenDBEntities();
var subcate = context.Products.ToList();
return subcate;
}
BTW your function name indicating that you are missing a Where clause.
Also you can create some custom DTO class and use it instead.
E.g.
class ProductDTO
{
public int ProductID { get; set; }
public string Name { get; set; }
public string Color { get; set; }
public decimal ListPrice { get; set; }
}
private IEnumerable<ProductDTO> GetSubProduct()
{
var context = new AdvenDBEntities();
var subcate = context.Products.Select(p => new ProductDTO
{
ProductID = p.ProductID,
Name = p.Name,
Color = p.Color,
ListPrice = p.ListPrice,
}).ToList();
return subcate;
}
The first bad smell code I can point out for you. DBContext implements IDisposable so you are responsible for calling Dispose on it. In all, but one case in here, using block
You must build query to get all the product and then extract from it.
private IEnumerable<Product> GetSubProduct()
{
using (var context = new AdvenDBEntities())
{
// Get all constructed type product and then select from it
var subcate = context.Products
.ToList()
.Select(p => new Product
{
ProductID = p.ProductID,
Name = p.Name,
Color = p.Color,
ListPrice = p.ListPrice,
});
return subcate;
}
}

Linq to entity - dynamic query

public void ApproveRowTable(string tablename, List<int> idValues)
{
foreach (var x in idValues)
{
var context = new SSPModel.sspEntities();
var genRules = (from a in context.GeneralRules
where a.ID == x
select a).SingleOrDefault();
genRules.Approved_by = GlobalClass.GlobalVar;
genRules.Approved_on = DateTime.Now;
context.SaveChanges();
}
}
In my query (from a in context.GeneralRules...) I would like to make it query base on a parameter (tablename) rather than i have to go and supply the name of the table in the query (as it is doing right now.). Any way i can get it to do that .. basic.. from a in context.TABLENAME -- TABLENAME is a parameter that is going to be passed when the function is called. Help
This will be difficult if your entity types do not all implement the same interface or derive from the same class. If they do, it's pretty simple:
// example base type, which your entities would need to implement
public interface IApprovable
{
public int ID {get; set;}
public string Approved_by {get; set;}
public DateTime Approved_on {get; set;}
}
//...
public void ApproveRowTable<T>(List<int> idValues)
where T : IApprovable
{
using(var context = new SSPModel.sspEntities())
{
var table = context.Set<T>();
var entities = table.Where(e => idValues.Contains(e.ID));
foreach(var entity in entities)
{
entity.Approved_by = GlobalClass.GlobalVar;
entity.Approved_on = DateTime.Now;
}
context.SaveChanges();
}
}
If your entity types do not implement a common base type, then you should modify them by creating empty partials which do implement it:
public partial class GeneralRule : IApprovable {}
If you cannot do that, then you can do something like the following. (I'm assuming ID is the PK, so we can use Find() rather than needing to build an expression:
public void ApproveTableRows(Type entityType, IEnumerable<int> idsToApprove)
{
using(var context = new SSPModel.sspEntities())
{
var set = context.Set(entityType);
if(set == null)
throw new ArgumentException("No DbSet found with provided name", "tableSetName");
var approveByProperty = entityType.GetProperty("Approved_by");
var approveOnProperty = entityType.GetProperty("Approved_on");
if(approveByProperty == null || approveOnProperty == null)
throw new InvalidOperationException("Entity type does not contain approval properties");
foreach (object id in idsToApprove)
{
var entityInstance = set.Find(id);
approveByProperty.SetValue(entityInstance, GlobalClass.GlobalVar);
approveOnProperty.SetValue(entityInstance, DateTime.Now);
}
context.SaveChanges();
}
}
As you can see, this is less efficient, as it issues a new query for each ID rather than getting them all at once. Also, the method accepts an entity Type rather than a string, to avoid the need to hunt down the right property by reflection. This could be improved, but really I think you should probably update your entities to implement a shared interface.
I assume you would like to have the method generic. When you are using EF all your tables are represented as objects, so you don't have to specify which table you want by name, just use a generic parameter.
I doubt that my solution is best, but it should work. But I have to warn you, reflection is slow and many times its usage is not right.
public void ApproveRowTable<T>(List<int> idValues)
{
var context = new SSPModel.sspEntities();
var table = context.GetType().GetProperties().OfType<T>().Single();
var genRules = (from a in table
where a.ID == x
select a).SingleOrDefault();
genRules.Approved_by = GlobalClass.GlobalVar;
genRules.Approved_on = DateTime.Now;
context.SaveChanges();
}

Problem getting same result from DataContext.Translate

This question is a follow up to this question:
How to create a list from two values
Consider this code:
class MainClass()
{
string MainKey {get;set;}
string MainName {get;set;}
IEnumerable<SmallObject> MainList {get;set}
}
class SmallObject()
{
string SmallKey {get;set}
}
and:
var mainQuery = (from v from DataContext.myTable
select v);
var myQuery = (from v in mainQuery
select new MainClass()
{
MainKey = v.Field1,
MainName = v.Field2,
MainList = new []
{
new SmallObject { SmallKey = v.Field3 },
new SmallObject { SmallKey = v.Field4 },
}
});
var result1 = myQuery.ToList();
//Changing datatypes for optimization reasons in SQLServer2000
var cmd = DataContext.GetCommand(myQuery);
foreach (System.Data.Common.DbParameter param in cmd.Parameters)
{
// nvarchar -> varchar
// decimal -> numeric
}
var result2 = DataContext.Translate<MainClass>(cmd.ExecuteReader()).ToList();
result1.MainList is OK
result2.MainList is null
The original query was very slow running on SQLServer2000, and I got it fixed when changing datatypes (Linq uses nvarchar and decimal, as my database use varchar and numeric)
So I want result2 to be the same as result1, but that doesn't happen when doing a DataContext.Translate like this.
Any thoughts of getting the same result here?
I've also tryed anonymous types, like this:
IEnumerable<object> MainList {get;set;}
...
MainList = new []
{
new { SmallKey = v.Field3},
new { SmallKey = v.Field4},
}
but the result is the same:
I think you are asking too much from Translate.
If I understand you correctly, it is the first query (mainQuery) that is too slow, so I would look to replace it.
I would create a simpler temporary class like
public class TmpClass
{
public string Field1 {get;set;}
public string Field2 {get;set;}
public string Field3 {get;set;}
public string Field4 {get;set;}
}
Once the list is in this format, you can use the second query to change it to a list of MainClass.
Just a matter of interest, what is the difference between the sql outputted by Linq and your customized version? Unless it is does some casting, I would not expect this type of query to need optimizing.
I would use the AsEnumerable extension method which basically converts the IQueryable to an IEnumerable which forces the enumerator to be processed. You could achieve the same thing by calling ToArray() or ToList() but AsEnumerable() magically lets you return it back to an IQueryable by calling AsQueryable()
So probably doing the following will work for you:
var result1 = DataContext.myTable.AsEnumerable()
.Select(v=> new MainClass {
MainKey = v.Field1,
MainName = v.Field2,
MainList = new []
{
new SmallObject { SmallKey = v.Field3 },
new SmallObject { SmallKey = v.Field4 },
}
});

Why predicate isn't filtering when building it via reflection

I'm building a rather large filter based on an SearchObject that has 50+ fields that can be searched.
Rather than building my where clause for each one of these individually I thought I'd use some slight of hand and try building custom attribute suppling the necessary information and then using reflection to build out each of my predicate statements (Using LinqKit btw). Trouble is, that the code finds the appropriate values in the reflection code and successfully builds a predicate for the property, but the "where" doesn't seem to actually generate and my query always returns 0 records.
The attribute is simple:
[AttributeUsage(AttributeTargets.Property, AllowMultiple=true)]
public class FilterAttribute: Attribute
{
public FilterType FilterType { get; set; } //enum{ Object, Database}
public string FilterPath { get; set; }
//var predicate = PredicateBuilder.False<Metadata>();
}
And this is my method that builds out the query:
public List<ETracker.Objects.Item> Search(Search SearchObject, int Page, int PageSize)
{
var predicate = PredicateBuilder.False<ETracker.Objects.Item>();
Type t = typeof(Search);
IEnumerable<PropertyInfo> pi = t.GetProperties();
string title = string.Empty;
foreach (var property in pi)
{
if (Attribute.IsDefined(property, typeof(FilterAttribute)))
{
var attrs = property.GetCustomAttributes(typeof(FilterAttribute),true);
var value = property.GetValue(SearchObject, null);
if (property.Name == "Title")
title = (string)value;
predicate.Or(a => GetPropertyVal(a, ((FilterAttribute)attrs[0]).FilterPath) == value);
}
}
var res = dataContext.GetAllItems().Take(1000)
.Where(a => SearchObject.Subcategories.Select(b => b.ID).ToArray().Contains(a.SubCategory.ID))
.Where(predicate);
return res.ToList();
}
The SearchObject is quite simple:
public class Search
{
public List<Item> Items { get; set; }
[Filter(FilterType = FilterType.Object, FilterPath = "Title")]
public string Title { get; set; }
...
}
Any suggestions will be greatly appreciated. I may well be going way the wrong direction and will take no offense if someone has a better alternative (or at least one that works)
You're not assigning your predicate anywhere. Change the line to this:
predicate = predicate.Or(a => GetPropertyVal(a, ((FilterAttribute)attrs[0]).FilterPath) == value);

LINQ GroupBy month

I am having trouble getting an IQueryable list of a (subsonic) object grouped by Month and Year.
Basic view of the object...
public partial class DatabaseObject
{
[SubSonicPrimaryKey]
public int objectID { get; set; }
public string Description { get; set; }
public decimal Value { get; set; }
public string Category { get; set; }
public DateTime DateOccurred { get; set; }
}
Method to get IQueryable in my Database repository...
public IQueryable GetData(string DataType)
{
return (from t in db.All<DatabaseObject>()
orderby t.DateOccurred descending
select t)
.Where(e => e.Category == DataType);
}
My question is, how can I return the dates grouped by Month? I have tried the below, but this results in compiler warnings regarding anonymous types...
public IQueryable GetData(string DataType)
{
var datalist = (from t in db.All<FinancialTransaction>().Where(e => e.Category == DataType);
let m = new
{
month = t.DateOccurred.Month,
year = t.DateOccurred.Year
}
group t by m into l select new
{
Description = string.Format("{0}/{1}", l.Key.month, l.Key.year),
Value = l.Sum(v => v.Value), // Sum(v => v.Value),
Category = "Grouped"
DateOccurred = l.Last(v => v.DateOccurred)
}
return datalist;
}
Any ideas?
Try this couple issues i found, but you basically need to select a Database object versus anonymous type?
IQueryable<DatabaseObject> datalist = (
from t in db.All<FinancialTransaction>().Where(e => e.Category == DataType)
let m = new
{
month = t.DateOccurred.Month,
year = t.DateOccurred.Year
}
group t by m into l
select new DatabaseObject()
{
Description = string.Format("{0}/{1}", l.Key.month, l.Key.year),
Value = l.Sum(v => v.Value), //Sum(v => v.Value),
Category = "Grouped",
DateOccurred = l.Max(v => v.DateOccurred)
}).AsQueryable();
Let me know if my solution is now what you want. I also noticed you were using Last? The extension you were using I do not have so I replaced it with Max. I don't have subsonic installed so it might come with the libraries.
Any way don't combine LINQ in query syntax and LINQ in extension methods syntax. Use next:
from t in db.All<DatabaseObject>()
where e.Category equals DataType
orderby t.DateOccurred descending
select t;
The issue is apparantly to do with the way Subsonic interprests certain linq statements and is a known bug.
IEnumerable<DatabaseObject> datalist = (
from t in db.All<FinancialTransaction>().Where(e => e.Category == DataType).ToList()
let m = new
{
month = t.DateOccurred.Month,
year = t.DateOccurred.Year
}
group t by m into l
select new DatabaseObject()
{
Description = string.Format("{0}/{1}", l.Key.month, l.Key.year),
Value = l.Sum(v => v.Value), //Sum(v => v.Value),
Category = "Grouped",
DateOccurred = l.Max(v => v.DateOccurred)
}).AsQueryable();
I have fixed this by declaring an list of type IEnumerable and using ToList() to cast the database interaction, finally the query is then recast AsQueryable()

Resources