How can I find matching values from two tables? - c#

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;

Related

Create query by dynamically pass the GroupBy() and create new class in Select() using Expression tree

I`m having simple method which builds IQueryable and returns it.
public IQueryable<ClassDTO> ReportByNestedProperty()
{
IQueryable<Class> query = this.dbSet;
IQueryable<ClassDTO> groupedQuery =
from opportunity in query
group new
{
ItemGroup = opportunity.OpportunityStage.Name,
EstimatedRevenue = opportunity.EstimatedRevenue,
CostOfLead = opportunity.CostOfLead
}
by new
{
opportunity.OpportunityStage.Name,
opportunity.OpportunityStage.Id
}
into item
select new ClassDTO()
{
ItemGroup = string.IsNullOrEmpty(item.Key.Name) ? "[Not Assigned]" : item.Key.Name,
Count = item.Select(z => z.ItemGroup.Name).Count(), // int
Commission = item.Sum(z => z.EstimatedRevenue), // decimal
Cost = item.Sum(z => z.CostOfLead), // decimal?
};
return groupedQuery;
}
This is fine. The thing i need is to create method with same return type, but groupby by different prperties dynamically. So from the above code I want to have 3 dynamic parts which will be passed as params:
ItemGroup = opportunity.OpportunityStage.Name
and
by new
{
opportunity.OpportunityStage.Name,
opportunity.OpportunityStage.Id
}
So the new method should be like this
public IQueryable<ClassDTO> ReportByNestedProperty(string firstNestedGroupByProperty, string secondNestedGroupByProperty)
{
// TODO: ExpressionTree
}
And call it like this:
ReportByNestedProperty("OpportunityStage.Name","OpportunityStage.Id")
ReportByNestedProperty("OtherNestedProperty.Name","OtherNestedProperty.Id")
ReportByNestedProperty("OpportunityStage.Name","OpportunityStage.Price")
So the main thing is to create expressions with these two selects:
opportunity.OpportunityStage.Name,
opportunity.OpportunityStage.Id
I have tried toe create the select expressions, groupby, the creation of Anonomoys classes and the DTO Class but I just cant get it right.
EDIT:
Here are the classes involved:
public class ClassDTO
{
public ClassDTO() { }
[Key]
public string ItemGroup { get; set; }
public decimal Commission { get; set; }
public decimal? Cost { get; set; }
public int Count { get; set; }
}
Class obj is a pretty big one so i`m posting just part of it
public partial class Class
{
public Class() { }
[Key]
public Guid Id { get; set; }
public Guid? OpportunityStageId { get; set; }
[ForeignKey(nameof(OpportunityStageId))]
[InverseProperty(nameof(Entities.OpportunityStage.Class))]
public virtual OpportunityStage OpportunityStage { get; set; }
}
public partial class OpportunityStage
{
public OpportunityStage()
{
this.Classes = new HashSet<Class>();
}
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
[InverseProperty(nameof(Class.OpportunityStage))]
public virtual ICollection<TruckingCompanyOpportunity> Classes{ get; set; }
}
I have simplified your Grouping query and introduced private class IdName which should replace anonymous class usage:
class IdName
{
public int Id { get; set; }
public string Name { get; set; } = null!;
}
static Expression MakePropPath(Expression objExpression, string path)
{
return path.Split('.').Aggregate(objExpression, Expression.PropertyOrField);
}
IQueryable<ClassDTO> ReportByNestedProperty(IQueryable<Class> query, string nameProperty, string idProperty)
{
// Let compiler to do half of the work
Expression<Func<Class, string, int, IdName>> keySelectorTemplate = (opportunity, name, id) =>
new IdName { Name = name, Id = id };
var param = keySelectorTemplate.Parameters[0];
// generating expressions from prop path
var nameExpr = MakePropPath(param, nameProperty);
var idExpr = MakePropPath(param, idProperty);
var body = keySelectorTemplate.Body;
// substitute parameters
body = ReplacingExpressionVisitor.Replace(keySelectorTemplate.Parameters[1], nameExpr, body);
body = ReplacingExpressionVisitor.Replace(keySelectorTemplate.Parameters[2], idExpr, body);
var keySelectorLambda = Expression.Lambda<Func<Class, IdName>>(body, param);
// finalize query
IQueryable<ClassDTO> groupedQuery = query
.GroupBy(keySelectorLambda)
.Select(item => new ClassDTO()
{
ItemGroup = string.IsNullOrEmpty(item.Key.Name) ? "[Not Assigned]" : item.Key.Name,
Count = item.Count(x => x.Name), // int
Commission = item.Sum(x => x.EstimatedRevenue), // decimal
Cost = item.Sum(x => x.CostOfLead), // decimal?
});
return groupedQuery;
}

