Chain Lambda Linq - c#

public class Translation
{
public string LanguageCode { get; set; }
public string Value { get; set; }
}
public class tblEnumJobFunction
{
public string strEnum { get; set; }
public List<Translation> mlgValue { get; set; } //mlgValue->MultiLingualValue
}
I have a List<tblEnumJobFunction> JobFunctionList with some data.
Example Data:
JobFunctionList[0].strEnum="ENUM_Manager";
JobFunctionList[0].mlgValue[0].LanguageCode ="EN";
JobFunctionList[0].mlgValue[0].Value="Manager";
JobFunctionList[0].mlgValue[1].LanguageCode ="DE";
JobFunctionList[0].mlgValue[1].Value="Geschäftsführer";
JobFunctionList[1].strEnum="ENUM_Student";
JobFunctionList[1].mlgValue[0].LanguageCode ="EN";
JobFunctionList[1].mlgValue[0].Value="Student";
JobFunctionList[1].mlgValue[1].LanguageCode ="DE";
JobFunctionList[1].mlgValue[1].Value="Schüler";
I can filter this list with LINQ by given country Code and happy with it.
The Question is how can I write equivalent below query syntax by lambda with the List/Collection extensions?
It is a cascade/chain query; looking into a list that is inside another list.
This query syntax is working OK.
string CountryCode ="EN";
var Query = from jobfunction in JobFunctionList
from translation in jobfunction.mlgValue
where translation.LanguageCode == CountryCode //'EN'
select translation;
The Result is;
List<string> JobList;
foreach (var translationitem in Query)
{
JobList.Add(translationitem .Value);
}
now I have
JobList[0]="Manager";
JobList[1]="Student";
For CountryCode="DE" I have;
JobList[0]="Geschäftsführer";
JobList[1]="Schüler";
Is there any way to write above query syntax with lambda similiar to this one?
JobFunctionList.Select(a=>a.mlgValue).Where(b=>b....)...

Two from clauses, as in your example, flatten your sequence. You need to use SelectMany extension method. This is probably what you are looking for:
List<string> JobList = Objs.SelectMany(jobFunction => jobFunction.mlgValue)
.Where(translation => translation.LanguageCode == CountryCode)
.Select(translation => translation.Value)
.ToList();
note: consider using good names, even for formal parameters with the small scope within lambdas . a, b, m, fo are not the best names for this.

Related

How to use a copy-constructor in a LINQ to Entities query?

I'm working on a Linq expression in which I get an object from a DBContext, and I want to make it a custom ViewModel object
my ViewModel receives as parameter an object obtained from the DBContext to work the information and return it completely
This is a little example
public class Obj1 // Object i get from database
{
public int id { get; set; }
public string Param { get; set; }
public string Param2 { get; set; }
public string Random { get; set; }
}
public class Obj2 //ViewModel
{
public string ParamFormateado { get; set; }
public string Random { get; set; }
public Obj2(Obj1 parametro)
{
ParamFormateado = parametro.Param + parametro.Param2;
Random = parametro.Random;
}
}
What I'm trying to do is get an Obj2 with a Linq expression who returns an Obj1 without transforming the information in the linq expression, since in my case it becomes a basically illegible expression
I was try something like this
Obj2 objeto = db.Obj1.Where(x => x.id == "0").Select(x => new Obj2(x)).FirstOrDefault();
Is it possible to perform a Linq query similar to the one I am proposing? since otherwise, I end up having extremely long Linq expressions to format this information, but what would be the best alternative in these cases?
You can't do that because only parameterless constructors are supported. But you can do it with Linq-To-Objects which can be forced with AsEnumerable:
Obj2 objeto = db.Obj1
.Where(x => x.id == "0")
.AsEnumerable() // <--- here
.Select(x => new Obj2(x))
.FirstOrDefault();
So only the filter with Where will be executed in the database, the remaining record(s) are processed in-process.
https://codeblog.jonskeet.uk/2011/01/14/reimplementing-linq-to-objects-part-36-asenumerable/
Dont do it with Linq like that. you have to create a method that takes obj1 as parameter, maps properties and then returns obj2. Or use Automapper from nuget repository.
do it like this
public obj2 Map(obj1 source)
{
var destination = new obj2();
destination.param1 = source.param1;
//
return destination;
}
if you want to pass a collection of objects then do just that and just foreach through the list and return a list of mapped objects. But i would advise you to use Automapper since it automates the proces and you dont have to write a long mapping code.

