Project attribute values - c#

Not sure if this is possible. I have a subset of 'MarketInventory' nodes:
<MARKET_INVENTORY _Type="TotalSales" _MonthRangeType="Prior7To12Months" _Count="18"/>
<MARKET_INVENTORY _Type="TotalSales" _MonthRangeType="Prior4To6Months" _Count="6"/>
<MARKET_INVENTORY _Type="TotalSales" _MonthRangeType="Last3Months" _Count="11"/>
<MARKET_INVENTORY _Type="TotalSales" _TrendType="Stable"/>
filtered on the _Type="TotalSales" node.
I'm wondering if it's possible to project the _Count value attributes into this class:
public class MarketInventoryListing
{
public string Prior7To12Months { get; set; }
public string Prior4To6Months { get; set; }
public string LastThreeMonths { get; set; }
}
This is as far as I got:
var marketInventoryTotalListings = from totalListings in xe.Descendants("MARKET_INVENTORY")
where (string) totalListings.Attribute("_Type") == "TotalSales"
select new MarketInventoryListing()
{
Prior7To12Months =
(
from thing in totalListings.Descendants()
where (string)totalListings.Attribute("_MonthRangeType") == "Prior7To12Months"
select thing.Attribute("_Count").Value
)
};

It's not working for 2 reasons:
Select is returning IEnumerable where as you need a single string
All MARKET_INVENTORY elements are on the same level so "from thing in totalListings.Descendants()" is not returning anything. All these elements are siblings at this point.
I changed your code to address those issues and it works as below:
var marketInventoryTotalListings = (from totalListings in xe.Descendants("MARKET_INVENTORY")
where (string)totalListings.Attribute("_Type") == "TotalSales"
select new MarketInventoryListing()
{
Prior7To12Months =
(
from thing in totalListings.Parent.Descendants()
where (string)totalListings.Attribute("_MonthRangeType") == "Prior7To12Months"
select thing.Attribute("_Count").Value
).FirstOrDefault(),
}).FirstOrDefault();

If you are sure that there will be just one node for each, then you can use FirstOrDefault like this:-
var marketInventoryTotalListings = from totalListings in xe.Descendants("MARKET_INVENTORY")
where (string)totalListings.Attribute("_Type") == "TotalSales"
let prior712 = xe.Descendants("MARKET_INVENTORY")
.FirstOrDefault(x => (string)x.Attribute("_MonthRangeType") == "Prior7To12Months")
let prior46 = xe.Descendants("MARKET_INVENTORY")
.FirstOrDefault(x => (string)x.Attribute("_MonthRangeType") == "Prior4To6Months")
let last3 = xe.Descendants("MARKET_INVENTORY")
.FirstOrDefault(x => (string)x.Attribute("_MonthRangeType") == "Last3Months")
select new MarketInventoryListing
{
Prior7To12Months = prior712 != null ? (string)prior712.Attribute("_Count") : "",
Prior4To6Months = prior712 != null ? (string)prior46.Attribute("_Count") : "",
LastThreeMonths = last3 != null ? (string)last3.Attribute("_Count") : "",
};
Otherwise if they are multiple then you should have IEnumerable<string> as the datatype in the properties of MarketInventoryListing instead of string.

Related

Use a filter to query a list with only child entity conditions EF Core 5

