Get result from list inside list - c#

I have a structure like
class a
{
public IList<b> bs{ get; set; }
public class b
{
public string r{ get; set; }
public IList<sl> sls{ get; set; }
public class sl
{
public string sn{ get; set; }
public string st{ get; set; }
}
}
}
the query is like if sn == "abc" then get r
I have done
a aobj = new a();
var aa = aobj.bs.Where(c => c.sl != null).Select(c => c).ToList(); // here I get `r = "qwerty", sls will have data like sn = "qwerty0", st= "1" ; sn = "asdf" , st="2"; sn = "zxc" st = "abc"; sn="me" , st = "abc"
var bb = aa.where(c => c.sl.Select(dr => dr.st.ToLower().Contains("abc"))); // I 'm here checking that `sn` contain abc or not
var cc = bb.Select(c => c.r).ToList(); // result
my expected output of query is "zxc", "me"
but I am getting all the list not only contains abc.. can anyone suggest me what should I do? I am partitioning this query to debug.
Thank you

You'll need to use the Any operator to check if an enumerable collection has an item that meets a criteria.
You can't use Select as that only projects an item, it isn't returning an predicate and as such has no function in a where clause.
Here is your (fixed for syntax errors) changed code:
var aa = aobj.bs.Where(c => c.sls != null).Select(c => c).ToList();
// use Any here
var bb = aa.Where(c => c.sls.Any(dr => dr.sn.ToLower().Contains("abc")));
var cc = bb.Select(c => c.r).ToList();
And here is the test set I used:
a aobj = new a();
aobj.bs = new List<b>();
aobj.bs.Add(new b {
r ="bar",
sls = new List<sl>{
new sl { sn="tets"},
new sl { sn="no"}
}
});
aobj.bs.Add(new b {
r ="foo",
sls = new List<sl>{
new sl { sn="no"},
new sl { sn="abc"}
}
});
aobj.bs.Add(new b {
r ="fubar",
sls = new List<sl>{
new sl { sn="no"},
new sl { sn="abc"}
}
});
This will output:
foo
fubar
If you combine all operators together you'll get:
var merged = aobj
.bs
.Where(c => c.sls != null
&& c.sls.Any(dr => dr.sn.ToLower().Contains("abc")))
.Select(c => c.r);

I think you can use a code like this:
var cc = a.bs
.Where(w => w.sls?.Any(s => s.st?.ToLower().Contains("abc") ?? false) ?? false)
.Select(c => c.r);

Related

How to extract max(property) from left join in LINQ with lambda expression