How to make expression treat value type as a reference type?

I wanted to store a collection of expressions accessing object's properties. For example:
class Entity
{
public int Id { get; set; }
public Entity Parent { get; set; }
public string Name { get; set; }
public DateTime Date { get; set; }
public decimal Value { get; set; }
public bool Active { get; set; }
}
static void Main(string[] args)
{
var list = new List<Expression<Func<Entity, object>>>();
list.Add(e => e.Id);
list.Add(e => e.Name);
list.Add(e => e.Parent);
list.Add(e => e.Date);
list.Add(e => e.Value);
list.Add(e => e.Active);
StringBuilder b = new StringBuilder();
list.ForEach(f => b.AppendLine(f.ToString()));
Console.WriteLine(b.ToString());
Console.ReadLine();
}
This code outputs:
e => Convert(e.Id)
e => e.Name
e => e.Parent
e => Convert(e.Date)
e => Convert(e.Value)
e => Convert(e.Active)
It does add Convert to value types.
As far as in the end I wanted to use those expressions with LINQ to SQL, I need not to have that Convert in expressions, for them to be successfully translated to SQL.
How can I achieve this?
P.S.: expressions from this collection are later used as arguments to OrderBy and ThenBy methods.
If you create a function generic in the proeprty type you can avoid the Convert:
private static LambdaExpression GetExpression<TProp>
(Expression<Func<Entity, TProp>> expr)
{
return expr;
}
then you can change the type of list:
var list = new List<LambdaExpression>();
list.Add(GetExpression(e => e.Id));
list.Add(GetExpression(e => e.Name));
This will require you to create your OrderBy and ThenBy expressions using reflection e.g.
LambdaExpression idExpr = list[0];
Type keyType = idExpr.ReturnType;
var orderByMethod = typeof(Queryable).GetMethods()
.Single(m => m.Name == "OrderBy" && m.GetParameters().Length == 2)
.MakeGenericMethod(typeof(Entity), keyType);
var ordered = (IQueryable<Entity>)
orderByMethod.Invoke(null, new object[] { source, idExpr });
I patched up a EF code first attempt at using your code like this
public class Entity
{
public int Id { get; set; }
public Entity Parent { get; set; }
public string Name { get; set; }
public DateTime Date { get; set; }
public decimal Value { get; set; }
public bool Active { get; set; }
}
public class EntityContext : DbContext
{
public EntityContext()
: base(new SqlCeConnection("Data Source=Database.sdf;Persist Security Info=False;"),
contextOwnsConnection: true)
{
// Using a SQL Compact database as backend
}
public DbSet<Entity> Entities { get; set; }
}
and attempted some linq on the context
static void Main(string[] args)
{
var list = new List<Expression<Func<Entity, object>>>();
list.Add(e => e.Date);
list.Add(e => e.Name);
using (var c = new EntityContext())
{
//each time a new record is added
var data = new Entity
{
Name = string.Format("Data{0}", c.Entities.Count()),
Date = DateTime.Now
};
c.Entities.Add(data);
c.SaveChanges();
// sort by date
foreach (var e in c.Entities.OrderBy(list.First().Compile()))
Console.WriteLine(string.Format("{0} - {1}", e.Name, e.Date));
// sort by name .. in reverse
foreach (var e in c.Entities.OrderByDescending(list.Last().Compile()))
Console.WriteLine(string.Format("{0} - {1}", e.Name, e.Date));
}
Console.ReadLine();
}
There were no issues running the code.
UPDATE The same holds true for LINQ to SQL: I built a table in a local SQL Server with the same structure as the class, and tried to OrderBy it : no problem.
My answer is "You don't need to worry about that".
Thank's to the answer by Alex I found out for myself that, when ordering the data I can use two different methods, depending on the specified argument:
Queryable.OrderBy Method with Expression<Func<TSource, TKey>>
Enumerable.OrderBy Method with Func<TSource, TKey>
When Queryable.OrderBy is used, LINQ compiles the OrderBy clause into the SQL statement, executed over the database. So when I try to give it a Expression<Func<TEntity, object>> that looks like e => Convert(e.Field), LINQ throws an InvalidOperationException, saying Cannot order by type 'System.Object'.
When Enumerable.OrderBy is used, LINQ does not compile the OrderBy clause into the SQL query, but executes the current query and applies sorting on the enumerable of entities, returned by the query, in the program's memory. Here no problem with ordering by Func<TEntity, object>.
So I found two alternatives here:
Query the database without sorting and order the returned result set
Provide better expressions to LINQ, that it could compile the SQL query, and then apply sorting in the database layer; here the answer by Lee suggests one way..
In my exact case sorting is the last operation to execute, and I don't see much harm, if I order the result set in the programm's memory...I'm not going to expect huge amounts of data to be returned...
Though in a more common case, probably it's still better to do all possible operations in the database layer...
P.S.: SO: Order a linq query - a close discussion...