LINQ/C# - Making a DTO from a collection?

I'm using EF 6.2 with SQL. Suppose I have these DTO classes:
private class ParentModel
{
public string FullName { get; set; }
public IEnumerable<ChildModel> Children { get; set; }
}
private class ChildModel
{
public string FullName { get; set; }
public string SpiritAnimalDescription { get; set; }
}
ParentModel is derived from an entity class Parent.
ChildModel is from Child, which has a relationship with another entity class SpiritAnimal. Note that I changed it in the .EDMX to Children.
As you can infer, SpiritAnimal has a Description field which I'm trying to retrieve into the ChildModel field, SpiritAnimalDescription.
Naturally, a Parent has a collection of Child, which in turn has one SpiritAnimal (by design). Now, I'm trying to obtain a List<ParentModel> with this code, which currently isn't working:
var query = from p in db.Parents
join c in db.Children on p.Id equals c.Parent_Id
join sa in db.SpiritAnimals on c.SpiritAnimal_Id equals sa.Id
select new ParentModel
{
FullName = p.LastName + ", " + p.FirstName
Children = c.Select(a => new ChildModel // <-- Error here :(
{
FullName = a.FirstName + " " + a.LastName,
SpiritAnimalDescription = sa.Description
}
};
var list = query.ToList();
How can I solve this, as efficiently as possible? Thanks!
EDIT:
Entity classes look something like this, for brevity:
private class Parent
{
public int Id { get; set; } // PK
public string LastName { get; set; }
public string FirstName { get; set; }
}
private class Child
{
public int Id { get; set; } // PK
public string LastName { get; set; }
public string FirstName { get; set; }
public int Parent_Id { get; set; } // FK
public int SpiritAnimal_Id { get; set; } // FK
}
private class SpiritAnimal
{
public int Id { get; set; } // PK
public string Description { get; set; }
}
Your code cannot be compiled and run, so it is impossible to determine exactly what should be.
I can only assume that it should be something like this:
var query = from p in db.Parents
select new ParentModel
{
FullName = p.LastName + ", " + p.FirstName,
Children = db.Children.Where(c => c.Parent_Id == p.Id)
.Select(c => new ChildModel
{
FullName = c.FirstName + " " + c.LastName,
SpiritAnimalDescription = db.SpiritAnimals
.FirstOrDefault(sa => sa.Id == c.SpiritAnimal_Id).Description
})
};
Note: use the navigation properties.
Should look something like this:
var query = from p in db.Parents
select new ParentModel()
{
FullName = p.LastName + ", " + p.FirstName,
Children = p.Clildren.Select(a => new ChildModel()
{
FullName = a.FirstName + " " + a.LastName,
SpiritAnimalDescription = sa.Description
}).ToList()
};

Inner join with two select clauses LinQ MVC

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!

ASP.Net Bootgrid Integration (without sorting)

I've been struggling for days now trying to implement jQuery Bootgrid with my ASP.Net application. So far this is what I have: (Order By Functionality isn't working yet, I'll tackle that later)
public JsonResult IndexJson(RequestData model)
{
var result = (from x in db.ContactSet
select new
{
x.AccountId,
x.FirstName,
x.LastName,
x.FullName,
x.JobTitle,
x.ParentCustomerId,
x.EMailAddress1,
x.Telephone1,
x.MobilePhone,
x.Fax,
x.GenderCode,
x.BirthDate
}); //? Gets all rows
result = (from x in result
where x.FirstName.Contains(model.searchPhrase)
|| x.LastName.Contains(model.searchPhrase)
select x); //? Search Filter
var totalRows = result.Count(); //? Sets totalRows (for ResponseData)
if (model.rowCount == -1)
model.rowCount = totalRows; //? In case View All Rows is selected by Bootgrid (for ResponseData)
// TODO: Add Order By functionality
var tResult = new ResponseData<object>()
{
current = model.current,
rowCount = model.rowCount,
rows = result.ToList(),
total = totalRows
}; //? Builds Json Response
return Json(tResult, JsonRequestBehavior.AllowGet);
}
The problem with this code is I need to count the total number of records after the search functionality and I'm just not that skilled at using the LINQ Queries properly.
By the time I get to var totalRows = result.Count(); I get the following error:
System.NotSupportedException: 'The method 'Where' cannot follow the method 'Select' or is not supported. Try writing the query in terms of supported methods or call the 'AsEnumerable' or 'ToList' method before calling unsupported methods.'
Any idea what's wrong here?
I have been using bootgrid in difference situations, including implementing server-side paging and sorting asc, desc without any problem.
Try this:
//Let's assume this is your model....
public class RequestData
{
public int RowCount { get; set; }
public int Current { get; set; }
public string Search { get; set; }
public string SortBy { get; set; }
public string SortDirection { get; set; }
public int TotalItems { get; set; }
}
1.If you are not selecting all the columns of your DB table, create a DTO that will map your selected columns. Otherwise skip this part and replace anywhere you see ContactSetDTO with ContactSet
public class ContactSetDTO
{
public string AccountId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName { get; set; }
public string JobTitle { get; set; }
public string ParentCustomerId { get; set; }
public string EMailAddress1 { get; set; }
public string Telephone1 { get; set; }
public string MobilePhone { get; set; }
public string Fax { get; set; }
public string GenderCode { get; set; }
public DateTime BirthDate { get; set; }
}
2.Assuming you are using SQL Server, you could use the following method to retrieve the count:
public int getContactSetCount(string searchPhrase)
{
int ret = 0;
try
{
string query = string.Empty;
if (!string.IsNullOrWhiteSpace(searchPhrase))
{
// ********* Assuming your db table is also called ContactSet **********************
query = #"SELECT COUNT(*) FROM ContactSet s WHERE s.FirstName LIKE '%' + #p0 + '%' OR s.LastName LIKE '%' + #p0 + '%')";
ret = db.Database.SqlQuery<int>(query, new System.Data.SqlClient.SqlParameter(parameterName: "#p0", value: searchPhrase)).FirstOrDefault();
}
else
{
ret = db.ContactSet.Count();
}
}
catch (Exception)
{
throw;
}
return ret;
}
3.And finally, your method would look like this:
public JsonResult IndexJson(RequestData model)
{
var searchPhrase = model.Search;
if (!string.IsNullOrWhiteSpace(searchPhrase))
{
//Notice that the select columns match the ContactSetDTO properties
string query = #"SELECT TOP " + model.RowCount + " s.AccountId, s.FirstName, s.LastName, s.FullName, s.JobTitle, s.ParentCustomerId, s.EmailAddress1, s.Telephone1, s.MobilePhone, s.Fax, s.GenderCode, s.BirthDate FROM ContactSet s WHERE s.FirstName LIKE '%' + #p0 + '%' OR s.LastName LIKE '%' + #p0 + '%')";
//Then, this should return a list of ContactSetDTO for you
var result = db.Database.SqlQuery<ContactSetDTO>(query, new System.Data.SqlClient.SqlParameter(parameterName: "#p0", value: searchPhrase)).ToList();
var totalRows = getContactSetCount(searchPhrase);
var tResult = new { rows = result, rowCount = model.RowCount, total = totalRows, current = model.Current, searchPhrase = model.Search }
};
return Json(tResult, JsonRequestBehavior.AllowGet);
}
I hope the above will help.

linq query join with multiple tables filtering not working

I have a form with four text boxes and two comboboxes ...
i am filtering the data and displaying the data in datagrid view depends upon the selection in combobox and text typed in textboxes ..
for that i have written the below code
private void btnRunreports_Click(object sender, EventArgs e)
{
int agefrom = Convert.ToInt32(cbGEFrom.Text);
int ageto = Convert.ToInt32(cbGETo.Text);
DateTime today = DateTime.Today;
DateTime max = today.AddYears(-(agefrom + 1));
DateTime min = today.AddYears(-(ageto));
string maximum = Convert.ToString(max);
string minimum = Convert.ToString(min);
string gender = "";
gender = Classes.reportmembers.ConvertGender(cbGEGendertype.Text);
var mems = Classes.reportmembers
.getallreportmembers(gender,
cbGEMembershiptype.SelectedText,
txtlastname.Text,
txtpostcode.Text,
txtcardnum.Text,
txtreference.Text,
cbGEStatustype.SelectedText,
maximum, minimum);
BindingSource bs = new BindingSource();
bs.DataSource = mems;
dgvReportMembers.DataSource = bs;
}
and this is my class reportmembers:
class ReportMebers
{
public int MemberID { get; set; }
public string Lastname { get; set; }
public string Firstname { get; set; }
public string Postcode { get; set; }
public string Reference { get; set; }
public string CardNum { get; set; }
public string IsBiometric { get; set; }
public string DOB { get; set; }
public string MShipType { get; set; }
public string StatusType { get; set; }
public string EndDate { get; set; }
}
class reportmembers
{
public static List<ReportMebers> getallreportmembers(string gender, string membershiptype, string lastname,
string postcode,string cardnum,string refernce,
string membershipstatustypesa, string maxage, string minage)
{
//CultureInfo provider = CultureInfo.InvariantCulture;
EclipseEntities eclipse = new EclipseEntities();
List<ReportMebers> reporall = new List<ReportMebers>();
var memberreport = from report in eclipse.members
join memtomship in eclipse.membertomships on report.member_Id equals memtomship.member_Id
join mshoption in eclipse.mshipoptions on memtomship.mshipOption_Id equals mshoption.mshipOption_Id
join membershiptypes in eclipse.mshiptypes on mshoption.mshipType_Id equals membershiptypes.mshipType_Id
join membershipstatustypes in eclipse.mshipstatustypes on memtomship.mshipStatusType_Id equals membershipstatustypes.mshipStatusType_Id
where report.member_Lastname.Equals(lastname)
&& report.member_CardNum.Equals(cardnum)
&& report.member_Postcode.Equals(postcode)
&& report.member_Reference.Equals(refernce)
&& report.member_Gender.Equals(gender)
&& membershiptypes.mshipType_Name.Equals(membershiptype)
&& membershipstatustypes.mshipStatusType_Name.Equals(membershipstatustypesa)
&& string.Compare(report.member_Dob,maxage) >= 0
&& string.Compare(report.member_Dob, minage)< 0
select new
{
report.member_Id,
report.member_Lastname,
report.member_Firstname,
report.member_Postcode,
report.member_Reference,
report.member_CardNum,
report.member_IsBiometric,
report.member_Dob,
membershiptypes.mshipType_Name,
membershipstatustypes.mshipStatusType_Name,
memtomship.memberToMship_EndDate
};
try
{
foreach (var membe in memberreport)
{
ReportMebers allmembersrepor = new ReportMebers();
allmembersrepor.MemberID = membe.member_Id;
allmembersrepor.Lastname = membe.member_Lastname;
allmembersrepor.Firstname = membe.member_Firstname;
allmembersrepor.Postcode = membe.member_Postcode;
allmembersrepor.Reference = membe.member_Reference;
allmembersrepor.CardNum = membe.member_CardNum;
allmembersrepor.IsBiometric = membe.member_IsBiometric;
allmembersrepor.DOB = membe.member_Dob;
allmembersrepor.MShipType = membe.mshipType_Name;
allmembersrepor.StatusType = membe.mshipStatusType_Name;
allmembersrepor.EndDate = membe.memberToMship_EndDate;
reporall.Add(allmembersrepor);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return reporall;
}
if i type robin in txtlastname the details will be displayed whoose last name is robin...
i have checked in database there is person with last name robin..
but it does not displayed in datagrid view...
would any guys pls help on this...
Many thanks In advance....
Your problem is, that you are doing an AND comparison over all fields. That means, only entries from the database are returned, that match ALL entered data! If you only enter robin as last name and nothing else, you will get no results, because all the other fields aren't matching. Change your query to include only those fields that are not empty. Something like this:
var query = from report in eclipse.members
join memtomship in eclipse.membertomships on report.member_Id equals memtomship.member_Id
join mshoption in eclipse.mshipoptions on memtomship.mshipOption_Id equals mshoption.mshipOption_Id
join membershiptypes in eclipse.mshiptypes on mshoption.mshipType_Id equals membershiptypes.mshipType_Id
join membershipstatustypes in eclipse.mshipstatustypes on memtomship.mshipStatusType_Id equals membershipstatustypes.mshipStatusType_Id;
if(!string.IsNullOrEmpty(lastname))
query = query.Where(r => r.member_Lastname == lastname);
if(!string.IsNullOrEmptry(cardnum)
query = query.Where(r => r.member_CardNum == cardnum);
// and so on for all parameters

Categories

Resources