This works fine. But how do I customize the conditions for adding filters?input is the value entered by the user
var resultList = dbContext.BuyerBill
.Include(x=>x.BuyerBillItems.Where(x=>x.Status == input.Status && x.BuildTime > input.BeginTime && x.BuildTime < input.EndTime))
.ToList();
the way I want:
var query = WhereIf(input.Status!=null,x=>x.Status == input.Status);
query = WhereIf(input.BeginTime!=null,x=>x.BuildTime > input.BeginTime);
query = WhereIf(input.EndTime!=null,x=>x.BuildTime > input.EndTime);
this is my entity
public class BuyerBill
{
public BuyerBill()
{
BuyerBillItems = new List<BuyerBillItems>();
}
public int Id {get;set;}
public int BuyUserId {get;set;}
public int OrderId {get;set;}
public List<BuyerBillItems> BuyerBillItems { get; set; }
....
}
public class BuyerBillItems
{
public int Id {get;set;}
public int BuyerBillId {get;set;}
public decimal Fee {get;set;}
public int Status {get;set;}
public dateTime CreateTime {get;set;}
public BuyerBill BuyerBill {get;set;}
....
}
1、If the user does not select the time query
Select * from BuyerBill as buy inner join BuyerBillItems As item On buy.Id=item.BuyerBillId
where item.Status=1
2、If the user selects the time query
Select * from BuyerBill as buy inner join BuyerBillItems as item on buy.Id=item.BuyerBillId
where item.Status=1 and item.BuildTime > '2022-7-19' and item.BuildTime < '2022-7-19'
How to use efcore to implement the SQL conditions I described?
Mainly, I want to filter sub entities according to conditions. If there is only one entity, I know Where() method can be used for filtering, but I don't know how to use conditional filtering for sub entities
I solved the above problem with LinqKit, but now I have a new problem。
var predicatess = PredicateBuilder.New<BuyerBillItems>(true);
predicatess = predicatess.And(x => x.CreateTime > StringHelper.AddDateTime("2022-07-16"));
predicatess = predicatess.And(x => x.Status == 2);
//I'm dumb and this line of code seems redundant. But I don't know how to convert implicitly
var convertPredicate = (Expression<Func<BuyerBillItems, bool>>)predicatess;
var query = dbContext.BuyerBill.AsExpandable().Include(x => x.BuyerBillItems.Where(x => convertPredicate.Invoke(x)))
.Where(x => x.BuyerBillItems.Any(s => convertPredicate.Invoke(s)))
.Where(x => x.BuyUserId == 4);
//If you don't use Select, everything is normal
var result1 = query.ToList();
//BuyerBillItemsDto result is incorrect after using Select
var result2 = query.Select(x => new BuyerBillDto
{
Id = x.Id,
BuyUserId = x.BuyUserId,
OrderId = x.OrderId,
BuyerBillItemsDto = mapper.Map<List<BuyerBillItems>, List<BuyerBillItemsDto>>(x.BuyerBillItems)
}).ToList();
I have to use select to filter the columns to avoid performance loss
Curently you cannot use own extensions in Include body. So, consider to write query in the following way:
var resultList = dbContext.BuyerBill
.Include(x => x.BuyerBillItems.Where(x =>
(input.Status == null || x.Status == input.Status && x.BuildTime > input.BeginTime && x.BuildTime < input.EndTime)) &&
(input.BeginTime == null || x.BuildTime > input.BeginTime) &&
(input.EndTime == null || x.BuildTime > input.EndTime)
)
.ToList();
Update based on updated reuiqrements
For fully custom projection, there is not needed to build Include, because Select specifies which data is needed to retrieve from database.
Query can be rewritten using WhereIf extension:
var result = dbContext.BuyerBill
.Select(b => new BuyerBillDto
{
Id = b.Id,
BuyUserId = b.BuyUserId,
OrderId = b.OrderId,
BuyerBillItemsDto = b.BuyerBillItems
.AsQueryable()
.WhereIf(input.Status != null, x => x.Status == input.Status)
.WhereIf(input.BeginTime != null, x => x.BuildTime > input.BeginTime)
.WhereIf(input.EndTime != null, x => x.BuildTime > input.EndTime)
.Select(item => new BuyerBillItemsDto
{
Id = item.Id,
// other properties
})
.ToList()
}).ToList();

Is there a way to extract contents of a JSON string using LINQ

