How to create dynamic Linq Select Expression with anonymous objects - c#

Am using Entity Framework to run a query on a table. However, i need to get select columns only.
class MyEvent
{
public string Name { get; set; }
public int Id { get; set; }
virtual Stage EventStage { get; set; }
..... more columns .....
}
class Stage
{
public string Name { get; set; }
public string Location { get; set; }
..... more columns .....
}
I can write an IQueryable to return these as
dbContext.MyEvents
.Select(s =>
new {
Name = s.Name,
Id = s.Id,
EventStage = new
{
Name = s.EventStage.Name,
Id = s.EventStage.Id
}
}
)
.ToList();
This works as expected, giving me just those columns am interested in.
Now, I need to construct that 'Select' call dynamically using Expression tree, something like here.
How can I achieve that? Is it feasible to construct an anynomous object, like above, via expressions?
EDIT:
The use case for me is that I have a generic dB context class which takes a list of columns as strings to be fetched. In the past, we were returning all columns, ignoring that input list. So, now I need to dynamically generate the select statement to return only the required subset of columns, which can either be done via anonymous object or a dynamically created DTO.
Thanks

Maybe you can use something like the ToDynamic method from here:
https://gist.github.com/volak/20f453de023ff75edeb8
A possible usecase for this problem:
Let the user select the columns to display and query only those selected columns, so you don't query always the whole entity from the database.

Define a strongly typed object and return that. I would avoid using a dynamic object.
Note: you can't return an anonymous object.

Related

Loop through strongly type fields Entity Framework c#

I am trying to come up with a neat solution for this problem to make it scalable. I've got a DataTable dt, which has its structure read from a database. I want to be able to correctly map this data into the correct fields using Entity Framework and allow the code to function even if columns are added or deleted.
using (Entities db = new Entities())
{
foreach (DataRow dr in dt.Rows)
{
var result = db.myTable.SingleOrDefault(e => e.Email == dr["Email"].ToString());
foreach (SourceToDestinationMapping s in mapping)
{
// want to do something like this
result[s.DestinationColumn] = dt[s.DestinationColumn];
// instead of this
result.Name = dt["Name"].ToString();
result.Address = dt["Address"].ToString();
// all field mappings
}
}
}
Is this something that is possible to do? Or do I need to make code changes every time a new column gets added/removed? If this isn't something that works then I can switch to doing something like this without Entity Framework.
Edit:
Example would be:
1, EmailAddress, Email, 1
public partial class SourceToDestinationMapping
{
public int MappingId { get; set; }
public string SourceColumn { get; set; }
public string DestinationColumn { get; set; }
public bool Active { get; set; }
}
Since Entity Framework works with objects you'd need to use reflection to get and set properties without knowing which properties you need to operate on, and it can get pretty complicated if you have many types that you need to handle. So basically examine the type of the object you're looking at, get its list of properties, and search for columns with the same name as the property (or some other convention you have) in the data table row. But again, you'll need to handle the type conversions, if the property is an int you need to get the cell value as an int etc.

LINQ query a list inside a record

