Inner join with two select clauses LinQ MVC - c#

Query result from search
Greetings, i am new using linq syntax and i need help translating the query in the picture to get the needed result in c#. I have two questions. First of all How do i do inner joins using linq syntax in c# in order to get the desired result showed in the image. Second, in order to show the data obtained from the query, do i need to create a ViewModel that has 3 ViewModels from the different tables used in the query search?
Thank you so very much for your help.

As levelonehuman said, linq is designed to query data. lets say you have a couple classes:
public class Person
{
public static class Factory
{
private static int currentId = 0;
public static Person Create(string firstName, string lastName, string phoneNumber, int companyId)
{
return new Person()
{
Id = ++currentId,
FirstName = firstName,
LastName = lastName,
PhoneNumber = phoneNumber,
CompanyId = companyId
};
}
}
public int Id { get; private set; }
public string FirstName { get; private set; }
public string LastName { get; private set; }
public string PhoneNumber { get; private set; }
public int CompanyId { get; private set; }
}
public class Company
{
public static class Factory
{
private static int companyId=0;
public static Company Create(string name, string city, string state, string phoneNumber)
{
return new Company()
{
Id = ++ companyId,
City = city,
State = state,
Name = name,
PhoneNumber = phoneNumber
};
}
}
public int Id { get; set; }
public string Name { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PhoneNumber { get; set; }
}
and then you want to see only people from a certain area code you could do something like this:
class Program
{
static void Main(string[] args)
{
var companies = new[]
{
Company.Factory.Create("ABC", "Indianapolis", "In", "(317) 333 5555"),
Company.Factory.Create("Def", "Bloominton", "In", "(812) 333 5555"),
};
var people = new[]
{
Person.Factory.Create("Jane", "Doe", "(317) 555 7565", 1),
Person.Factory.Create("Paul", "Smith", "(812) 555 7565", 2),
Person.Factory.Create("Sean", "Jackson", "(317) 555 7565", 2),
Person.Factory.Create("Jenny", "Gump", "(812) 555 7565", 1)
};
var peopleFromIndianapolis =
(
from company in companies
join person in people on company.Id equals person.CompanyId
where person.PhoneNumber.StartsWith("(317)")
orderby person.LastName, person.FirstName
select new
{
person.FirstName,
person.LastName,
company.Name
}
).ToList();
foreach (var person in peopleFromIndianapolis)
{
Console.WriteLine($"PersonName: {person.LastName}, {person.FirstName} - Company:{person.Name}");
}
}
}
Hope this helps!

Related

How can I find matching values from two tables?

I have a question about how to find collections of values from one table that match some values from another table.
Here's my code snippet:
public async Task<List<TableB>> GetTableBResults(string vCode, string number)
{
var tableARepo = DependencyResolver.Get<IRepository<DBTableA>>();
var TableBRepo = DependencyResolver.Get<IRepository<DBTableB>>();
var tableAQuery = string.Format("SELECT * FROM DBTableA WHERE Identifier = '{0}'",
number);
List<DBTableA> tableA = await tableARepo.QueryAsync(tableAQuery);
if (tableA != null)
{
//Find all tableB records with info from Identifier
//And then do a distinct on BusinessName and return those results
foreach (var item in DBTableA)
{
var TableBQuery = String.Format("SELECT *" +
"FROM[DBTableB] INNER JOIN DBTableA" +
"ON DBTableB.Code = {0}" +
"AND DBTableB.HouseNo = {1}" +
"AND DBTableB.BusinessName = {2}" +
"AND DBTableB.VCode = {3}",
item.Code, item.HouseNo, item.FirstName, vCode);
List<DBTableB> tableB = await TableBRepo.QueryAsync(TableBQuery);
if (tableBs != null)
{
return tableBs.Select(_ => new TableB
{
BoroCode = _.BoroCode,
Code = _.Code,
HouseNo = _.HouseNo,
Date = _.Date,
BusinessName = _.BusinessName,
}).ToList();
}
else
{
return new List<TableB>();
}
}
}
return new List<TableB>();
}
Here are the entities:
public class DBTableA
{
[PrimaryKey, AutoIncrement]
public int DBTableAKey { get; set; }
[NotNull]
[Indexed]
public Int64 Identifier { get; set; }
[NotNull]
public int Code { get; set; }
[Indexed]
public int? HouseNo { get; set; }
public string FirstName { get; set; }
}
public class DBTableB
{
[PrimaryKey, AutoIncrement]
public int DBTableBKey { get; set; }
[NotNull]
[Indexed]
public string BoroCode { get; set; }
[NotNull]
[Indexed]
public int Code { get; set; }
[NotNull]
[Indexed]
public string HouseNo { get; set; }
[NotNull]
public DateTime Date { get; set; }
[NotNull]
public string BusinessName { get; set; }
[NotNull]
[Indexed]
public string VCode { get; set; }
}
Basically, using Identifier from TableA, I want to get all matches rows from Table A where Identifier is equal to the number passed in. Then, do a compare where select fields from that tableA set match same values in TableB set
but I'm not sure how to set up the logic, I added my attempt above.
I mean I just want to return the values from Table B that matches the parameters I want to check/match in the sql query above if any. How can I improve my code above to make return the correct values?
EDIT:
Here is some mock data:
Identifier: 123 (only in TableA: grab Code, HouseNo, FirstName that have Identifier 123)
Then match those from Table A with values from Table B where the values are the same like the sample values below:
Code = 456
HouseNo = 34
BusinessName = 'Bar, Foo'
VCode = 'E4T' (passed in to method)
Return TableB row(s) that match the above info.
You should just be able to use LINQ to handle this query:
var ans = (from a in tableARepo
where a.Identifier == number
join b in tableBRepo on new { a.Code, HouseNo = a.HouseNo.ToString(), a.FirstName, VCode = vCode } equals new { b.Code, b.HouseNo, FirstName = b.BusinessName, b.VCode }
select b).ToList();
return ans;

Expression-tree to build sub-select results

I'm trying to build a sub-query by using expression-trees. In linq I would write something like:
var single = MyTable
.AsExpandable()
.Select(c => new
{
Childs = Enumerable.Select(
MyTable.VisibleChilds.Invoke(c, dbContext),
cc => Convert(cfg.ChildsConfig).Invoke(dbContext, cc))
});
where the Convert is building an expression like
p => new MyTableSelect {
Id = p.Id,
Name = p.Name
}
depending on the given values from the config (to only read needed data from database).
but I'm struggeling with the second parameter to be passed to the Select call as I need cc to be passed to the Convert-call.
I guess I need something like Expression.Lambda<Func<>> but I don't see it.
Expression.Lambda>(Expression.Invoke(Instance.Convert(cfg.ChildOrganizersFilterConfig), ContextParameter, theEntity));
I am not familiar with your use of Invoke but if you just want to run a 'Converter' in a fluent syntax for use in a Linq Expression I could show you an example of that. Say I have three POCO classes, one parent container, a child container, and a container I want to convert to.
public class POC
{
public int Id { get; set; }
public string Name { get; set; }
public POC(int id, string name)
{
Id = id;
Name = name;
}
}
public class ChildPOC
{
public int ParentId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ChildPOC(int parentId, string firstName, string lastName)
{
ParentId = parentId;
FirstName = firstName;
LastName = lastName;
}
}
public class ChildPOCAlter
{
public int ParentId { get; set; }
public string Name { get; set; }
public ChildPOCAlter(string first, string last, int parentId)
{
ParentId = parentId;
Name = $"{first} {last}";
}
}
I could write a converter method to take ChildPOC to ChildPOCAlter like so:
public static Converter<ChildPOC, ChildPOCAlter> ChildPOCOAlter()
{
return new Converter<ChildPOC, ChildPOCAlter>((x) => { return new ChildPOCAlter(x.FirstName, x.LastName, x.ParentId); });
}
I could then populate some data:
var someParents = new List<POC> { new POC(1, "A"), new POC(2, "B") };
var somechildren = new List<ChildPOC> { new ChildPOC(1, "Brett", "x"), new ChildPOC(1, "Emily", "X"), new ChildPOC(2, "John", "Y") };
And then I may want to take these relationships and apply a converter directly on it:
var relationships = someParents.Select(x => new
{
Id = x.Id,
Name = x.Name,
Children = somechildren.Where(y => y.ParentId == x.Id).ToList().ConvertAll(ChildPOCOAlter())
});

Take fields and inner collection fields together

For example, I have class
public class Team
{
public string Name {get;set;}
public string Location {get;set;}
public List<Player> PList{get;set;}
}
And class Player
public class Player
{
public string FirstName {get;set;}
public string LastName {get;set;}
public int Age {get;set;}
}
Please tell me, how should I perform a select from List of Team objects, to get new object:
public class TeamPlayer
{
public string TeamName {get;set;}
public string TeamLocation {get;set;}
public string PlayerFirstName {get;set;}
public string PlayerFirstName {get;set;}
public int PlayerAge {get;set;}
}
For example I've got
var TList = new List<Team>
{
new Team() {
Name = "Rostov",
Location = "Rostov-on-Don",
PList = new List<Player>
{
new Player(){ FirstName = "Soslan", LastName = "Djanaev", Age = 28 },
new Player(){ FirstName = "Christian", LastName = "Noboa", Age = 29 }
}
};
And I want TList to become a List
required selection can be performed with SelectMany method (query syntax is more compact)
var items = from a in aList
from b in a.BList
select new
{
Field1 = a.A1,
Field2 = a.A2,
Field3 = b.B1,
Field4 = b.B2,
Field5 = b.B3
};

Uppercase a List of object with LINQ

I have the code below. I'd like to convert all items in this list to uppercase.
Is there a way to do this in Linq ?
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
public class MyClass
{
List<Person> myList = new List<Person>{
new Person { FirstName = "Aaa", LastName = "BBB", Age = 2 },
new Person{ FirstName = "Deé", LastName = "ève", Age = 3 }
};
}
Update
I don't want to loop or go field by field. Is there a way by reflection to uppercase the value for each property?
Why would you like to use LINQ?
Use List<T>.ForEach:
myList.ForEach(z =>
{
z.FirstName = z.FirstName.ToUpper();
z.LastName = z.LastName.ToUpper();
});
EDIT: no idea why you want to do this by reflection (I wouldn't do this personally...), but here's some code that'll uppercase all properties that return a string. Do note that it's far from being perfect, but it's a base for you in case you really want to use reflection...:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
public static class MyHelper
{
public static void UppercaseClassFields<T>(T theInstance)
{
if (theInstance == null)
{
throw new ArgumentNullException();
}
foreach (var property in theInstance.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
var theValue = property.GetValue(theInstance, null);
if (theValue is string)
{
property.SetValue(theInstance, ((string)theValue).ToUpper(), null);
}
}
}
public static void UppercaseClassFields<T>(IEnumerable<T> theInstance)
{
if (theInstance == null)
{
throw new ArgumentNullException();
}
foreach (var theItem in theInstance)
{
UppercaseClassFields(theItem);
}
}
}
public class Program
{
private static void Main(string[] args)
{
List<Person> myList = new List<Person>{
new Person { FirstName = "Aaa", LastName = "BBB", Age = 2 },
new Person{ FirstName = "Deé", LastName = "ève", Age = 3 }
};
MyHelper.UppercaseClassFields<Person>(myList);
Console.ReadLine();
}
}
LINQ does not provide any facilities to update underlying data. Using LINQ, you can create a new list from an existing one:
// I would say this is overkill since creates a new object instances and
// does ToList()
var updatedItems = myList.Select(p => new Person
{
FirstName = p.FirstName.ToUpper(),
LastName = p.LastName.ToUpper(),
Age = p.Age
})
.ToList();
If using LINQ is not principal, I would suggest using a foreach loop.
UPDATE:
Why you need such solution? Only one way of doing this in generic manner - reflection.
the Easiest approach will be to use ConvertAll:
myList = myList.ConvertAll(d => d.ToUpper());
Not too much different than ForEach loops the original list whereas ConvertAll creates a new one which you need to reassign.
var people = new List<Person> {
new Person { FirstName = "Aaa", LastName = "BBB", Age = 2 },
new Person{ FirstName = "Deé", LastName = "ève", Age = 3 }
};
people = people.ConvertAll(m => new Person
{
FirstName = m.FirstName?.ToUpper(),
LastName = m.LastName?.ToUpper(),
Age = m.Age
});
to answer your update
I don't want to loop or go field by field. Is there a way by
reflection to uppercase the value for each property?
if you don't want to loop or go field by field.
you could use property on the class to give you the Uppercase like so
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public string FirstNameUpperCase => FirstName.ToUpper();
public string LastNameUpperCase => LastName.ToUpper();
}
or you could use back field like so
public class Person
{
private string _firstName;
public string FirstName {
get => _firstName.ToUpper();
set => _firstName = value;
}
private string _lastName;
public string LastName {
get => _lastName.ToUpper();
set => _lastName = value;
}
public int Age { get; set; }
}
You can only really use linq to provide a list of new objects
var upperList = myList.Select(p=> new Person {
FirstName = (p.FirstName == null) ? null : p.FirstName.ToUpper(),
LastName = (p.LastName == null) ? null : p.LastName.ToUpper(),
Age = p.Age
}).ToList();
p.lastname.ToString().ToUpper().Contains(TextString)

How can I send where statements to a method which are dynamically executed in a LINQ statement?

In the following example, GetFilteredCustomers() works fine so I can send various letters which I want customers to have in their last name.
But how can I build GetFilteredCustomersDynamic() which will enable me to send a full where clause that can be dynamically included in the LINQ statement?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestDynamicLinq2343
{
public class Program
{
static void Main(string[] args)
{
List<Customer> customers = Customer.GetCustomers();
//semi-dynamic
foreach (var customer in Customer.GetFilteredCustomers(customers, "o"))
{
Console.WriteLine(customer.LastName);
}
//fully-dyanmic (can send where clauses)
foreach (var customer in Customer.GetFilteredCustomersDynamic(customers, c => c.FirstName.Contains("a")))
{
Console.WriteLine(customer.LastName);
}
Console.ReadLine();
}
}
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Street { get; set; }
public string Location { get; set; }
public string ZipCode { get; set; }
public static List<Customer> GetCustomers()
{
List<Customer> customers = new List<Customer>();
customers.Add(new Customer { FirstName = "Jim", LastName = "Jones" });
customers.Add(new Customer { FirstName = "Joe", LastName = "Adams" });
customers.Add(new Customer { FirstName = "Jake", LastName = "Johnson" });
return customers;
}
public static List<Customer> GetFilteredCustomers(List<Customer> customers, string letter)
{
return (from c in customers
where c.LastName.Contains(letter)
select c).ToList();
}
public static List<Customer> GetFilteredCustomersDynamic(List<Customer> customers, Func<..., bool> whereClause)
{
return (from c in customers
where ...whereClause...
select c).ToList();
}
}
}
Answer:
thanks elder_george and arjuns, I got this example to work like this (albeit without the Expression<> ):
using System;
using System.Collections.Generic;
using System.Linq;
namespace TestDynamicLinq2343
{
public class Program
{
static void Main(string[] args)
{
List<Customer> customers = Customer.GetCustomers();
Func<Customer, bool> whereClause = c => c.LastName.Contains("a") && c.FirstName.Contains("J");
foreach (var customer in Customer.GetFilteredCustomers(customers, whereClause))
{
Console.WriteLine(customer.LastName);
}
Console.ReadLine();
}
}
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Street { get; set; }
public string Location { get; set; }
public string ZipCode { get; set; }
public static List<Customer> GetCustomers()
{
List<Customer> customers = new List<Customer>();
customers.Add(new Customer { FirstName = "Jim", LastName = "Jones" });
customers.Add(new Customer { FirstName = "Joe", LastName = "Adams" });
customers.Add(new Customer { FirstName = "Jake", LastName = "Johnson" });
customers.Add(new Customer { FirstName = "Angie", LastName = "Reckar" });
customers.Add(new Customer { FirstName = "Jean-Luc", LastName = "Beaudoir" });
return customers;
}
public static List<Customer> GetFilteredCustomers(List<Customer> customers, Func<Customer, bool> whereClause)
{
return customers
.Where(whereClause).ToList();
}
}
}
You'd need to represent filter as Expression<Func<Customer, bool>>, not as Func<Customer, bool>. This way you can use Queryable.Where method to add this filter to LINQ expression tree.
EDIT: I was wrong, as this code uses LINQ to objects, where delegates are proper filter criteria. My bad.
E.g. (corrected to using normal delegates):
public static List<Customer> GetFilteredCustomersDynamic(List<Customer> customers, Func<Customer, bool> whereClause)
{
return customers
.Where(whereClause).ToList();
}
public static List<Customer> GetFilteredCustomers(List<Customer> customers, string letter)
{
return GetFilteredCustomersDynamic(c => c.LastName.Contains(letter));
}
Try this code,
public static List<Customer> GetFilteredCustomersDynamic(List<Customer> customers, Expression<Func<Customer, bool>> whereClause)
{
return customers.Where(whereClause.Compile()).ToList();
}
#elder_george,
there is a typo, expression should be compiled to get it's delegate otherwise this can no compile.
return customers
.Where(whereClause).ToList();
should be read as
return customers
.Where(whereClause.Compile()).ToList();

Categories

Resources