Compare two generic lists and remove duplicates

I have two generic Lists, one called "Featured" and the other called "Filtered".
List<Content> Featured = new List<Content>();
List<Content> Filtered = new List<Content>();
Both contain "Content" items which are simple classes like so :
public class Content
{
public long ContentID { get; set;}
public string Title { get; set; }
public string Url { get; set; }
public string Image { get; set; }
public string Teaser { get; set; }
public Content(long contentId, string title, string url, string image, string teaser)
{
ContentID = contentId;
Title = title;
Url = url;
Image = image;
}
}
Any items that appear in "Filtered" but also appear in "Featured" need to be removed from "Filtered". Additionally, both lists will then be combined into a single generic list with the "Featured" items appearing first.
I know I could write a couple of foreach loops to do this but I can't help feel there must be a more elegant method using LINQ.
I am using C# 4.0.
If you have an IEqualityComparer defined you can use the Union method:
List<Content> FeaturedAndFiltered = Featured.Union(Filtered, new MyContentComparer());
A rough implementation of MyContentComparer would be:
public class ContentEqualityComparer : IEqualityComparer<Content>
{
public bool Equals(Content c1, Content c2)
{
return (c1.ContentID == c2.ContentID);
}
public int GetHashCode(Content c)
{
return c.ContentID.GetHashCode();
}
}
You're looking for the LINQ method Union, specifically
var singleList = Featured.Union(Filtered);
This will return all the Featured items, followed by all the Filtered items that were not Featured. Note that it will also remove any duplicates within a list - so if an item is Featured twice, it will only show up once.
You do, however, need to be able to compare instances of Content in order to do this, either by adding in implementations of Equal and GetHashCode or by providing an IEqualityComparer.
Assuming the objects presenter in both lists are the actual same objects, you can do the following:
var totalList = Featured
.Concat(Filtered.Where(f => !Featured.Contains(f)))
.ToList()
Update
Or, using the Except method as mentioned by Mahmoud Gamal:
var totalList = Featured
.Concat(Filtered.Except(Featured))
.ToList()

Cannot serialize interface System.Linq.IQueryable

