Concat Two IQueryables with Anonymous Types? - c#

I've been wrestling with this a little while and it's starting to look like it may not be possible.
I want to Concat() two IQueryables and then execute the result as a single query. I tried something like this:
var query = from x in ...
select new
{
A = ...
B = ...
C = ...
};
var query2 = from y in ...
select new
{
A = ...
B = ...
C = ...
};
var query3 = query.Concat(query2);
However, the last line gives me the following error:
'System.Linq.IQueryable' does not contain a definition for 'Concat' and the best extension method overload 'System.Linq.ParallelEnumerable.Concat(System.Linq.ParallelQuery, System.Collections.Generic.IEnumerable)' has some invalid arguments
It appears it's expecting an IEnumerable for the argument. Is there any way around this?
It looks like I could resolve both queries to IEnumerables and then Concat() them. But it would be more efficient to create a single query, and it seems like that should be possible.

As you said previously in the comments, it seems that the two queries return different objects:
Query 1 (as per comment):
f__AnonymousTypee<Leo.Domain.FileItem,Leo.Domain.Employ‌​ee,int,string,string>
Query2 is
f__AnonymousTypee<Leo.Domain.FileItem,L‌​eo.Domain.Employee,int?,string,string>
This is why Concat is giving you an error message complaining about invalid arguments.

Anonymous objects will be equivalent types to other anonymous objects with the same property names and types declared in exactly the same order.
Assuming both query and query2 from from the same contexts, you should be able to combine the two, provided they are queries of equivalent types.
Your comment indicates that neither are of the same type.
query returns objects of type Anon<FileItem, Employee, int, string, string>
query2 returns objects of type Anon<FileItem, Employee, int?, string, string>.
You cannot combine the two because they are of different types. You'll need to make sure that both queries return objects of the same type.
var query = from x in ...
select new
{
A = (FileItem)...
B = (Employee)...
C = (int)...
...
};
var query2 = from y in ...
select new
{
A = (FileItem)...
B = (Employee)...
C = (int)...
...
};

The IDE determined query and query2 are of different types, while the IEnumerable<TSource> Concat<TSource>() extension method expects two same types (IEnumerable<TSource>). The three TSource's must be the same.
string[] strA = {"123", "234", "345"};
int[] intA = { 1, 2, 3 };
var query = from s in strA
select s;
var query2 = from i in strA // intA
select i;
var query3 = query.Concat(query2);
Uncomment "// intA" in VS and you'll see the difference.

Are you missing any namespace? Normally I mark my .NET Project Properties to target .net 4.0 for vs 2010. I do not use .net 4.0 Client Profile.
Please make sure that the types of A, B and C is matches in both the query anonymous types. Also the order of A, B and C should also match in both queries.
The following example works like a charm.
namespace Test
{
using System;
using System.Collections.Generic;
using System.Linq;
internal class Employee
{
public string Name { get; set; }
public int Age { get; set; }
public double Salary { get; set; }
public string Address { get; set; }
}
internal class Program
{
private static List<Employee> employees = new List<Employee>();
private static void BuildList()
{
employees.AddRange(
new Employee[]
{
new Employee() {Name = "Tom", Age = 22, Address = "sample1", Salary = 10000},
new Employee() {Name = "Mithun", Age = 27, Address = "sample1", Salary = 20000},
new Employee() {Name = "Jasubhai", Age = 24, Address = "sample1", Salary = 12000},
new Employee() {Name = "Vinod", Age = 34, Address = "sample1", Salary = 30000},
new Employee() {Name = "Iqbal", Age = 52, Address = "sample1", Salary = 50000},
new Employee() {Name = "Gurpreet", Age = 22, Address = "sample1", Salary = 10000},
}
);
}
private static void Main(string[] args)
{
BuildList();
var query = from employee in employees
where employee.Age < 27
select new
{
A = employee.Name,
B = employee.Age,
C = employee.Salary
};
var query2 = from employee in employees
where employee.Age > 27
select new
{
A = employee.Name,
B = employee.Age,
C = employee.Salary
};
var result = query.Concat(query2);
foreach (dynamic item in result.ToArray())
{
Console.WriteLine("Name = {0}, Age = {1}, Salary = {2}", item.A, item.B, item.C);
}
}
}
}

Related

JSON File and LINQ Sum and Group