I have this code:
var jsonResponse = response.Content.ReadAsStringAsync().Result;
List<TranslationResult> a = JsonConvert.DeserializeObject< List<TranslationResult>>(jsonResponse);
var t0 = (a[0] != null) ? a[0] : null;
var t1 = (t0 != null) ? t0.Translations[0] : null;
var t2 = (t1 != null) ? t1.DisplayTarget : null;
var p2 = (t1 != null) ? t1.PosTag : null;
public class TranslationResult
{
public string DisplaySource { get; set; }
public Translation[] Translations { get; set; }
}
public class Translation
{
public string DisplayTarget { get; set; }
public string PosTag { get; set; }
}
The code I am using with all the null tests looks messy and I would like to clean this up. Can anyone suggest a way that I can do this or perhaps suggest a way using LINQ if that's possible. Note that I only need the DisplayTarget and PosTag details.
You can only use LINQ to get the translation results and translations within. You will still have to unpack DisplayTarget and PosTag.
List<TranslationResult> a = JsonConvert.DeserializeObject<List<TranslationResult>>(jsonResponse);
var firstTranslation = a.FirstOrDefault()?.Translations?.FirstOrDefault();
var displayTarget = firstTranslation?.DisplayTarget;
var posTag = firstTranslation?.PosTag;
Also, you could use C# 7.0 tuples to unpack multiple values in one go.
List<TranslationResult> a = JsonConvert.DeserializeObject<List<TranslationResult>>(jsonResponse);
var firstTranslation = a.FirstOrDefault()?.Translations?.FirstOrDefault();
var (displayTarget, posTag) = (firstTranslation?.DisplayTarget, firstTranslation?.PosTag);
If you would like to use all the values, not just first like in your example, you can use LINQ as following:
List<TranslationResult> results = JsonConvert.DeserializeObject<List<TranslationResult>>(jsonResponse);
var translations = (results ?? Enumerable.Empty<TranslationResult>())
.Select(x => x.Translations)
.Where(x => x != null)
.SelectMany(x => x)
.Where(x => x != null)
.Select(x => (x.DisplayTarget, x.PosTag));
foreach (var (displayTarget, posTag) in translations)
{
// do something with displayTarget and posTag
}
Here is an example using Linq and named tuples:
List<TranslationResult> a = JsonConvert.DeserializeObject<List<TranslationResult>>(jsonResponse);
IEnumerable<(string DisplayTarget, string PosTag)> tuples =
a.Where(t0 => t0?.Translations ?? false)
.SelectMany(
t0 => t0.Translations.Select(
t1 => (DisplayTarget: t1?.DisplayTarget ?? string.Empty, PosTag: t1?.PosTag ?? string.Empty)));
// Iterate over the result of named tuples
foreach ((string DisplayTarget, string PosTag) tuple : tuples)
{
// Values are string.Empty when they returned null from deserialization
var displayTarget = tuple.DisplayTarget;
var posTag = tuple.PosTag;
}
var t0 = (a[0] != null) ? a[0] : null;
This can be changed to
var t0 = a[0];
because the result is the same. And
var t1 = (t0 != null) ? t0.Translations[0] : null;
var t2 = (t1 != null) ? t1.DisplayTarget : null;
var p2 = (t1 != null) ? t1.PosTag : null;
can be changed to
var t1 = t0?.Translations[0];
var t2 = t1?.DisplayTarget;
var p2 = t1?.PosTag;
Using the null condition operator is a short form for the "if not null return this otherwise return null" check.
Might be easier to query it :
string PosTag = (string)JToken.Parse(jsonResponse).SelectToken("$..PosTag");

Linq query with All rule in array