I'm faced with an error, "Cannot serialize interface System.Linq.IQueryable." when I try to run my method in my web service. My class is as such:
public class AirlineSearchStrong
{
public Flight_Schedule flightSchedule { get; set; }
public Flight_Schedule_Seats_and_Price flightScheduleAndPrices { get; set; }
public Airline airline { get; set; }
public Travel_Class_Capacity travelClassCapacity { get; set; }
}
[WebMethod]
public IQueryable SearchFlight(string dep_Date, string dep_Airport, string arr_Airport, int no_Of_Seats)
{
AirlineLinqDataContext db = new AirlineLinqDataContext();
var query = (from fs in db.Flight_Schedules
join fssp in db.Flight_Schedule_Seats_and_Prices on fs.flight_number equals fssp.flight_number
join al in db.Airlines on fs.airline_code equals al.airline_code
join altc in db.Travel_Class_Capacities on al.aircraft_type_code equals altc.aircraft_type_code
where fs.departure_date == Convert.ToDateTime(dep_Date)
where fs.origin_airport_code == dep_Airport
where fs.destination_airport_code == arr_Airport
where altc.seat_capacity - fssp.seats_taken >= no_Of_Seats
select new AirlineSearchStrong {
flightSchedule = fs,
flightScheduleAndPrices = fssp,
airline = al,
travelClassCapacity = altc
});
return query;
}
I've tried IQueryable, IList and returning .ToList() but most of it has turned out to be unsuccessful
i dont think
you can use Iqueryable or Ienumerable as they both do lazy execution and are not serializable. The query gets executed only when you iterate through the collection.so it doesn't make sense to return the query to the caller and asking him to iterate as his end.you need to pass a List or an Array.
You may need to change the return type to List<Type>
Hows about
public IEnumerable<AirlineSearchStrong> SearchFlight(string dep_Date, string dep_Airport, string arr_Airport, int no_Of_Seats)
{
...
return query.ToList();
}
Your trying to serialize a representation of the data, the linq query itself, instead of the data resulting from executing the query, thats why it isnt working.
You need to enumerate the linq query into an enumerable set, and serialize that.
AirlineSearchStrong might need to be marked [Serializable()]

Linq Query to IEnumerable<T> Extension Method

Consider this,
class Item
{
public string ID { get; set;}
public string Description { get; set; }
}
class SaleItem
{
public string ID { get; set;}
public string Discount { get; set; }
}
var itemsToRemoved = (List<Item>)ViewState["ItemsToRemove"];
// get only rows of ID
var query = from i in itemsToRemoved select i.ID;
var saleItems= (List<SaleItem>)ViewState["SaleItems"];
foreach (string s in query.ToArray())
{
saleItems.RemoveItem(s);
}
How can I write this LINQ phrase using IEnumerable/List Extension methods
// get only rows of ID
var query = from i in items select i.ID;
thanks in advance.
That one's easy:
var query = items.Select(i => i.ID);
A select clause always corresponds to a call to Select. Some of the other operators end up with a rather more complex expansion :) If you work hard, you can get the compiler to do some very odd stuff...
You can find all the details of this and other query expression translations in section 7.16 of the C# specification (v3 or v4).
<plug>
You could also buy C# in Depth, 2nd edition and read chapter 11 if you really wanted to :)</plug>
You can use this:
var query = items.Select(i => i.ID);
A couple of other points:
Here you don't need the call to ToArray:
foreach (string s in query.ToArray())
Also if your list is large and you are removing a lot of items you may want to use List.RemoveAll instead of iterating. Every time you remove an item from a list all the other items after it have to be moved to fill the gap. If you use RemoveAll this only has to be done once at the end, instead of once for every removed item.
List<Item> itemsToRemove = (List<Item>)ViewState["ItemsToRemove"];
HashSet<string> itemIds = new HashSet<string>(itemsToRemove.Select(s => s.ID));
saleItems.RemoveAll(c => itemIds.Contains(c.ID));
public static class ItemCollectionExtensions
{
public static IEnumerable<int> GetItemIds(this List<Item> list)
{
return list.Select(i => i.ID);
}
}

Categories