LINQ Group by column name passed as parameter - LINQ TO SQL - c#

Consider the LINQ code :
var objs = (ClientsDB.Context.ClientProcessed.GroupBy(grp => new
{
City = grp.City,
Day = grp.Insert_Date.Value.Day,
Month = grp.Insert_Date.Value.Month,
Year = grp.Insert_Date.Value.Year
})....
var objs = (ClientsDB.Context.ClientProcessed.GroupBy(grp => new
{
Department = grp.Department,
Day = grp.Insert_Date.Value.Day,
Month = grp.Insert_Date.Value.Month,
Year = grp.Insert_Date.Value.Year
})....
var objs = (ClientsDB.Context.ClientProcessed.GroupBy(grp => new
{
State = grp.State,
Day = grp.Insert_Date.Value.Day,
Month = grp.Insert_Date.Value.Month,
Year = grp.Insert_Date.Value.Year
})....
How can I have GroupBy done by a specific column passed as a string ?
For example :
String grpByParam = "City"; // grpByParam = "State"; // String grpByParam = "Department";
And then pass that param to a linq query , instead of duplicating LINQ queries every now and again .
FYI , I need that as LINQ-TO-SQL and not LINQ-TO-OBJECTS query , meaning I want the filtering to be done in the DB , and not in the memory .
Is that possible ?
Thanks

You can check the string value and call GroupBy based on that
if (grpByParam.Equals("City")) {
result = source.GroupBy(a => a.City);
} ...
You can implement a method which takes a source (the LINQ query without the GroupBy) and return a result (the result of GroupBy)
See
This method is implemented by using deferred execution. The immediate
return value is an object that stores all the information that is
required to perform the action. The query represented by this method
is not executed until the object is enumerated either by calling its
GetEnumerator method directly or by using foreach in Visual C# or For
Each in Visual Basic.
in the docs.

Related

EF Core & LINQ - How to build a dynamic Where clause using a string variable at run time? [duplicate]

This question already has answers here:
Creating dynamic queries with entity framework
(4 answers)
Closed 3 years ago.
I've read many different variations to this question and I cannot believe the solution I need is so complicated that warrants using additional libraries and crazy tricks, hopefully not!
At runtime, the LINQ query in my project needs to change dynamically depending on how many columns in the DB table the user wants to filter by. In my example, I first show a working LINQ Query which is hard coded.
The next example uses a list that is built at runtime, all i need to figure out is how to insert the string variable (whereClause) into the LINQ Query without compile errors?
Working example (hard coded)
logs = _context.Logs.Where(s => s.Level == LogLevel & s.LogEventCategory == EventCategory)
.Select(s => new Logs()
{
TimeStamp = s.TimeStamp,
Level = s.Level,
Exception = s.Exception,
LogEventCategory = s.LogEventCategory,
LogEventType = s.LogEventType,
LogEventSource = s.LogEventSource,
LogEventName = s.LogEventName,
LogUserName = s.LogUserName,
LogForename = s.LogForename,
LogSurname = s.LogSurname,
LogData = s.LogData
});
Example Two - The solution I want to fix and use...
First create a list, the contents of the list will change each time a new query is run, strings passed as variables through the parent OnGet method will either contain a value and be used in the string join concatenation, or will be null and therefore not added to the list and used in the concatenation.
Second example is where I get compilation errors.
var filtersList = new List<string>();
if (LogLevel != null)
{
filtersList.Add("s.LogLevel == LogLevel");
}
if (EventCategory != null)
{
filtersList.Add("s.EventCategory == EventCategory");
}
var whereClause = string.Join(" & ", filtersList.ToArray());
logs = _context.Logs.Where(s => whereClause) // *HERE I WANT TO USE THE STRING VARIABLE! not working
.Select(s => new Logs()
{
TimeStamp = s.TimeStamp,
Level = s.Level,
Exception = s.Exception,
LogEventCategory = s.LogEventCategory,
LogEventType = s.LogEventType,
LogEventSource = s.LogEventSource,
LogEventName = s.LogEventName,
LogUserName = s.LogUserName,
LogForename = s.LogForename,
LogSurname = s.LogSurname,
LogData = s.LogData
});
The error I get says 'Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type' blah blah blah
You just add the .Where() calls to the end of your query, before materializing:
query = _context.Logs.Select(s => new ..);
if (EventCategory != null) query = query.Where(e => e.EventCategory == EventCategory);
if (LogLevel != null) query = query.Where(e => e.LogLevel == LogLevel);
var items = query.ToList();

MVC 5 - Why is my linq code selecting an entire query instead of a single value?

Here is an example of what some locations look like in my database:
ID, DepartmentId, LocationName
8,2,Main Warehouse
12,2, Filter Cellar
When I use the following code, it grabs the entire query and puts it as the value for my HTML dropdown list.
public ActionResult GetLocations(int id)
{
List<SelectListItem> locations = new List<SelectListItem>();
//based on the input coming to this method ( product id)
var incident = new AccidentSupervisorViewModel();
incident.Locations = DB.Locations.ToList().Select(i => new SelectListItem
{
Text = i.LocationName,
Value = i.DepartmentId.ToString()
}
).Where(i => i.Value == id.ToString());
var departmentId = from loc in DB.Locations where loc.DepartmentId == id select loc.DepartmentId;
for (var x = 0; x < incident.Locations.Count(); x++) {
locations.Add(new SelectListItem {
Text = incident.Locations.ElementAt(x).Text,
Value = departmentId.ToString()
});
}
return Json(locations,JsonRequestBehavior.AllowGet);
}
This is most likely happening because I have a syntax error, but I haven't used linq much for queries so any help is appreciated.
It appears you've not 'done' anything with the IQueryable<T> that is generated by Linq. Linq generates the query but doesn't do anything with it until the IQueryable<T> or other IEnumerable is iterated over. See Deferred Execution and Classification of Standard Query Operators by Manner of Execution.
In your case, since you're looking for a single value, you'll need to pop in the following line after you first declare departmentId:
var department = departmentId.FirstOrDefault();
This will pop out the first or default value from the IQueryable<T> you made.

Entity Framework - Using Linq to Invoke Field Name

I am using net 4.5, C# and Entity Framework. I want to be able to invoke a Field at runtime for a linq query.
using var( context = new SomeDataEntities())
{
var abc = from b in context.someTable
where b.SomeField == 1
select b.AnotherField;
}
However I am want to invoke b.SomeField based on a input string parameter.
my current code is
using var( context = new SomeDataEntities())
{
var abc = from b in context.someTable
where b.GetType().GetProperty("SomeField").GetValue(b, null).ToString() == "test"
select b.AnotherField;
}
If you want to dynamically create a where clause, the best way to do this is the method chaining instead of the linq format. For instance:
using (var context = new SomeDataEntities())
{
var query = context.Set<SomeTable>();
if (field1.HasValue)
{
query = query.Where(e => e.Field1 == field1.Value);
}
if (field2.HasValue)
{
query = query.Where(e => e.Field2 == field2.Value);
}
var abc = query.Select(b => b.AnotherField);
}
If you have a lot of fields or an unknown number, SQL generation might be your best strategy and can be accessed with the DbContext.Database.SqlQuery() methods.

Is there an "Explain Query" for MongoDB Linq?

Is there a way to run .explain() or equivalent on Linq queries? I would want to know
The text of the actual JSON query
The output of .explain() (indexes used, etc)
It would also be nice to have the execution time of the query
You can get the Json easily enough if you have a query wrapper;
var qLinq = Query<T>.Where(x => x.name=="jim");
Console.WriteLine(qLinq.ToJson());
There's also an Explain() method on MongoCursor, so you could do this;
var exp = Collection.FindAs<T>(qLinq).Explain()
Console.WriteLine(exp.ToJson());
So if you want the time taken, "millis" is in there;
var msTaken = exp.First(x => x.Name == "millis").Value.AsInt32;
If you have an IQueryable, try something like this;
void Do(MongoCollection col, IQueryable iq)
{
// Json Mongo Query
var imq = (iq as MongoQueryable<Blob>).GetMongoQuery();
Console.WriteLine(imq.ToString());
// you could also just do;
// var cursor = col.FindAs(typeof(Blob), imq);
var cursor = MongoCursor.Create(typeof(Blob), col, imq, ReadPreference.Nearest);
var explainDoc = cursor.Explain();
Console.WriteLine(explainDoc);
}//Do()
If you want this functionality in a library, I just created a GitHub project entitled
MongoDB query helper for .NET
https://github.com/mikeckennedy/mongodb-query-helper-for-dotnet
It will:
Explain a LINQ query as a strongly typed object (does it use an index for example)
Convert a LINQ query to the JavaScript code run in MongoDB
Check it out and contribute if you find it interesting.
Yes, there is. It shows everything .explain does and has a boolean for verbosity (it includes the time it took to execute):
var database = new MongoClient().GetServer().GetDatabase("db");
var collection = database.GetCollection<Hamster>("Hamsters");
var explanation = collection.AsQueryable().Where(hamster => hamster.Name == "bar").Explain(true);
Console.WriteLine(explanation);
It doesn't show the query though. Here's an extension method for that:
public static string GetMongoQuery<TItem>(this IQueryable<TItem> query)
{
var mongoQuery = query as MongoQueryable<TItem>;
return mongoQuery == null ? null : mongoQuery.GetMongoQuery().ToString();
}
Usage:
var query = collection.AsQueryable().Where(hamster => hamster.Name == "bar").GetMongoQuery();
Console.WriteLine(query);
In mongodb 3 C# I used following:
var users = Mongo.db.GetCollection<User>("Users");
var r = users(m => m._id == yourIdHere)
.Project(m => new { m._id, m.UserName, m.FirstName, m.LastName })
.Limit(1);
Console.WriteLine(r.ToString());
Result:
find({ "_id" : ObjectId("56030e87ca42192008ed0955") }, { "_id" : 1, "UserName" : 1, "FirstName" : 1, "LastName" : 1 }).limit(1)

LINQ query with SELECT and two GROUP-BY condition

What's the equivalent LINQ instruction for a Datatable of the following SQL query:
SELECT code_direction, count(TP) AS CN
FROM table1
WHERE cod_time = 'A011'
GROUP BY TP,code_direction;
and how to get the result into a new datatable?
I tried to convert it but I there're some errors. Someone could take a look on this:
var query = from t in table1.AsEnumerable()
group t by new { t.TP, t.code_direction }
into grp
select new
{
grp.Key.code_direction,
CN = grp.Count(t.TP)
};
foreach (var x in query)
{
Console.Write(x.code_direction);
Console.Write(x.CN);
}
As far as your first question goes. The LINQ equivalent of the SQL query is:
var query = from t in table1.AsEnumerable()
where t.cod_time == "A011"
group t by new { t.TP, t.code_direction }
into grp
select new
{
grp.Key.code_direction,
CN = grp.Count()
};
Note that you don't have to pass any argument to grp.Count(). (For the obvious reason that in SQL COUNT(TP) is the same as COUNT(*), i.e. just count the number of rows. The story would be different if you'd use COUNT(DISTINCT TP) or similar.)
As far as the second question goes, if your query just returned an IEnumerable<T> where T is DataRow (i.e. a query like table1.AsEnumerable().Where(r => r.cod_time == "A011")) then you could just the DataTableExtensions.CopyToDataTable extension method. As your query returns an anonymous type however, you will have to follow these instructions found on MSDN.
I Have been using LINQ to work on a JSON object returned from a remote sharepoint web service. I have posted this because most of the answers I found online were slightly different from what I needed.
a json list of daily activities is returned from a remote sharepoint list & is then summarised using LINQ
The simplified version of a custom object definition is shown below( & which is defined in the models area of an MVC application)
public class MyCustomObjectList
{
public string eventdate { get; set; }
public string userid { get; set; }
public string action { get; set; }
}
The JSON object is serialised into a MyCustomObjectList array.
var customobject = serializer.Deserialize<MyCustomObjectList>(jsonobject);
I wanted to work out how many actions of each type happened on a given day. NB eventdate is stored as a string in format yyyy-mm-dd hh:MM:ss. This was to simplify conversions between c#, JSON & Jquery ( where required I create DateTime objects elsewhere in the code using the
eventdate.
Some will argue this is inefficient, but I prefer to split processes into a sequential set of really simple operations, for the sake of easier debugging & to help other people follow my code. Thats why there are 2 Linq queries .
querya strips out the time component from the eventdate This ensures our later grouping happens by day, & not by second. To be doubly sure that there is no caching, I create it in a new field called actionday. I also rename action to activity, because intellisense was getting confused!! The other columns are copied as is.
var querya =
from c in customobject.rows
select new { actionday = c.eventdate.Substring(0, 10), activity = c.action, c.userid,
c.eventdate };
/* queryb produces a grouped count of querya, grouped on actionday & activity, creating new columns actionkey,ActionCount,Dte,action & DetailList ( which is a summary for debugging purposes)
*/
var queryb=
from p in querya group p by new { p.actionday, p.activity} into idGroup
actionkey = idGroup.Key,
ActionCount = idGroup.Count(),
Dte = idGroup.Key.actionday,
action = idGroup.Key.activity,
DetailList = idGroup
};
Here’s a version that sumarises by 3 columns
var queryc = from p in querya
group p by new { p.actionday, p.userid, p.activity} into idGroup
select new
{
actionday = idGroup.Key,
ActionCount = idGroup.Count(),
userid = idGroup.Key.userid,
Dte = idGroup.Key.actionday,
action = idGroup.Key.activity,
DetailList = idGroup
};

Categories