I need a filter between two entity.
Have two tables 1.User 2.Product
Product map with the User table.
I am going to create a dynamic where filter.
I need to find out all the users which have 'test' product.
Conditions: if userFilter count is 0 then I need all test product with the respected user.
If userFilter is there and productFilter is there then below code is working but if userFilter is not there and productFilter is there then it returning 0 row. How can I find the users which have test product? ?
Here is my Code.
public IHttpActionResult GetFilter()
{
var userFilters = new List<Filter>()
{
new Filter { PropertyName = "Username" ,
Operation = Op .Equals, Value = "Karan" },
};
var productfilter = new List<Filter>()
{
new Filter { PropertyName = "Name" ,
Operation = Op .Equals, Value = "Test product" }
};
Func<User, bool> deleg = x => true;
Func<Product, bool> delegProduct = x => true;
if (userFilters.Count > 0)
{
deleg = ExpressionBuilder.GetExpression<User>(userFilters).Compile();
}
if (productfilter.Count > 0)
{
delegProduct = ExpressionBuilder.GetExpression<Product>(productfilter).Compile();
}
var resultt = _localmarketEntities.Users.Where(deleg)
.Select(x => new
{
x.Id,
x.Username,
Product = x.Products.Where(delegProduct).Select(y => new
{
y.Id,
y.Name
}).ToList()
})
.ToList();
return Ok(resultt);
}
if i understand correct, you need all users that have test product when userFiler count is 0.
List<User> res;
var user = _localmarketEntities.Users.Where(deleg).ToList();
if (user.Count == 0) {
res = _localmarketEntities.Products.Where(delegProduct).Select(q => new User() {
Id = q.Id,
Username = q.Username,
Product = q
}).ToList();
}
else {
res = _localmarketEntities.Users.Where(deleg)
.Select(x => new
{
x.Id,
x.Username,
Product = x.Products.Where(delegProduct).Select(y => new
{
y.Id,
y.Name
}).ToList()
})
.ToList();
}
Related
I have flat collection of the following data (IQueryable<Article>) which was obtained by querying DB:
ArticleId
LanguageName
ArticleText
ExtraData1
ExtraData2
1
English
EngText1
Something1
Something2
1
English
EngText2
Another1
Another2
1
French
FraText1
Blabla1
2
English
EngText2
Ololo1
Blabla2
2
German
GerText1
Naturlisch2
Now I need to fill the IQueryable<AgregatedArticle>: the idea is grouping by ArticleId and putting repeating data into nested list:
public class AgregatedArticle {
public int ArticleId { get; set; }
public List<Data> ArticleTexts { get; set; }
public class Data {
public string LanguageName { get; set; }
public string ArticleText { get; set; }
}
}
Unfortunately, I cannot make it: I am getting various EfCore5 translation errors and don't know: if it's me or EfCore5 bugs or limitations. I wasted 3 days trying different approaches. Please help - I was unable to find suitable examples in Internet. The problem comes up when I try to fill ArticleTexts property.
Here is the simplified example:
private async Task<IQueryable<LawArticleAggregated>> GetLawArticlesGroupedById(DbSet<LawArticleDetail> dbSet, string userContentLangRestriction = null)
{
var dbContext = await GetDbContextAsync();
var articlesQuery =
(from articleIds in dbSet.Select(x => x.ArticleId).Distinct()
from articlesPerId in dbSet
.Where(x => x.ArticleId == articleIds.ArticleId)
join askedL in dbContext.Langs
.Where(l => l.LanguageCode == userContentLangRestriction)
on
articlesPerId.LanguageCode
equals
askedL.StringValue
into askedLanguages
from askedLawLanguage in askedLanguages.DefaultIfEmpty()
join fallbackL in dbContext.Langs
.Where(l => l.LanguageCode == CoreConstants.LanguageCodes.English)
on
articlesPerId.LanguageCode
equals
fallbackL.StringValue
into fallbackLanguages
from fallbackLanguage in fallbackLanguages.DefaultIfEmpty()
select new
{
ArticleId = articleIds.ArticleId,
ArticleText = articlesPerId.ArticleText,
LanguageName = askedLawLanguage.ShortName ?? fallbackLanguage.ShortName
})
.OrderBy(x => x.ArticleId).ThenBy(x => x.LanguageName).ThenBy(x => x.ArticleText);
await articlesQuery.LoadAsync();
var aggregatedArticleData = articlesQuery.Select(x => new
{
ArticleId = x.ArticleId,
ArticleText = x.ArticleText,
LanguageName = x.LanguageName
});
var aggregatedArticles = articlesQuery.Select(x => x.ArticleId).Distinct().Select(x => new ArticleAggregated
{
ArticleId = x.ArticleId,
ArticleTexts = aggregatedArticleData.Where(a => a.ArticleId == x.ArticleId)
.Select(x => new LawArticleAggregated.Data
{
ArticleText = x.ArticleText,
LanguageName = x.LanguageName
}).ToList()
});
return aggregatedArticles;
}
For this specific code the exception is as follows:
Unable to translate collection subquery in projection since the parent
query doesn't project key columns of all of it's tables which are
required to generate results on client side. This can happen when
trying to correlate on keyless entity or when using 'Distinct' or
'GroupBy' operations without projecting all of the key columns.
I think I have reverse engineered your query. Big difference that we cannot return IQueryable from this funcrtio, but prepared IEnumerable. So if you have pagination later, better to pass page info into function parameters.
private async Task<IEnumerable<LawArticleAggregated>> GetLawArticlesGroupedById(DbSet<LawArticleDetail> dbSet, string userContentLangRestriction = null)
{
var dbContext = await GetDbContextAsync();
var articlesQuery =
from article in dbSet
from askedLawLanguage in dbContext.Langs
.Where(askedLawLanguage => askedLawLanguage.LanguageCode == userContentLangRestriction && article.LanguageCode == askedLawLanguage.StringValue)
.DefaultIfEmpty()
from fallbackLanguage in dbContext.Langs
.Where(fallbackLanguage => fallbackLanguage.LanguageCode == CoreConstants.LanguageCodes.English && article.LanguageCode == fallbackLanguage.StringValue)
.DefaultIfEmpty()
select new
{
ArticleId = article.ArticleId,
ArticleText = article.ArticleText,
LanguageName = askedLawLanguage.ShortName ?? fallbackLanguage.ShortName
};
articlesQuery = articlesQuery
.OrderBy(x => x.ArticleId)
.ThenBy(x => x.LanguageName)
.ThenBy(x => x.ArticleText);
var loaded = await articlesQuery.ToListAsync();
// group on the client side
var aggregatedArticles = loaded.GroupBy(x => x.ArticleId)
.Select(g => new ArticleAggregated
{
ArticleId = g.Key,
ArticleTexts = g.Select(x => new LawArticleAggregated.Data
{
ArticleText = x.ArticleText,
LanguageName = x.LanguageName
}).ToList()
});
return aggregatedArticles;
}
I ended up with the following implementation (I show it "as is", without simplification from the first message to demonstrate the approach, slightly modified from the initial variant to use proper paging):
private async Task<IEnumerable<LawArticleAggregated>> GetLawArticlesGroupedByIdListAsync(
DbSet<LawArticleDetail> dbSet,
Expression<Func<IQueryable<LawArticleDetail>, IQueryable<LawArticleDetail>>> filterFunc,
int skipCount,
int maxResultCount,
string userContentLangRestriction = null,
CancellationToken cancellationToken = default
)
{
var dbContext = await GetDbContextAsync();
var articlesQuery =
(from articleIds in filterFunc.Compile().Invoke(dbSet).Select(x => new { x.TenantId, x.LawArticleId })
.Distinct().OrderBy(x => x.TenantId).OrderByDescending(x => x.LawArticleId).Skip(skipCount).Take(maxResultCount)
from articlesPerId in dbSet
.Where(x => x.TenantId == articleIds.TenantId && x.LawArticleId == articleIds.LawArticleId)
join askedL in dbContext.FixCodeValues
.Where(l =>
l.DomainId == CoreConstants.Domains.CENTRAL_TOOLS
&& l.CodeName == CoreConstants.FieldTypes.LANGUAGE
&& l.LanguageCode == userContentLangRestriction)
on
articlesPerId.LanguageCode
equals
askedL.StringValue
into askedLanguages
from askedLawLanguage in askedLanguages.DefaultIfEmpty()
join fallbackL in dbContext.FixCodeValues
.Where(l =>
l.DomainId == CoreConstants.Domains.CENTRAL_TOOLS
&& l.CodeName == CoreConstants.FieldTypes.LANGUAGE
&& l.LanguageCode == CoreConstants.LanguageCodes.English)
on
articlesPerId.LanguageCode
equals
fallbackL.StringValue
into fallbackLanguages
from fallbackLanguage in fallbackLanguages.DefaultIfEmpty()
select new
{
TenantId = articleIds.TenantId,
LawArticleId = articleIds.LawArticleId,
Shortcut = articlesPerId.Shortcut,
ArticleText = articlesPerId.ArticleText,
LanguageName = askedLawLanguage.ShortName ?? fallbackLanguage.ShortName
})
.OrderBy(x => x.TenantId).ThenByDescending(x => x.LawArticleId).ThenBy(x => x.Shortcut).ThenBy(x => x.LanguageName).ThenBy(x => x.ArticleText);
var articleList = await articlesQuery.ToListAsync(cancellationToken);
var aggregatedArticles = articleList.GroupBy(x => new { x.TenantId, x.LawArticleId })
.Select(g => new LawArticleAggregated
{
TenantId = g.Key.TenantId,
LawArticleId = g.Key.LawArticleId,
ArticleTexts = g.Select(x => new LawArticleAggregated.Data
{
Shortcut = x.Shortcut,
ArticleText = x.ArticleText,
LanguageName = x.LanguageName
}).ToList()
});
return aggregatedArticles;
}
private async Task<long> GetLawArticlesGroupedByIdCountAsync(
DbSet<LawArticleDetail> dbSet,
Expression<Func<IQueryable<LawArticleDetail>, IQueryable<LawArticleDetail>>> filterFunc,
CancellationToken cancellationToken = default
)
{
return await filterFunc.Compile().Invoke(dbSet).GroupBy(x => new { x.TenantId, x.LawArticleId }).LongCountAsync(cancellationToken);
}
I have two tables with similar data for body insurance and third party car insurance ... I have used enum in the model to separate the insurances and I want to do the creation operation for it .... There are two modes for each insurance. One case when that car does not have insurance yet and the second case when we want to extend it.
I wrote this code to create the form, but it encounters the following error
I also get an error on the name of the Create function.error = not all code paths return a value.
Please advise
public async Task<IActionResult> Create(int id, int type)
{
InsuranceViewModel model;
ViewBag.Type = type;
var companies = await _context.InsuranceCompany
.Where(e => e.IsActice)
.ToListAsync();
ViewData["CompanyList"] = new SelectList(companies, "Id", "CompanyName");
if ((InsuranceType)type == InsuranceType.Body)
{
var bodyInsurance = await _context.BodyInsurance
.Include(e => e.InsuranceCompany)
.FirstOrDefaultAsync(e => e.Id == id);
if (bodyInsurance == null)
{
model = new InsuranceViewModel
{
CompanyId = bodyInsurance.InsuranceCompanyId,
CompanyName = bodyInsurance.InsuranceCompany.CompanyName,
InsuranceType = InsuranceType.Body,
IssueDate = new DateTime(bodyInsurance.IssueDate).Ticks,
ExpireDate = new DateTime(bodyInsurance.ExpireDate).Ticks,
VehicleInformationId = id
};
}
else
{
var lastBody = await _context.BodyInsurance.Include(e => e.InsuranceCompany)
.Where(e => e.VehicleInformationId == id)
.OrderBy(e => e.ExpireDate)
.LastAsync();
model = new InsuranceViewModel
{
ExpireDate = new DateTime(lastBody.ExpireDate).AddYears(1).AddDays(1).Ticks,
CompanyId = lastBody.InsuranceCompanyId,
CompanyName = lastBody.InsuranceCompany.CompanyName,
InsuranceType = InsuranceType.Body,
IssueDate = new DateTime(lastBody.ExpireDate).AddDays(1).Ticks,
VehicleInformationId = id
};
}
}
else
{
if ((InsuranceType)type == InsuranceType.Thirdpart)
{
var thirdParty = await _context.ThirdPartyInsurance
.Include(e => e.InsuranceCompany)
.FirstOrDefaultAsync(e => e.Id == id);
if (thirdParty == null)
{
model = new InsuranceViewModel
{
CompanyId = thirdParty.InsuranceCompanyId,
CompanyName = thirdParty.InsuranceCompany.CompanyName,
InsuranceType = InsuranceType.Body,
IssueDate = new DateTime(thirdParty.IssueDate).Ticks,
ExpireDate = new DateTime(thirdParty.ExpireDate).Ticks,
VehicleInformationId = id
};
}
else
{
var lastThirdParty = await _context.ThirdPartyInsurance.Include(e => e.InsuranceCompany)
.Where(e => e.VehicleInformationId == id)
.OrderBy(e => e.ExpireDate)
.LastAsync();
model = new InsuranceViewModel
{
ExpireDate = new DateTime(lastThirdParty.ExpireDate).AddYears(1).AddDays(1).Ticks,
CompanyId = lastThirdParty.InsuranceCompanyId,
CompanyName = lastThirdParty.InsuranceCompany.CompanyName,
InsuranceType = InsuranceType.Body,
IssueDate = new DateTime(lastThirdParty.ExpireDate).AddDays(1).Ticks,
VehicleInformationId = id
};
}
}
return View(model);
}
I have a class for a cart in a store and when the buy method gets run it skips the breakpoint and still hits the RedirectToAction at the bottom of the method. This is the code for the method
public ActionResult Buy(int productid /*string optionid*/)
{
Product productModel = new Product();
List<ProductOptionJoin> productOptionJoins = GetProductOptions();
var test = productOptionJoins.Where(x => x.ProductID == productid && x.OptionID.ToString() == "1" /*optionid*/).Select(x => new CartItem() { getProduct = x.GetProduct, getOption = x.GetOption });
var i = test;
if (Session["cart"] == null)
{
List<CartItem> cart = new List<CartItem>();
//cart.Add(new CartItem { getProduct = productModel.find(id), getOption = productModel.find(id), Quantity = 1 });
Session["cart"] = cart;
}
else
{
List<CartItem> cart = (List<CartItem>)Session["cart"];
int index = isExist(productid);
if (index != -1)
{
cart[index].Quantity++;
}
else
{
//cart.Add(new CartItem { getProduct = productModel.find(id), getOption = productModel.find(id), Quantity = productModel.find(id) });
}
Session["cart"] = cart;
}
return RedirectToAction("Index");
}
Any ideas?
You are only creating the session variable at the end, maybe put it above?
Product productModel = new Product();
List<ProductOptionJoin> productOptionJoins = GetProductOptions();
var test = productOptionJoins.Where(x => x.ProductID == productid && x.OptionID.ToString() == "1" /*optionid*/).Select(x => new CartItem() { getProduct = x.GetProduct, getOption = x.GetOption });
var i = test;
Session["cart"] = xyz;
I have something like this which is getting string categories (from dropdown).
I am taking all list in catList and comparing that item in string[]categories and if it is null add this to newCategories for add to database. And lastly i want to return List<Category> with categories values.
public List<Category> ExistingCategories(string[] categories)
{
var catList = GetAllCategories().ToList();
List<Category> newCategories = new List<Category>();
var existedCategory = catList.ToLookup(x=>x.Name , v=>v.Name);
foreach (var item in categories)
{
var lookUpExistedCategory = existedCategory[item];
if (lookUpExistedCategory != )
{
newCategories.Add(new Category { Name = item });
}
}
CreateCategories(newList);
return GetAllCategories().ToList();
}
How should I do that?
You can use .Contains(TKey value)
So you can replace your foreach loop by
var newCategories = categories
.Where(m => !existedCategory.Contains(m))
.Select(m => new Category{Name = m}).toList()
By the way, I don't see the need of a LookUp<TKey, TValue>, you could use a HashSet<T>
var existedCategory = new HashSet(GetAllCategories().Select(x => x.Name).ToList());
So your method would be
public List<Category> ExistingCategories(string[] categories)
{
var existingCategories = new HashSet(GetAllCategories().Select(x => x.Name).ToList());
var newCategories = categories
.Where(m => !existingCategories .Contains(m))
.Select(m => new Category{Name = m}).toList());
//assuming this method will add and save to your db
CreateCategories(newCategories);
return GetAllCategories().ToList();
}
I have the following code:
public class Report
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Sales { get; set; }
}
var result = myItems.GroupBy(x => new { Id = x.Id, Name = x.Name }).Select(x => new Report { Id = x.Key.Id, Name = x.Key.Name });
foreach (var item in result)
{
item.Sales = anotherColletion.FirstOrDefault(x => x.Id == item.Id).Sales;
}
I am unable to set the sales property to any value this way. Even if I try:
foreach (var item in result)
{
item.Sales = 50;
}
However, if I set the property using the following code it works:
var result = myItems.GroupBy(x => new { Id = x.Id, Name = x.Name }).Select(x => new Report { Id = x.Key.Id, Name = x.Key.Name, Sales = 50 });
Is this by design?
The problem is that LINQ queries are lazy ("deferred execution"). You are setting the property on each result of the query in the foreach loop, but these results will essentially disappear into thin air.
When you enumerate the results of the query again after your foreach (which you haven't shown us), the query is re-executed and the results recreated, effectively undoing your changes. Remember that the query is just a specification for how to produce the results, not the results themselves.
A simple fix is to materialize the query into a collection first.
var result = myItems.GroupBy(x => new { Id = x.Id, Name = x.Name })
.Select(x => new Report { Id = x.Key.Id, Name = x.Key.Name })
.ToList();
Your foreach will then end up mutating the elements of an in-memory collection rather than the results of a lazy query, and will therefore be visible downstream.
Personally though, consider setting the property in the query itself:
var result = myItems.GroupBy(x => new { Id = x.Id, Name = x.Name })
.Select(x => new Report
{
Id = x.Key.Id,
Name = x.Key.Name,
Sales = anotherCollection.First(a => a.Id == x.KeyId)
.Sales
});
Thomas,
The var results you are using is just a query, when you iterate over it, in the foreach loop, you are generating a new report object but it is not 'stored' anywhere.
Add the ToArray() or ToList() to the end of the query to fix the issue:
var result = myItems.GroupBy(x => new { Id = x.Id, Name = x.Name }).Select(x => new Report { Id = x.Key.Id, Name = x.Key.Name }).ToList();
Amir.