Contains with Linq - c#

I have class "Estimation", this class has a property "EstimationItems" (the type is IList)
public class EstimationItem
{
public virtual int Id { get; set; }
public virtual Product Product { get; set; }
public virtual int Quantity { get; set; }
public virtual decimal Price { get; set; }
}
public class Product
{
public virtual int Id { get; set; }
public virtual string Code { get; set; }
public virtual string Name { get; set; }
}
When I have an instance of "Estimation", I'd like to know if "EstimationItems" contain a product with the code "MyCode".

Using this :
List<EstimationItem> items = new List<EstimationItem>();
// Add items
int searchedCode = 1
if(items.Any(i => i.Product.Code == searchedCode))
{
// Contained
}

You can use Any():
bool hasMyCode = yourEstimation.EstimationItems.Any(
item => item.Product.Code == "MyCode");

Enumerable.Any Method determines whether any element of a sequence satisfies a condition.
Boolean result = estimationItems.Any(x => x.Product.Code == "MyCode");

var query = from ei in EstimationItems where ei.Product.Code == "MyCode" select ei;

Related

C# Web Api rest linq query return too much data

Good day,
I run into the problem then returning DTO object.
I have these classes
public class ProductBase
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public IEnumerable<ProductVariant> Variants { get; set; }
public int BaseImageId { get; set; } = 0;
}
public class ProductVariant
{
public int Id { get; set; }
public int Quantity { get; set; }
public int ProductBaseId { get; set; }
public ProductBase productBase { get; set; }
public int ProductSizeId { get; set; }
public ProductSize ProductSize { get; set; }
public int ProductColorId { get; set; }
public ProductColor ProductColor { get; set; }
public IEnumerable<ImageVariant> imageVariants { get; set; }
}
public class ProductColor
{
public int Id { get; set; }
public string Color { get; set; }
public IEnumerable<ProductVariant> productVariant { get; set; }
}
public class ProductSize
{
public int Id { get; set; }
public string Size { get; set; }
public IEnumerable<ProductVariant> productVariant { get; set; }
}
In productBaseRepository I have this call
public async Task<IEnumerable<Models.ProductBase>> GetAllWithVariantsAsync()
{
var result = await _dataContext.ProductBases
.Include(pb => pb.Variants)
.ThenInclude(v => v.ProductSize)
.Include(pb => pb.Variants)
.ThenInclude(v => v.ProductColor)
.ToListAsync();
return result;
}
I have created DTO convertion function
public static IEnumerable<ProductBaseDTO> ConvertToDto(this IEnumerable<ProductBase> productBases)
{
var returnProductBaseDto = (from product in productBases
select new ProductBaseDTO
{
Id = product.Id,
Name = product.Name,
Variants = product.Variants.ToList(),
Description = product.Description,
BaseImageId = product.BaseImageId,
}).ToList();
return returnProductBaseDto;
}
But then I call this function from swagger
[HttpGet]
public async Task<ActionResult<List<ProductBaseDTO>>> GetAllProductsWithVariants()
{
var baseProductDomain = await _productBaseRepository.GetAllWithVariantsAsync();
var baseProduct = baseProductDomain.ConvertToDto();
return Ok(baseProduct);
}
I get that
System.Text.Json.JsonException: A possible object cycle was detected. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 32
If I remove variants from call it works, so I need to some how remove Unecessry values from Variants
The problem happens because your ProductVariant references ProductSize and ProductColor and ProductSize and ProductColor reference a list of ProductVariant objects. And ProductVariant has a reference to its BaseProduct. This creates a cycle, two objects referencing each other.
In order to solve this issue, remove productVariant List from ProductSize and ProductColor or remove the ProductSize and ProductColor references from ProductVariant. Also remove the productBase reference from ProductVariant.
Use the IDs to find object references if required instead of having circular object references.
See prevent property from being serialized in web API on how to prevent properties from being serialized without removing the property declaration from the class.
This is because Variants has a reference to its parents type,
You could simply set this to null, e.g.
foreach(var x in baseProduct.SelectMany(c => c.Variants) { x.ProductBase = null }
We ussually have different viewmodels to stop these cycles, e.g:
public class Order {
public List<OrderLine> OrderLines {get;set}
}
public class OrderLine {
public Order Order {get;set}
}
// Gets mapped to the following viewmodels:
public class OrderViewModel {
public List<OrderOrderLineViewModel > OrderLines {get;set}
}
public class OrderOrderLineViewModel {
public Order Order => null; // Stop object cycling
}
Note that the exception message should tell you where the issue is for example $.Variants.productBase.variants.productBase.variants or something similar.

Remove an object from a list based on an attribute of another list with linq

I have the following class to store objects from a REST API call from SharePoint:
[Serializable]
public class DocumentSearchResult
{
public string TotalCount { get; set; }
public string DocumentPath { get; set; }
public string DocumentTitle { get; set; }
public string DocumentSize { get; set; }
public string DocumentAuthor { get; set; }
public string DocumentDescription { get; set; }
public string DocumentFileExtension { get; set; }
public double DocumentRank { get; set; }
public Int64 DocumentDocId { get; set; }
public Int64 DocumentWorkId { get; set; }
public DateTime DocumentWrite { get; set; }
public string DocumentParentLink { get; set; }
public DateTime DocumentLastModifiedDate { get; set; }
public string DocumentFileType { get; set; }
//These next set of properties are used for Viewing the results in embedded or preview
public string DocumentRedirectedEmbededURL { get; set; }
public string DocumentRedirectPreviewURL { get; set; }
public string DocumentRedirectURL { get; set; }
}
I create a list of these objects in my code:
var docReturnResult = new List<DocumentSearchResult>();
I have another list I create using:
var filterList = this.Where(x => x.TenantId == TenantId);
this will return an IQueryable list that contains a value I need to filter the second list. The filterList has an attribute called SharePointId (x => x.SharePointId) that I need to use to filter the docReturnResult list. So I need to compare the filterList SharePointId to the docReturnResult DocumentDocId and remove any objects in the docReturnResult list that don't match the SharePointId's in the filterList.
This is what I tried last:
var trimResults = new DocumentSearchResult();
trimResults = docReturnResult.RemoveAll(x => x.DocumentDocId != filterList.Where(y => y.SharePointId));
but I get an error:
Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type.
Any help is much appreciated.
Try this, it should remove all documents from docReturnResult where match is not found in filterList
docReturnResult.RemoveAll(x => !filterList.Any(y => y.SharePointId == x.DocumentDocId));
Root cause of your error -
x.DocumentDocId != filterList.Where(y => y.SharePointId)
Right hand side will return IQuarable object and you are trying to compare it with DocumentDocId which will not work.
Edit
You don't need a new variable trmResults, as RemoveALL on docReturnResult will trim the same object, so you can just return docReturnResult
return docReturnResult.RemoveAll(x => !filterList.Any(y => y.SharePointId == x.DocumentDocId));

Simple query performance

Let's say i have 4 Tables A,B,C and D
What is the best way to get list of B to be in a dropdown list.
Condition is that each record should have associated list of D1
so that if B has no D1 records i should not show it in the dropdown list
D1 is a child class of D
How i did it:
// number 2 to specify the selected A Table normally it's a variable
var b_Per_A = Uow.B.GetAll().Where(x => x.A_Id == 2).ToList();
var b_list = new List<B>();
foreach (var b in b_list)
{
var d =
Uow.D1.GetAll().Where(x => x.C.B_Id == b.Id);
if (!d.IsNullOrEmpty()) // extension method that checks if a collection is null or empty
{
b_list.Add(b);
}
}
It works but it's slow
UPDATE:
The signature of GetAll() is
IQueryable<T> GetAll();
UPDATE 2:
My Model is
public class A
{
public int Id { get; set; }
public string Name { get; set; }
public IEnumerable<B> ChildBs { get; set; }
}
public class B
{
public int Id { get; set; }
public string Name { get; set; }
public A ParentA { get; set; }
public IEnumerable<C> ChildCs { get; set; }
}
public class C
{
public int Id { get; set; }
public string Name { get; set; }
public B ParentB { get; set; }
public IEnumerable<D> ChildDs { get; set; }
}
public class D
{
public int Id { get; set; }
public string Name { get; set; }
public C ParentC { get; set; }
}
public class D1 : D
{
/// other properties
}
Given that you've confirmed you have navigation properties on your entity classes, this could be achieved in a single expression:
var result = Uow.B.GetAll()
.Where(b =>
b.ParentA.Id == 2 &&
b.ChildCs.Any() &&
b.ChildCs.SelectMany(c => c.ChildDs).Any())
.ToList();
This will push the filtering to your database rather than doing it in memory and should be a lot more efficient.
Also, I'm assuming that a parent navigation property will never be null and that child collections are always initialized.
Example here
put your var d = Uow.D.GetAll().Where(x => x.C.B_Id == b.Id); outside of your foreach. I also recommend you to use IEnumerable/ICollection instead of List and use for loop instead of foreach. Indexed looping is faster.

How to group multiple lists using LINQ - Sum and GroupBy

I have three generic lists and I am trying to combine and get them into one (List<One>). I am not sure how to use LINQ group by on different lists. I want to group by AccountCode, AccountDate, and SalesCode; and sum the amount.
public class One
{
public string AccountCode { get; set; }
public DateTime AccountDate { get; set; }
public string SalesCode { get; set; }
public decimal? Amount { get; set; }
}
public class Two
{
public string AccountCode { get; set; }
public DateTime AccountDate { get; set; }
public string SalesCode { get; set; }
public decimal? Amount { get; set; }
}
public class Three
{
public string AccountCode { get; set; }
public DateTime AccountDate { get; set; }
public string SalesCode { get; set; }
public decimal? Amount { get; set; }
}
List<One> oneList = new List<One>();
List<Two> twoList = new List<Two>();
List<Three> threeList = new List<Three>();
This is the sample query I have, which is not working. Note: I have not included List<Three>.
from first in oneList
join second in twoList
on first.AccountCode equals second.AccountCode
where
(first.AccountDate.Date == second.AccountDate.Date && first.SalesCode == second.SalesCode)
select new
{
first.AccountCode,
first.AccountDate,
first.SalesCode
first.Amount,
second.Amount
}).Distinct()
group bill by new { bill.AccountCode, bill.AccountDate, bill.SalesCode }
into groupedList
select new
{
groupedList.Key.UAN,
groupedList.Key.AccountDate,
Amount = groupedList.Sum(a => a.Amount)
};
As mentioned in the comment, the first thing you'll need is a common type so that the 3 lists can be joined into a single list. That could be a base class or an interface as I've shown here.
public interface IAccount
{
string AccountCode { get; set; }
DateTime AccountDate { get; set; }
string SalesCode { get; set; }
decimal? Amount { get; set; }
}
public class One : IAccount
{
// ...
}
public class Two : IAccount
{
// ...
}
public class Three : IAccount
{
// ...
}
Once that's in place, then it's easy to combine your 3 lists into one like this:
var allList = oneList.Cast<IAccount>().Union(twoList).Union(threeList);
From there, the group by becomes much simpler as well. When using LINQ to group by multiple fields, you'll want an IEqualityComparer class that looks like this:
public class AccountComparer : IEqualityComparer<IAccount>
{
public bool Equals(IAccount x, IAccount y)
{
return x.AccountCode == y.AccountCode &&
x.AccountDate == y.AccountDate &&
x.SalesCode == y.SalesCode;
}
public int GetHashCode(IAccount obj)
{
return (obj.AccountCode + obj.AccountDate + obj.SalesCode).GetHashCode();
}
}
The group by call is then one line:
var groups = allList.GroupBy(a => a, new AccountComparer());
Alternatively, you could create an AccountKey class that only has the 3 key fields and return that from the keySelector lambda. That feels a little cleaner, but is a bit more code.
From there, you can select the sums like this:
var amountSums = from g in groups
select new
{
g.Key.AccountCode,
g.Key.AccountDate,
g.Key.SalesCode,
Amount = g.Sum(a => a.Amount)
};