EDIT
Turns out EF wont map List strings to a table. So to fix it I've created a simple table with an id, a string field and a foreign Key to the Example table, then in the example table i've updated the List<> attribute to point to this table. ...I think I had a brain fart. sorry all.
I've tried the other questions but I'm still getting the same error.
I have a table with the following:
public class Example
{
public Example()
{
ExampleList = new List<string>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public List<string> ExampleList{ get; set; }
}
Now when I go to query the database, I want to return back entries that contain "imastring" in their ExampleList I'm currently using:
var test = db.Examples.Where(c => c.ExampleList.Count(z => z.Contains("imastring")) == 0).ToList();
However, this query brings back the following error:
The specified type member 'ExampleList' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
I can't figure out whats wrong, what am i missing guys?
For clarity
I'm trying to bring back all "Example" record whos "ExampleList<>" contains the string "imastring"
You cannot store in database list of string type (or primitives). You need to create for example ExampleTable class with id and string value. Or keep whole list in one property (for example strings separated with ';') and parse given string with split. Or serialize list to json and save as string property.

Pass Linq Expression to a function

I want to pass a property list of a class to a function. with in the function based on property list I'm going to generate a query. As exactly same functionality in Linq Select method.
Here I'm gonna implement this for Ingress Database.
As an example,
in front end I wanna run a select as this,
My Entity Class is like this
public class Customer
{
[System.Data.Linq.Mapping.ColumnAttribute(Name="Id",IsPrimaryKey=true)]
public string Id { get; set; }
[System.Data.Linq.Mapping.ColumnAttribute(Name = "Name")]
public string Name { get; set; }
[System.Data.Linq.Mapping.ColumnAttribute(Name = "Address")]
public string Address { get; set; }
[System.Data.Linq.Mapping.ColumnAttribute(Name = "Email")]
public string Email { get; set; }
[System.Data.Linq.Mapping.ColumnAttribute(Name = "Mobile")]
public string Mobile { get; set; }
}
I wanna call a Select function like this,
var result = dataAccessService.Select<Customer>(C=>C.Name,C.Address);
then,using result I can get the Name and Address properties' values.
I think my Select function should looks like this,
( *I think this should done using Linq Expression. But im not sure what are the input parameter and return type. * )
Class DataAccessService
{
// I'm not sure about this return type and input types, generic types.
public TResult Select<TSource,TResult>(Expression<Func<TSource,TResult>> selector)
{
// Here I wanna Iterate through the property list, which is passed from the caller.
// Here using the property list,
// I can get the ColumnAttribute name value and I can generate a select query.
}
}
This is a attempt to create a functionality like in Linq. But im not an expert in Linq Expressions.
There is a project call DbLinq from MIT, but its a big project and still i couldn't grab anything helpful from that.
Can someone please help me to start this, or can someone link me some useful resources to read about this.
What you're trying to do is creating a new anonymous type that consists of Name and Address. This is easily achievable via long form linq (I made that term up, for lack of a better explanation.) Here's a sample from Microsoft, link provided below:
public void Linq11()
{
List<Product> products = GetProductList();
var productInfos =
from p in products
select new { p.ProductName, p.Category, Price = p.UnitPrice };
Console.WriteLine("Product Info:");
foreach (var productInfo in productInfos)
{
Console.WriteLine("{0} is in the category {1} and costs {2} per unit.", productInfo.ProductName, productInfo.Category, productInfo.Price);
}
}
Details: Linq Select Samples
Update:
So are you trying to do something like this then?
var result = dataAccessService.Select<Customer>(c => c.Name, c => c.Address);
public object[] Select<TSource>(params Expression<Func<TSource, object>>[] selectors)
{
var toReturn = new object[selectors.Count()];
foreach (var s in selectors)
{
var func = s.Compile();
//TODO: If you implement Select a proper extension method, you can easily get the source
toReturn[i] = func(TSource);
}
return toReturn;
}
I don't understand why you're trying to implement Select as a function of DataAccessService? Are trying to create this as an extension method rather?
If this is not what you mean though, you need to rephrase you're question big time and as one commenter suggested, tell us what you need not how you want us to design it.

IQueryable returns null on invoking Count c#

I have a problem trying to get the count out of the following query:
var usersView = PopulateUsersView(); //usersView is an IQueryable object
var foo = usersView.Where(fields => fields.ConferenceRole.ToLower().Contains("role"));
Where UsersView is a class which is populated from an EF entity called users (refer to the first line in the code above)
This is the class definition for the UsersView class:
public class UsersView
{
public int UserId { get; set; }
public string Title { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Street1 { get; set; }
public string Street2 { get; set; }
public string City { get; set; }
public string PostCode { get; set; }
public string CountryName { get; set; }
public string WorkPlaceName { get; set; }
public string Gender { get; set; }
public string EMail { get; set; }
public string Company { get; set; }
public string RoleName { get; set; }
public string ConferenceRole { get; set; }
}
As I said trying to execute the line foo.Count() returns Null Exception and this might be because the ConferenceRole column allows Null in the database.
Now what I can't understand is that when I invoke the same query directly on the ObjectQuery the Count of records (i.e. invoking foo2.Count()) is returned without any exceptions.
var foo2 = entities.users.Where(fields => fields.ConferenceRole.ToLower().Contains("role"));
Is it possible to the same query above but using the IQueryable usersView object instead?
(It is crucial for me to use the usersView object rather than directly querying the entities.users entity)
EDIT
Below is the code from the PopulateUsersView method
private IQueryable<UsersView> PopulateUsersView()
{
using (EBCPRegEntities entities = new EBCPRegEntities())
{
var users = entities.users.ToList();
List<UsersView> userViews = new List<UsersView>();
foreach (user u in users)
{
userViews.Add(new UsersView()
{
UserId = u.UserId,
Title = u.Title,
Name = u.Name,
Surname = u.Surname,
Street1 = u.Street1,
Street2 = u.Street2,
City = u.City,
PostCode = u.Post_Code,
CountryName = u.country.Name,
WorkPlaceName = u.workplace.Name,
Gender = u.Gender,
EMail = u.E_Mail,
Company = u.Company,
RoleName = u.roles.FirstOrDefault().Name,
ConferenceRole = u.ConferenceRole
});
}
return userViews.AsQueryable();
}
}
Thanks
UPDATE...
Thanks guys I finally found a good answer to the difference between the IQueryable and the ObjectQuery objects.
As a solution I am checking if the ConferenceRole is null and then checking with the contains method as many of you guys have said.
My guess is that your PopulateUsersView() method is actually executing a query and returning an IQueryable Linq-to-Objects object - while the foo2 line executes the query only in the SQL layer. If this is the case, the obviously PopulateUsersView() is going to be quite an inefficient way to perform the Count
To debug this:
can you post some code from PopulateUsersView()?
can you try running both sets of code through the EF tracing provider to see what is executed in SQL? (see http://code.msdn.microsoft.com/EFProviderWrappers)
Update
#Ryan - thanks for posting the code to PopulateUsersView
Looks like my guess was right - you are doing a query which gets the whole table back into a List - and its this list that you then query further using Linq2Objects.
#ntziolis has provided one solution to your problem - by testing for null before doing the ToLower(). However, if your only requirement is to Count the non-empty items list, then I recommend you look at changing the PopulateUsersView method or changing your overall design. If all you need is a Count then it would be much more efficient to ensure that the database does this work and not the C# code. This is espeically the case if the table has lots of rows - e.g. you definitely don't want to be pulling 1000s of rows back into memory from the database.
Update 2
Please do consider optimising this and not just doing a simple != null fix.
Looking at your code, there are several lines which will cause multiple sql calls:
CountryName = u.country.Name
WorkPlaceName = u.workplace.Name
RoleName = u.roles.FirstOrDefault().Name
Since these are called in a foreach loop, then to calculate a count of ~500 users, then you will probably make somewhere around 1501 SQL calls (although some roles and countries will hopefully be cached), returning perhaps a megabyte of data in total? All this just to calculate a single integer Count?
Try to check whether ConferenceRole is null before calling a method on it:
var foo = usersView.Where(fields => fields.ConferenceRole != null
&& fields.ConferenceRole.ToLower().Contains("role"));
This will enable you to call the count method on the user view.
So why does it work against the ObjectQuery?
When executing the query against the ObjectQuery, LinqToSql is converting your query into proper sql which does not have problems with null values, something like this (it's sample markup sql only the actual query looks much different, also '=' is used rather than checking for contains):
SELECT COUNT(*) from USERS U WHERE TOLOWER(U.CONFERENCEROLE) = 'role'
The difference to the :NET code is: It will not call a method on an object but merely call a method and pass in the value, therefore no NullReference can occur in this case.
In order to confirm this you can try to force the .NET runtime to execute the SQL prior to calling the where method, by simply adding a ToList() before the .Where()
var foo2 = entities.users.ToList()
.Where(fields => fields.ConferenceRole.ToLower().Contains("role"));
This should result in the exact same error you have seen with the UserView.
And yes this will return the entire user table first, so don't use it in live code ;)
UPDATE
I had to update the answer since I c&p the wrong query in the beginning, the above points still stand though.

Custom object with nested collection

I've created an object that contains another collection in one of it properties.
This is the main object:
public class MeterPrevReadInfo
{
public int JobMeterID { get; set; }
public string PreviousJobReference { get; set; }
public FuelType MeterFuelType { get; set; }
public List<MeterPrevReadRegInfo> Regs { get; set; }
public DateTime DateMeterRead { get; set; }
}
This is the child object:
public class MeterPrevReadRegInfo
{
public string RegisterID { get; set; }
public string MeterRead { get; set; }
}
I need to bind this object to a repeater control, I would like to show the DateMeterRead property and all the MeterRead properties in the repeater.
Is this possible using Linq? I could easily do it using a t-sql query from the database, but I just figured it should be possible to do this in memory without the overhead of another trip to the database.
Don't get confused - LINQ isn't a data access layer or ORM (perhaps you're thinking of LINQ-to-SQL, or LINQ-to-Entities?)
You can absolutely query an in-memory collection using LINQ, although your questions seems to relate to database.
I could easily do it using a t-sql
query from the database, but I just
figured it should be possible to do
this in memory without the overhead of
another trip to the database.
You can retrieve all this data from the database in one query & then construct objects. You can do this with a stored procedure, LINQ-to-SQL, Entity Framework, or other tools. You should choose the best tool for your requirements. I expect this is a very small part of the requirement, so take a step back, choose the best tool, and make this work using that tool.
sure this is possible. It looks like you want something like this:
List<MeterPrevReadInfo> list = ...;
var result = from item in list
from info in item.Regs
select new {item.DateMeterRead, info.MeterRead};
This query defines a list of anonymous objects with the two properties you want.
You can access an anonymous object representing the model you want by using the Linq Select extension method as follows:
var readInfo = new MeterPrevReadInfo();
readInfo.Regs.Select(x => new {
x.RegisterID,
x.MeterRead,
readInfo.DateMeterRead
});

Categories

Resources