I have a list of images and I want to search for multiple keywords with a BOTH rules
For example if I search for "dancing child" I want to show a list of items with both keywords dancing and child
I implemented a query something like this:
List<string> target_keywords = //an array contains Keywords to Lookup
var RuleAny_results = (from imageItem in images
select new{ imageItem,
Rank =target_keywords.Any(x => imageItem.Title != null && imageItem.Title.ToLower().Contains(x)) ? 5 :
target_keywords.Any(x => imageItem.Name != null && imageItem.Name.ToLower().Contains(x)) ? 4 :
0
}).OrderByDescending(i => i.Rank);
//exclude results with no match (ie rank=0 ) and get a Distinct set of items
_searchResult = (from item in RuleAny_results
where item.Rank != 0
select item.imageItem).Distinct().ToList();
But this will return results with any of the items in the target_keywords, e.g. if I search for "dancing child" above code returns list of items with any of the keywords dancing or child. But I want the list with Both dancing and child keywords only
So how can I convert the query so that it fetch all records that contains BOTH keywords?
System.Linq.Enumerable::All is what you want.
using System.Linq;
using System.Collections.Generic;
struct ImageItem {
public string Title { get; set; }
public string Name { get; set; }
}
bool Contains(string toSearch, string x) {
return toSearch != null && toSearch.ToLower().Contains(x);
}
IEnumerable<ImageItem> FilterItems(IEnumerable<string> targetKeywords, IEnumerable<ImageItem> items) {
return items.Where(item => targetKeywords.All(x => Contains(item.Name, x) || Contains(item.Title, x)));
}
Try this:--
you have to just replace Any keyword in syntax with All
And one more rank condition for all keyword in both fields
Replace target_keywords.Any( with target_keywords.All(
List<string> target_keywords = //an array contains Keywords to Lookup
var RuleAny_results = (from imageItem in images
select new{ imageItem,
Rank =target_keywords.Any(x => imageItem.Title != null && imageItem.Title.ToLower().Contains(x)) ? 5 :
target_keywords.All(x => imageItem.Name != null && imageItem.Name.ToLower().Contains(x)) ? 4 :
target_keywords.All(x => (imageItem.Name != null && imageItem.Name.ToLower().Contains(x)) || imageItem.Title != null && imageItem.Title.ToLower().Contains(x)) ? 3 :
0
}).OrderByDescending(i => i.Rank);
//exclude results with no match (ie rank=0 ) and get a Distinct set of items
_searchResult = (from item in RuleAny_results
where item.Rank != 0
select item.imageItem).Distinct().ToList();
class ImageDemo
{
public string Title { get; set; }
public string Name { get; set; }
}
static void TestCode()
{
List<string> target_keywords = new List<string>(){"dancing","child"};
List<ImageDemo> images = new List<ImageDemo>()
{
new ImageDemo{Title = "dancing"} ,
new ImageDemo{Name = "child"} ,
new ImageDemo{Title = "child", Name="dancing"} ,
new ImageDemo{Title = "dancing", Name="child"} ,
new ImageDemo{Name="dancing child"} ,
new ImageDemo{Title="dancing child"}
};
var searchFuncs = target_keywords.Select(x =>
{
Func<ImageDemo, bool> func = (img) =>
{
return (img.Title ?? string.Empty).Contains(x) || (img.Name ?? string.Empty).Contains(x);
};
return func;
});
IEnumerable<ImageDemo> result = images;
foreach (var func in searchFuncs)
{
result = result.Where(x => func(x));
}
foreach (var img in result)
{
Console.WriteLine(string.Format("Title:{0} Name:{1}", img.Title, img.Name));
}
}
is it the right code you want now?

Linq query related to some properties

I am new here so please be tolerant :)
I have query like this below:
ThesisListViewModel reviewModel = new ThesisListViewModel
{
ThesisModel = from the in thesisNavigatorRepository.Thesis
join tp1 in thesisNavigatorRepository.ThesisType1 on the.ThesisType1Id equals tp1.ThesisType1Id
join tp2 in thesisNavigatorRepository.ThesisType2 on the.ThesisType2Id equals tp2.ThesisType2Id
select new ThesisModel
{
Thesis_ThesisId = the.ThesisId,
Thesis_Subject = the.Subject,
Thesis_ShortDescription = the.ShortDescription,
ThesisType1_Description = tp1.Description,
ThesisType2_Description = tp2.Description,
Thesis_URL = the.URL,
Thesis_ThesisLocalization = the.ThesisLocalization,
AuthorModel = from per in thesisNavigatorRepository.Person
join uod in thesisNavigatorRepository.UnitOfDepartment on per.UODId equals uod.UODId
join dep in thesisNavigatorRepository.Department on uod.DepartmentId equals dep.DepartmentId
join ptt in thesisNavigatorRepository.PersonToThesis on per.PersonId equals ptt.PersonId
join prr in thesisNavigatorRepository.PersonRole on ptt.PersonRoleId equals prr.PersonRoleId
where ptt.ThesisId == the.ThesisId && ptt.PersonRoleId == 1
select new AuthorModel
{
Person_FistName = per.FirstName,
Person_LastName = per.LastName,
UnitOfDepartment_ShortName = uod.ShortName,
UnitOfDepartment_LongName = uod.LongName,
Department_ShortName = dep.ShortName,
Department_LongName = dep.LongName,
},
StaffModel = from per in thesisNavigatorRepository.Person
join uod in thesisNavigatorRepository.UnitOfDepartment on per.UODId equals uod.UODId
join dep in thesisNavigatorRepository.Department on uod.DepartmentId equals dep.DepartmentId
join ptt in thesisNavigatorRepository.PersonToThesis on per.PersonId equals ptt.PersonId
join prr in thesisNavigatorRepository.PersonRole on ptt.PersonRoleId equals prr.PersonRoleId
where ptt.ThesisId == the.ThesisId && ptt.PersonRoleId != 1
select new StaffModel
{
Person_FistName = per.FirstName,
Person_LastName = per.LastName,
Person_Title = per.Title,
UnitOfDepartment_ShortName = uod.ShortName,
UnitOfDepartment_LongName = uod.LongName,
Department_ShortName = dep.ShortName,
Department_LongName = dep.LongName,
},
},
ThesisType1Model = thesisNavigatorRepository.ThesisType1,
ThesisType2Model = thesisNavigatorRepository.ThesisType2,
PersonModel = thesisNavigatorRepository.Person,
PersonRoleModel = thesisNavigatorRepository.PersonRole,
UnitOfDepartmentModel = thesisNavigatorRepository.UnitOfDepartment,
DepartmentModel = thesisNavigatorRepository.Department
};
return PartialView(reviewModel);
And I would like to make it dependent on some properties which I am passing to an action method.
The condition which I need is below (simple version):
if(property1 == null or property2 == null) -> query for ThesisModel without where clause
elseif(property1 == null) ThesisModel where tp2.Description like property2
elseif(property2 == null) ThesisModel where tp1.Description like property1
else ThesisModel where tp1.Description like property1 and tp2.Description like property2
I am stuck. I can of course make different queries for each condition but I hope that exist easiest way.
Could you please help?
P.S.
My english is poor so sorry if I made some mistakes.
EDIT:
I had tried with your ideas but I stil cannot use it in my project.
I am passing data to my controler from view using below properties:
public string SearchByType1 { get; set; }
public string SearchByType2 { get; set; }
public string SearchByPerson { get; set; }
public string SearchByPersonRole { get; set; }
public string SearchByUOD { get; set; }
public string SearchByDepartment { get; set; }
Each property is related to other table as you can see in my controler. Using PredicateBuilder looks easy only when my conditions are related to one property in one table. Currently for two properties I have such conditions:
where
(
string.IsNullOrEmpty(model.SearchByType1)
&&
string.IsNullOrEmpty(model.SearchByType2)
)
||
(
string.IsNullOrEmpty(model.SearchByType1)
&&
SqlFunctions.StringConvert((double)tp2.ThesisType2Id).Trim().Equals(model.SearchByType2)
)
||
(
string.IsNullOrEmpty(model.SearchByType2)
&&
SqlFunctions.StringConvert((double)tp1.ThesisType1Id).Trim().Equals(model.SearchByType1)
)
||
(
SqlFunctions.StringConvert((double)tp1.ThesisType1Id).Trim().Equals(model.SearchByType1)
&&
SqlFunctions.StringConvert((double)tp2.ThesisType2Id).Trim().Equals(model.SearchByType2)
)
It works but looks terrible and I have no ideas further. Could you please elaborete your ideas and help?
Just for information I am using SqlFunctions.StringConvert because of parameters coming from (they are from dropdownlist - id as string).
You can use the PredicateBuilder class
Just add the Linqkit dll to your project.
this is the Linqkit Link:http://www.albahari.com/nutshell/predicatebuilder.aspx
Declare each condition as a Func and then pass it to the query. Something along the lines of:
Func<string, bool> predicate;
if (prop1 == null && prop2 == null)
predicate = (s1, s2) => true;
else if (prop1 == null)
predicate = (s1, s2) => prop2 == s2;
else if (prop2 == null)
predicate = (s1, s2) => prop1 == s1;
else
predicate = (s1, s2) => prop1 == s1 && prop2 == s2;
var model = thesisNavigatorRepository.Thesis.Where(t => predicate(t.Description1, t.Description2));

Linq to objects - Search collection with value from other collection

I've tried to search SO for solutions and questions that could be similar to my case.
I got 2 collections of objects:
public class BRSDocument
{
public string IdentifierValue { get; set;}
}
public class BRSMetadata
{
public string Value { get; set;}
}
I fill the list from my datalayer:
List<BRSDocument> colBRSDocuments = Common.Instance.GetBRSDocuments();
List<BRSMetadata> colBRSMetadata = Common.Instance.GetMessageBRSMetadata();
I now want to find that one object in colBRSDocuments where x.IdentifierValue is equal to the one object in colBRSMetadata y.Value. I just need to find the BRSDocument that matches a value from the BRSMetadata objects.
I used a ordinary foreach loop and a simple linq search to find the data and break when the value is found. I'm wondering if the search can be done completely with linq?
foreach (var item in colBRSMetadata)
{
BRSDocument res = colBRSDocuments.FirstOrDefault(x => x.IdentifierValue == item.Value);
if (res != null)
{
//Do work
break;
}
}
Hope that some of you guys can push me in the right direction...
Why not do a join?
var docs = from d in colBRSDocuments
join m in colBRSMetadata on d.IdentiferValue equals m.Value
select d;
If there's only meant to be one then you can do:
var doc = docs.Single(); // will throw if there is not exactly one element
If you want to return both objects, then you can do the following:
var docsAndData = from d in colBRSDocuments
join m in colBRSMetadata on d.IdentiferValue equals m.Value
select new
{
Doc = d,
Data = m
};
then you can access like:
foreach (var dd in docsAndData)
{
// dd.Doc
// dd.Data
}
Use Linq ?
Something like this should do the job :
foreach (var res in colBRSMetadata.Select(item => colBRSDocuments.FirstOrDefault(x => x.IdentifierValue == item.Value)).Where(res => res != null))
{
//Do work
break;
}
If you are just interested by the first item, then the code would be :
var brsDocument = colBRSMetadata.Select(item => colBRSDocuments.FirstOrDefault(x => x.IdentifierValue == item.Value)).FirstOrDefault(res => res != null);
if (brsDocument != null)
//Do Stuff

Categories

Resources