I have fourtables:
CL_ProductType
CL_InsuranceProduct
PR_Product
PR_ProductInsuranceProduct (aggregation table for PR_Product and CL_InsuranceProduct)
I need left join for PR_ProductInsuranceProduct and I've done it with SelectMany() selector.
The problem is that this query has groupBy method, and I need to extract the max(ID_ProductInsuranceProduct).
My question is: How to extract in .SelectMany() the highist value of ID_ProductInsuranceProduct?
SQL that works:
select p.ID_Product,p.Name, p.Code, p.InProduction, MAX(pip.ID_ProductInsuranceProduct)
from PR_Product p
join CL_ProductType pt ON p.ID_ProductType = pt.ID_ProductType
left join PR_ProductInsuranceProduct pip ON p.ID_Product = pip.ID_Product
join CL_InsuranceProduct ip ON pip.ID_InsuranceProduct = ip.ID_InsuranceProduct
GROUP BY p.ID_Product,p.Name, p.Code, p.InProduction
My code in C# and LINQ Lambda:
var query = DBContext.PR_Product
.Where(m => m.Active)
.Where(nameFilter)
.Where(activationDateFilter)
.Where(closureDateFilter)
.Where(productTypeFilter)
.Where(subgroupFilter)
.Where(inproductionFilter)
.Where(answerFilter)
.Where(insuranceProductFilter)
.Where(excludePidsFilter)
.Join(DBContext.CL_ProductType, p => p.ID_ProductType, pt => pt.ID_ProductType,
(p, pt) => new { p, pt = pt.Name })
.GroupJoin(DBContext.PR_ProductInsuranceProduct,
p => p.p.ID_Product,
pip => pip.ID_Product,
(p1, pip) => new { Products = p1, ProductInsuranceProduct = pip })
.SelectMany
(
x => x.ProductInsuranceProduct.DefaultIfEmpty(),
(x, y) => new
{
x.Products.p.ID_Product,
x.Products.p.Name,
x.Products.p.Code,
x.Products.p.ActivationDate,
x.Products.p.ClosureDate,
x.Products.pt,
x.Products.p.InProduction,
//Here I want to fill in to my custom property max for ID_ProductInsuranceProduct, MaxId is a custom property in a model
x.Products.p.MaxId = x.ProductInsuranceProduct.Max(pip => pip.ID_ProductInsuranceProduct)
})
.GroupBy(x =>
new
{
x.ID_Product,
x.Name,
x.Code,
x.ActivationDate,
x.ClosureDate,
x.pt,
x.InProduction,
});
I assume, beacause it's a SelectMany, that my code returns "flatten" data into one single table, therefore, my method Max, its input is bad, because its not a collection?
Can I do left join in linq with just .Select()?
My continuation of the code, when I execute the query:
count = query.Count();
var list = query
.OrderBy(x => x.FirstOrDefault().Code)
.DoPaging(pageSize, pageIndex)
.ToList();
List<PR_Product> products =
(from m in list
select new PR_Product
{
ID_Product = m.Key.ID_Product,
Name = m.Key.Name,
Code = m.Key.Code,
ActivationDate = m.Key.ActivationDate,
ClosureDate = m.Key.ClosureDate,
ActivationDateString = m.Key.ActivationDate.ToString("d", new CultureInfo(DALParams.LCID, false)),
ClosureDateString = m.Key.ClosureDate.ToString("d", new CultureInfo(DALParams.LCID, false)),
ProductType = m.Key.pt,
InProduction = m.Key.InProduction
//MaxId = implemention...
}).ToList();
Given the following objects:
public class PR_Product
{
public int ID_Product { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public bool InProduction { get; set; }
}
public class PR_ProductInsuranceProduct
{
public int ID_ProductInsuranceProduct { get; set; }
public int ID_Product { get; set; }
}
I've create in memory collections to demonstrate how to extract max(property) from a left join in LINQ with a lambda expression:
var product = new List<PR_Product>
{
new PR_Product { ID_Product = 1, Name = "TestName1", Code = "TestCode1", InProduction = true },
new PR_Product { ID_Product = 2, Name = "TestName2", Code = "TestCode3", InProduction = true },
new PR_Product { ID_Product = 3, Name = "TestName3", Code = "TestCode3", InProduction = true }
};
var productInsurance = new List<PR_ProductInsuranceProduct>
{
new PR_ProductInsuranceProduct { ID_ProductInsuranceProduct = 111, ID_Product = 1 },
new PR_ProductInsuranceProduct { ID_ProductInsuranceProduct = 222, ID_Product = 1 },
new PR_ProductInsuranceProduct { ID_ProductInsuranceProduct = 333, ID_Product = 3 },
};
var result = product
.GroupJoin(productInsurance,
prProduct => prProduct.ID_Product,
insuranceProduct => insuranceProduct.ID_Product,
(prProduct, insuranceProduct) => new { prProduct, insuranceProduct })
.SelectMany(arg => arg.insuranceProduct.DefaultIfEmpty(), (prProduct, insuranceProducts) => new { prProduct })
.GroupBy(arg => new { arg.prProduct.prProduct.ID_Product, arg.prProduct.prProduct.Name, arg.prProduct.prProduct.Code, arg.prProduct.prProduct.InProduction})
.Select(grouping => new
{
grouping.Key.ID_Product,
grouping.Key.Name,
grouping.Key.Code,
Max_ID_ProductInsuranceProduct = grouping.FirstOrDefault()?.prProduct.insuranceProduct.DefaultIfEmpty().Max(insuranceProduct => insuranceProduct?.ID_ProductInsuranceProduct)
});
Console.WriteLine(JsonConvert.SerializeObject(result, Formatting.Indented));
Hope this helps.

How to OrderBy nested Object value Linq

Object
namespace Example
{
public class ContractorAddValue
{
public Member Member { get; set; }
public List<Addresses> Addresses { get; set; }
public ICommand AddAddress { get; set; }
}
public class Addresses
{
public MemberAddress MemberAddress { get; set; }
public ICommand EditAddress { get; set; }
}
}
Query
public ObservableCollection<ContractorAddValue> GetContractorsOrderByCity()
{
var allContractors = (from c in db.Member where c.IsContrator == true select c).ToList();
//var allContractors2 = db.Member .Include(c => c.MemberAddress).SelectMany(c => c.MemberAddress).OrderBy(c => c.City).Select(c => c.Member ).ToList();
//var allContractors = (from c in db.Member where c.IsContrator == true select c).OrderBy(c => c.MemberAddress.OrderBy(x => x.City)).ToList(); <= dosent work
var listContractorAddValue = new ObservableCollection<ContractorAddValue>();
foreach (var i in allContractors)
{
var adressList = db.MemberAddress.Where(x => x.MemberId == i.MemberId).OrderBy(x => x.City).ToList();
ContractorAddValue contractorAddValue = new ContractorAddValue();
contractorAddValue.Member = i;
contractorAddValue.AddAddress = new BaseCommand(() => ContractorsViewModel.SendAddress(i.MemberId ));
contractorAddValue.Addresses = new List<Addresses>();
foreach (var a in adressList)
{
Addresses memberAdress = new Addresses();
memberAdress.MemberAddress = a;
memberAdress.EditAddress = new BaseCommand(() => ContractorsViewModel.SendEditAddress(a.MemberAddressId , i.MemberId ));
contractorAddValue.Addresses.Add(memberAdress);
}
listContractorAddValue.Add(contractorAddValue);
}
return listContractorAddValue;
}
allContractors2 - the order by works, but I retrieve repeating Members. In this approach I tried to use .Distinct() after Select(c => c.Member) but it doesn't work (the whole query stops working).
My goal is to make an order by MemberAddress.City
Thanks in advance!
I think that this code will work but you need to redefine the Equals method of the ContractorAddValue class.
I added one if statement when you want to add contractorAddValue to the list. First you need to check if your list contains that object. If not you add the object to the list. If yes you need to find that object and merge its addresses list with addresses list from the object you want to add.
public ObservableCollection<ContractorAddValue> GetContractorsOrderByCity()
{
var allContractors = (from c in db.Member where c.IsContrator == true select c).ToList();
//var allContractors2 = db.Member .Include(c => c.MemberAddress).SelectMany(c => c.MemberAddress).OrderBy(c => c.City).Select(c => c.Member ).ToList();
//var allContractors = (from c in db.Member where c.IsContrator == true select c).OrderBy(c => c.MemberAddress.OrderBy(x => x.City)).ToList(); <= dosent work
var listContractorAddValue = new ObservableCollection<ContractorAddValue>();
foreach (var i in allContractors)
{
var adressList = db.MemberAddress.Where(x => x.MemberId == i.MemberId).OrderBy(x => x.City).ToList();
ContractorAddValue contractorAddValue = new ContractorAddValue();
contractorAddValue.Member = i;
contractorAddValue.AddAddress = new BaseCommand(() => ContractorsViewModel.SendAddress(i.MemberId ));
contractorAddValue.Addresses = new List<Addresses>();
foreach (var a in adressList)
{
Addresses memberAdress = new Addresses();
memberAdress.MemberAddress = a;
memberAdress.EditAddress = new BaseCommand(() => ContractorsViewModel.SendEditAddress(a.MemberAddressId , i.MemberId ));
contractorAddValue.Addresses.Add(memberAdress);
}
if(!listContractorAddValue.Contains(contractorAddValue)){
listContractorAddValue.Add(contractorAddValue);
} else {
var contAddValue = listContractorAddValue.First(l => l.Equals( contractorAddValue));
contAddValue.Addresses.AddRange(contractorAddValue.Addresses);
}
}
return listContractorAddValue;
}

Return an new object from join using EntityFramework

I am a little bit stuck here.
I got a Database with tables for projects and versions of these projects:
public class Project
{
public int Id { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public List<ProjectVersion> Versions { get; set; } = new List<ProjectVersion>();
public Project() { }
}
and
public class ProjectVersion
{
public int Id { get; set; }
public string Version { get; set; }
public string Checksum { get; set; }
public string Description { get; set; }
public ICollection<EntryPoint> EntryPoints { get; set; } = new List<EntryPoint>();
public ICollection<System> Systems { get; set; } = new List<System>();
public ProjectVersion() { }
}
now I want to get a specific version of a project and some detailed information
public static Project GetVersionByProjectId( int projectId, string version )
{
using ( var ctx = new DatabaseContext() )
{
var query =
ctx.Projects
.Where(p => p.Id.Equals(projectId))
.Join(
ctx.Versions.Where( v => v.Version.Equals( version )),
p => p.Id,
v => v.ProjectId,
(p, v) => new Project
{
Name = p.Name,
Type = p.Type,
Id = p.Id,
Versions = new List<ProjectVersion>
{
new ProjectVersion
{
Checksum = v.Checksum,
Description = v.Description,
Version = v.Version ,
EntryPoints = new List<EntryPoint>(v.EntryPoints),
Systems = new List<System>(v.Systems)
}
}
}
)
.Select(x => x);
var result = query.ToList();
return result[0];
}
}
if I remove the whole
Versions = new List<ProjectVersion>
it works and I get the Project but not the Version. When I tried the LINQ in LINQPad I got following error:
Cannot create a query result of type 'System.Collections.Generic.List`1[UserQuery+ProjectVersion]'.
How can I get the Project with the requested Version?
UPDATE
thanks to the ideas of #RomanoZumbé and #Maritim I could solve it. Problem was different classes of the models.
using ( var ctx = new DatabaseContext() )
{
var query =
ctx.Projects
.Include(p => p.Versions)
.Where(p => p.Id.Equals(projectId))
.Select( p =>
new Project()
{
Id = p.Id,
Name = p.Name,
Type = p.Type,
Versions =
p.Versions
.Where( v => v.Version.Equals(version))
.Select( v =>
new ProjectVersion()
{
Checksum = v.Checksum,
Description = v.Description,
EntryPoints =
v.EntryPoints
.Select( e => new EntryPoint()
{
Call = e.Call,
Step = e.Step
})
.ToList()
})
.ToList()
})
.Select(x => x);
var result = query.ToList();
return result[0];
}
I don't know if I understood it correctly, and this is from top of my mind, but I think it would be easier if you used the Include(...) statement. And since you already included the project 'Versions' (which is equivalent to a JOIN statement in the database), you can do something like:
var query =
ctx.Projects
.Include(p => p.Versions)
.Where(p => p.Id.Equals(projectId))
.Select(
new Project
{
Name = p.Name,
Type = p.Type,
Id = p.Id,
Versions = p.Versions.Where(v => v.Version.Equals(version))
});
return query.FirstOrDefault();
I'm not entirely sure wether this is what you want to achieve, but maybe it helps.
var res = (from p in ctx.Projects
let v = ctx.Versions.FirstOrDefault(x => p.Versions.Any(v => v.Id == x.Id))
where p.Id == projectId
select new Project
{
Name = p.Name,
Type = p.Type,
Id = p.Id,
Versions = new List<ProjectVersion>()
{
new ProjectVersion
{
Checksum = v.Checksum,
Description = v.Description,
Version = v.Version ,
EntryPoints = new List<EntryPoint>(v.EntryPoints),
Systems = new List<System>(v.Systems)
}
}
});
return res.FirstOrDefault()

Linq perform join with updated field

I have 2 classes A and B, in which I wish to perform a linq query returning the class A (with all its members) along with a field from B.
Here is the class in which I'm returning:
public partial class A
{
// fields in A
public int classID { get; set; }
public string mem_1A { get; set; }
public string mem_2A { get; set; }
public int points { get; set; }
// ...
}
Here is the class in which I'm joining against:
public partial class B
{
// fields in B
public int classID { get; set; }
public string mem_1B { get; set; }
public string mem_2B { get; set; }
public int points { get; set; }
// ...
}
I've attempted the following variations but I wasn't able to get it working.
var db = new MyDBContext();
IEnumerable<A> result1 =
from tblA in db.A
join tblB in db.B on tblA.classID equals tblB.classID
select tblA;
IEnumerable<A> result2 = db.A.Join(db.B, x => x.classID, y => y.classID,(x,y) => x);
I'm only get all the members in A, but my points variable in class A is empty. I know I need to perform an assignment from the points in B, but I don't know how to do. What is the proper way to do this?
You might try this:
var result1 =
from tblA in db.A
join tblB in db.B on tblA.classID equals tblB.classID
select new { ClassA = tblA, PointsOfB = tblB.points };
It will create an anonymous class that holds tblA and the points of tblB.
Update the current value:
var result1 =
(from tblA in db.A
join tblB in db.B on tblA.classID equals tblB.classID
select new { ClassA = tblA, PointsOfB = tblB.points }).ToArray(); // <-- note this....
foreach(var r in result1)
r.ClassA.points = r.PointsOfB;
IEnumerable<A> classAUpdated = result1.Select(a => a.ClassA);
Or in one line:
IEnumerable<A> classAList = db.A
.Join(db.B, a => a.classID, b => b.classID,(a,b) => new
{
ClassA = a,
PointsOfB = b.points
}).ToArray () // <---------- to persist it
.Select(ab =>
{
ab.ClassA.points = ab.PointsOfB;
return ab.ClassA;
});
But you should test it.
I assume that this is what you want
var result2 = db.A.Join(db.B, x => x.classID, y => y.classID,(x,y) =>
new{classA = x,
pontOfB = y.points });
if you want it in classA, try this
var result2 = db.A.Join(db.B, x => x.classID, y => y.classID,(x,y) =>
new classA {
classId = x.classId,
mem_1a = x.mem_1a,
mem_1b = x.mem_1b,
points = y.points });

How to write Lambda expression for this SQL query?

I have the following SQL query
Select cLedgerName,dDateFrom,cPeriodType,nPeriodFrom,nPeriodTo
from sys_Account_Ledger a,sys_Log_Deposits_Interest_Master b
where a.cGLCode=b.cGLCode and b.dDateFrom='08-11-2012' and b.cPeriodType='Days'
I wanted to write this query using Lambda expression.This is where I am stuck.
public IList<ListViewData> GetDepositsListViewData(string glCode, string effectDate, string periodType)
{
using (var db = new DataClasses1DataContext())
{
var data=db.sys_Account_Ledgers.Join(db.sys_Log_Deposits_Interest_Masters,
ledger=>ledger.cGLCode,
deposits=>deposits.cGLCode,
(ledger,deposits)=>new {db.sys_Account_Ledgers =ledger,db.sys_Log_Deposits_Interest_Masters =deposits})
}
}
I have created a class which will be the return type of my query.
Here is the class
public class ListViewData
{
public string LedgerName { get; set; }
public string DateFrom { get; set; }
public string PeriodType { get; set; }
public int PeriodFrom { get; set; }
public int PeriodTo { get; set; }
}
Can anyone help me out with the lambda expression?
var result = dataContext.SysAccountLedger
.Join(dataContext.SysLogDepositsInterestMaster,
a => a.cGlCode,
b => b.cGlCode,
(a, b) => new ListViewData
{
LedgerName = a.LedgerName,
DateFrom = b.DateFrom,
PeriodType = b.PeriodType
// other properties
})
.Where(item => item.DateFrom = Convert.ToDateTime("08-11-2012") &&
item.PeriodType == "Days")
.ToList();
//Direct translation into Linq:
var query = from a in db.sys_Account_Ledger
join b in db.sys_Log_Deposits_Interest_Master on a.cGLCode equals b.cGLCode
where b.dDateFrom == Convert.ToDateTime("08-11-2012") && b.cPeriodType == "Days"
select new { a, b };
//Lambda of this:
var query = db.sys_AccountLedger
.Join(db.sys_Log_Deposits_Interest_Master,
a => a.cGLCode,
b => b.cGLCode,
(a, b) => new {a , b})
.Where(w => w.dDateFrom == Convert.ToDateTime("08-11-2012") && w.cPeriodType == "Days");

Categories