Entity Framework navigating using navigation properties more than one level

I have Entity model classes as follows
public partial class User
{
public User()
{
this.webpages_Roles = new HashSet<webpages_Roles>();
}
public int UserID { get; set; }
public virtual ICollection<webpages_Roles> webpages_Roles { get; set; }
}
.
public partial class webpages_Roles
{
public webpages_Roles()
{
this.Users = new HashSet<User>();
this.Roles_X_ApplicationModules =
new HashSet<Roles_X_ApplicationModules>();
}
public int RoleId { get; set; }
public virtual ICollection<User> Users { get; set; }
public virtual ICollection<Roles_X_ApplicationModules>
Roles_X_ApplicationModules { get; set; }
}
.
public partial class Roles_X_ApplicationModules
{
public long ID { get; set; }
public Nullable<int> ModuleID { get; set; }
public Nullable<int> RoleID { get; set; }
public Nullable<bool> ViewPermission { get; set; }
public virtual ApplicationModule ApplicationModule { get; set; }
public virtual webpages_Roles webpages_Roles { get; set; }
}
.and
public partial class ApplicationModule
{
public ApplicationModule()
{
this.Roles_X_ApplicationModules =
new HashSet<Roles_X_ApplicationModules>();
}
public int ModuleID { get; set; }
public virtual ICollection<Roles_X_ApplicationModules>
Roles_X_ApplicationModules { get; set; }
}
you can see User object has a navigation property to webpages_Roles which again has navigation property to Roles_X_ApplicationModules and which in turn navigates to ApplicationModule..
now I want to get all the ApplicationModule from User..how do I write query using navigation properties..
I tried something like this..
var appModules = user.webpages_Roles.SingleOrDefault()
.Roles_X_ApplicationModules.Where(z => z.ViewPermission == true)
.Select(x => x.ApplicationModule);
but problem with this is, it doesn't issue a single query to database. It splits the query to get the webpages_Roles at SingleOrDefault then another query to get the Roles_X_ApplicationModules based on the RoleId and at the end as many queries as Roles_X_ApplicationModules matching the condition to get the ApplicationModule.
How do I write the LINQ query so that a single sql query is issued to database?
You can use Include() to do this.
Example:
card = Cards.Include(l => l.DocumentLinks)
.Include(l => l.Charges.Select(ch => ch.DocumentLinks)
.SingleOrDefault(c=>c.Id==id);
This is for three linked Entities:
public class Card
{
public Guid Id{get;set;}
public virtual ICollection<DocumentLink> DocumentLinks{get;set;}
public virtual ICollection<Charge> Charges{get;set;}
}
public class Charge
{
...
public virtual ICollection<DocumentLink> DocumentLinks{get;set;}
}
public class DocumentLink
{
...
}
try this:
var appModules = from u in user
from w in u.webpages_Roles
from am in w.Roles_X_ApplicationModules
where am.ViewPermission == true
select am;
if you want eager loading then you just need to call ToList:
var appModules = (from u in user
from w in u.webpages_Roles
from am in w.Roles_X_ApplicationModules
where am.ViewPermission == true
select am).ToList();

Categories