Could I please get some help with querying from a JSON file? Populating a datagrid view works just fine but what I am trying to do now is filter the data using LINQ which I'm really struggling with.
This works just fine, populating the datagridview with all of my jsonfile data
//dataGridView1.DataSource = (from p in movie2
// select p).ToArray();
Below is what I have been playing around with. When I group by employee ID into g, I can not longer use my p references to fields.
using (StreamReader file = File.OpenText(#"C:\temp\GRMReportingJSONfiles\Assigned_FTE\" + myFile))
{
JsonSerializer serializer = new JsonSerializer();
IEnumerable<AssgnData> movie2 = (IEnumerable<AssgnData>)serializer.Deserialize(file, typeof(IEnumerable<AssgnData>));
dataGridView1.DataSource = (from p in movie2
group p by p.EMPLID[0] into g
select new {
EMPLID = p.EMPLID,
(decimal?)decimal.Parse(p.MNTH1) ?? 0).Sum(),
};
);
//dataGridView1.DataSource = (from p in movie2
// select Int32.Parse(p.MNTH1).Sum();
dataGridView1.DataSource = (from p in movie2
group p by p.EMPLID[0] into g
select (decimal?)decimal.Parse(p.MNTH1) ?? 0).Sum(); //dataGridView1.DataSource = (from p in movie2
// select p).ToArray();
//where p.Resource_BU == "7000776"
//chart1.DataBindCrossTable(movie2, "MNTH1", "1", "PROJECT_ID", "Label = FTE");
//chart1.Refresh();
}
Here is part of the array layout, removed other fields for now as I was just trying to focus on these two, dataset has 100k rows and 50 columns
public class AssgnData
{
public string EMPLID { get; set; }
public string MNTH1 { get; set; }
}
In my opinion, using Fluent Syntax usually makes it a bit easier to understand what is going wrong here.
As soon as you group your data you are no longer working on the individual objects, but on a 'group', which is the key and an enumerable of objects.
Getting the sum per employee should then be grouping by the full employee id and then parsing the MNTH1 fields of your objects and summing them.
dataGridView1.DataSource = movie2
.GroupBy(p => p.EMPLID) // create a group of data per employee
.Select(g => new
{
EMPLID = g.Key, // the employee id is the group key
Sum = g.Sum(data => decimal.Parse(data.MNTH1)) // parse and sum
})
.ToArray();
Edit: you are right, you need the ToArray to evaluate the query. I just verified on my computer and it works.
Try following :
class Program
{
static void Main(string[] args)
{
IEnumerable<AssgnData> movie2 = null;
dataGridView1.DataSource = movie2.GroupBy(x => new {id = x.EMPLID, month = x.MNTH1})
.Select(x => new {
EMPLYID = x.Key.id,
MONTH = x.Key.month,
SUM = x.Sum(y => y.value)
});
}
}
public class AssgnData
{
public string EMPLID { get; set; }
public string MNTH1 { get; set; }
public int value { get;set;}
}

many to many select list data

I would like to list the data with linq by combining the tables shown in the picture
expected result
using (CrmContext db = new Models.CrmContext())
{
_result = (from ct in db.CustomerDefination
join authorizedin db.authorized
on ct.customerId equals authorized.authorized
join authorizedDefination in db.authorizedDefination
on authorized.authorizedId equals authorizedDefination .authorizedId
select new
{
///I want to list the authorities belonging to
the customer
}).ToList();
}
There may be more than one authoritative definition of a customer. How can I do this in the LINQ query?
I spent WAY too much time doing your job, #IbrahimALPSOY:
Google Translating the turkish database screenshot,
Understanding which table holds what data using more Google Translate,
Understanding the expected result - which fields come from which tables,
Writing sample classes to represent the database,
Generating sample data for testing.
I wasted 30+ minutes before even starting to write a query. Next time I won't. Next time, you prepare your question such that, all other people could just copy your code and try queries right away.
This is the preparation:
class Authorisation
{
public int AuthorisationId; // yetkiliid
public int AccessId; // unvanid
public string AuthoriserName;
}
class Access
{
public int AccessId; // unvanid
public string AccessName;
}
class Customer
{
public int CustomerId; // musterid
public string CustomerName;
}
class Event
{
public int CustomerId;
public int AuthorisationId;
}
void Main()
{
var Customers = new[] {
new Customer() { CustomerId = 1, CustomerName = "Anne" },
new Customer() { CustomerId = 2, CustomerName = "Barbara" },
};
var Accesses = new[] {
new Access() { AccessId = 1, AccessName = "Read" },
new Access() { AccessId = 2, AccessName = "Write" },
};
var Authorisations = new[] {
new Authorisation() { AuthorisationId = 1, AuthoriserName = "The boss", AccessId = 1 }, // The boss can give read rights
new Authorisation() { AuthorisationId = 2, AuthoriserName = "The boss", AccessId = 2 }, // The boss can give write rights
new Authorisation() { AuthorisationId = 3, AuthoriserName = "A rookie", AccessId = 1 }, // A new employee can only give read rights
};
var Events = new[] {
new Event() { CustomerId = 1, AuthorisationId = 3 }, // A rookie let Anne read
new Event() { CustomerId = 1, AuthorisationId = 2 }, // While the boss let Anne write and scolded rookie
new Event() { CustomerId = 2, AuthorisationId = 1 }, // The boss thinks Barbara can't be trusted with write
};
}
I used this code instead of yours, because yours:
doesn't compile,
is illegible,
is badly formatted,
skips a table you've shown on your screenshot,
contains references to contexts only you have access to.
And here are the results:
Your query becomes feasible if you start with the table with non-unique keys:
from e in Events
join c in Customers on e.CustomerId equals c.CustomerId
join a in Authorisations on e.AuthorisationId equals a.AuthorisationId
join s in Accesses on a.AccessId equals s.AccessId
select new
{
e.CustomerId,
e.AuthorisationId,
c.CustomerName,
a.AuthoriserName,
s.AccessName
}
If this is not what you needed, modify my "preparation" to fit your question.

c# dapper error when parameter is list<int> No mapping exists from object type <>f__AnonymousType20`1[[System.Int32[]

string sqlQuery = "SELECT SellingPrice, MarkupPercent, MarkupAmount FROM ProfitMargins WHERE QuoteId in #QuoteId";
var profitMargin = await ctx.Database.SqlQuery<dynamic>(sqlQuery,
new { QuoteId = new[] { 1, 2, 3, 4, 5 } }
//String.Join(", ", QuoteIds.ToArray()))).ToListAsync();
can someone see what I am doing wrong?
No mapping exists from object type
<>f__AnonymousType20`1[[System.Int32[], mscorlib, Version=4.0.0.0,
Culture=neutral, PublicKeyToken=]] to a known managed provider native
type.
I got this idea from this post: SELECT * FROM X WHERE id IN (…) with Dapper ORM answered by: #LukeH
UPDATE:
I need it returned into a list . see my whole function, I've changed the code according to the answer posted by #JFM, but now cannot add .ToListAsync
#JFM
public static async Task<List<dynamic>> GetProfitMargin(List<int> QuoteIds)
{
using (var conn = new SqlConnection(new MYContext().Database.Connection.ConnectionString))
{
string sqlQuery = "SELECT SellingPrice, MarkupPercent, MarkupAmount FROM ProfitMargins WHERE QuoteId in #QuoteId";
{
var profitMargin = conn.Query<dynamic>(sqlQuery
, new { QuoteId = new[] { 1, 2, 3, 4, 5 } }).ToListAsync());
}
public static async Task<IEnumerable<dynamic>> GetProfitMargin(List<int> QuoteIds)
{
using (var conn = new SqlConnection(new MYContext().Database.Connection.ConnectionString))
{
string sqlQuery = "SELECT SellingPrice, MarkupPercent, MarkupAmount FROM ProfitMargins WHERE QuoteId in #QuoteId";
{
IEnumerable<dynamic> profitMargin = await conn.QueryAsync<dynamic>(sqlQuery
, new { QuoteId = new[] { 1, 2, 3, 4, 5 } });
}
If you don't map it to a list or array, by default it will be an IEnuerable.
Using Dapper to query and map dynamic works well for me:
string sqlQuery = "SELECT SellingPrice, MarkupPercent, MarkupAmount FROM ProfitMargins WHERE QuoteId in #QuoteId";
using(var conn = new SqlConnection(myConnString)
{
var profitMargin = conn.Query<dynamic>(sqlQuery
, new { QuoteId = new[] { 1, 2, 3, 4, 5 } });
}
Not 100% sure what the issue is, but below is an example of how you can work with Dynamics:
[Test]
public void TestDynamicsTest()
{
var query = #"select 1 as 'Foo', 2 as 'Bar' union all select 3 as 'Foo', 4 as 'Bar'";
var result = _connection.Query<dynamic>(query);
Assert.That(result.Count(), Is.EqualTo(2));
Assert.True(result.Select(x => x.Foo == 1).First());
Assert.True(result.Select(x => x.Bar == 4).Last());
}
Update
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
var query = #"select 1 as 'Id', 'Foo' as 'Name' union all select 1 as 'Id', 'Bar' as 'Name'";
var result = _connection.Query<Person>(query);
foreach (var person in result)
{
var output = string.Format("Id: {0}, Name: {1}", person.Id, person.Name);
}
For more examples, check out Dapper docs.

Concatenate Int to String in LINQ Query

I have the following LINQ query.
var providers = from c in Repository.Query<Company>()
where !c.IsDeleted
select new { c.Description, Id = "C" + c.Id };
I'm trying to concatenate the ID to "C". So, for example, if c.Id is 35 then the result should be "C35".
This obviously doesn't work because you can't add an integer (c.Id) to a string. I could easily resolve this in C# using string.Format() or converting the type. But how can I do this in LINQ?
Try using SqlFunctions.StringConvert Method:
var xd = (from c in Repository.Query<Company>()
where !c.IsDeleted
select new { c.Description, Id = "C" + SqlFunctions.StringConvert((double)c.Id).Trim()});
When you need functionality of .NET only in preparing the result (as opposed to, say, filtering, which should be done on RDBMS side to avoid bringing too much data in memory) the common trick is to complete the conversion in memory using the AsEnumerable method:
var providers = Repository.Query<Company>()
.Where(c => !c.IsDeleted)
.Select(c => new { c.Description, c.Id }) // <<== Prepare raw data
.AsEnumerable() // <<== From this point it's LINQ to Object
.Select(c => new { c.Description, Id = "C"+c.Id }); // <<== Construct end result
The code that you have written will work fine. Here is a mock up of the same code and it outputs the Id's
class Company
{
public string Description { get; set; }
public int Id { get; set; }
public bool IsDeleted { get; set; }
}
static void Main()
{
//setup
var list = new List<Company>();
list.Add(new Company
{
Description = "Test",
Id = 35,
IsDeleted = false
});
list.Add(new Company
{
Description = "Test",
Id = 52,
IsDeleted = false
});
list.Add(new Company
{
Description = "Test",
Id = 75,
IsDeleted = true
});
/* code you are looking for */
var providers = from c in list
where !c.IsDeleted
select new { c.Description, Id = "C" + c.Id };
foreach (var provider in providers)
{
Console.WriteLine(provider.Id);
}
Console.ReadKey();
}
What about string format
var providers = from c in Repository.Query<Company>()
where !c.IsDeleted
select new { c.Description, Id = "C" + c.Id.ToString() };

Nested group by based on flatten record set

I have the following responses from the API. How can I group them into the following structure?
Student[]
- Name
- Classes[]
- ClassName
- ClassId
- ClassCategories[]
- CategoryName
- CategoryWeight
- Assignments[]
- AssignmentName
- Score
I was managed to group them until the "Classes" level but unable to get the ClassCategories for each of the classes
var data = (from result in results
group result by new { result.StudentId, result.FirstName, result.LastName, result.MiddleInitial }
into StudentGroup
select new GroupedStudent
{
StudentId = StudentGroup.Key.StudentId,
FullName = string.Format("{0} {1} {2}", StudentGroup.Key.FirstName, StudentGroup.Key.MiddleInitial, StudentGroup.Key.LastName).Replace(" ", " "),
Classes = from result in results
group result by new { result.ClassId, result.ClassName } into ClassGroup
select new groupedClass
{
ClassName = ClassGroup.Key.ClassName,
ClassId = ClassGroup.Key.ClassId,
ClassCategories = ...
})
}).ToList();
Can anyone please assists me? Thank you.
First, you have make ClassGroup from StudentGroup not from results.
Classes = from s in StudentGroup group result by new { s.ClassId, s.ClassName } into ClassGroup
The complete linq query is as follows:
var data =
(from result in results
group result by new { result.StudentId, result.FirstName, result.LastName, result.MiddleInitial } into StudentGroup
select new
{
StudentId = StudentGroup.Key.StudentId,
FullName = string.Format("{0} {1} {2}", StudentGroup.Key.FirstName, StudentGroup.Key.MiddleInitial, StudentGroup.Key.LastName).Replace(" ", " "),
Classes = (from s in StudentGroup
group s by new { s.ClassId, s.ClassName } into ClassGroup
select new
{
ClassId = ClassGroup.Key.ClassId,
ClassName = ClassGroup.Key.ClassName,
ClassCategories = (from c in ClassGroup
group c by new { c.CategoryName, c.CategoryWeight } into CategoryGroup
select new
{
CategoryName = CategoryGroup.Key.CategoryName,
CategoryWeight = CategoryGroup.Key.CategoryWeight,
Assignments = (from ct in CategoryGroup
group ct by new { ct.AssignmentName, ct.Score } into AssingnmentGroup
select new
{
AssignmentName = AssingnmentGroup.Key.AssignmentName,
Score = AssingnmentGroup.Key.Score
}).ToList()
}).ToList()
}).ToList()
}).ToList();
For example, if you want to access to the first Assignment's score, you can get it like this:
var student = data.FirstOrDefault();
var score = student.Classes[0].ClassCategories[0].Assignments[0].Score;
This is usually how I do It.
Create a class to store your data
Create a list of that class type
In your case instead of string dataRow maybe you can use a sub class
.
// get data from webservice
var json = webClient.DownloadString(url);
var values = JsonConvert.DeserializeObject<JArray>(json);
// create a list to save all the element
List<myClass> classList = new List<myClass>();
// process every row
foreach (string dataRow in values)
{
string[] dataField = dataRow.Split(',');
// have a constructor to assign each value to this element
myClass ctROW = new myClass(dataField);
classList.add(ctROW );

Categories

Resources