I am developing an Asp.Net MVC Application and i am new to Linq and CodeFirst. In my controller this is the action that i have written:
public ActionResult Filter(int? PaperType , int? PaperGram , int? Brand)
{
var FilteredResult ;
if (PaperType.HasValue && PaperGram.HasValue && Brand.HasValue) {
FilteredResult = db.Stocks.where(m => m.PaperType == PaperType && m.PaperGram == PaperGram && m.Brand == Brand);
}
else if (PaperType.HasValue && PaperGram.HasValue) {
FilteredResult = db.Stocks.where(m => m.PaperType == PaperType && m.PaperGram == PaperGram);
}
else if (PaperType.HasValue && Brand.HasValue) {
FilteredResult = db.Stocks.where(m => m.PaperType == PaperType && m.Brand == Brand);
}
// and ifs continue to last
/*
.
.
.
.
*/
else {
FilteredResult = db.Stocks;
}
return View(FilteredResult);
}
But i know that this is not the best way to do in Linq and Codefirst. So, can you give a better solution to this problem?
You can do this:
FilteredResult = db.Stocks.where(m => (m.PaperType == PaperType || !PaperType.HasValue)
&& (m.PaperGram == PaperGram || !PaperGram.HasValue)
&& (m.Brand == Brand || !Brand.HasValue));
What you want to avoid is code duplication.
Create your original IQueriable and then add your where clauses when necessary
public ActionResult Filter(int? PaperType, int? PaperGram, int? Brand)
{
var FilteredResult FilteredResult = db.Stocks.AsQueryable();
if(PaperType.HasValue)
{
FilteredResult = FilteredResult.where(m => m.PaperType == PaperType);
if(PaperGram.HasValue)
FilteredResult = FilteredResult.where(m => m.PaperGram == PaperGram );
if ( Brand.HasValue)
FilteredResult = FilteredResult.where(m => m.Brand == Brand);
}
return View(FilteredResult);
}
You can just assign all elements to list and then filter every element in each if condition
IEnumerable<Stock> filteredResult = db.Stocks.AsQueryable();
if (PaperType.HasValue)
{
filteredResult = filteredResult.Where(m => m.PaperType == PaperType);
}
if (PaperGram.HasValue)
{
filteredResult = filteredResult.Where(m => m.PaperGram== PaperGram);
}
if (Brand.HasValue)
{
filteredResult= filteredResult.Where(m => m.Brand== Brand);
}
return View(FilteredResult.ToList());
Related
I'm instantiating an instance of DashboardActions.cs inside of my TabController.cs which is suppposed to return an IEnumerable<Tab> coming from Entity Framework. I've shown the code for the GET method of the TabController.cs and the GetTabs method of the DashboardActions.cs class that the controller is instantiating.
TabController.cs
public IEnumerable<Tab> GetTabs()
{
IEnumerable<Tab> recentPages = null;
try
{
using (var context = new Clarity.BusinessLayer.CLARITY_DNN())
{
recentPages = (from t in context.Tabs
join tp in context.TabPermissions on t.TabID equals tp.TabID
where tp.RoleID == -1 && tp.AllowAccess && tp.PermissionID == 3
&& !t.IsDeleted && t.IsVisible && t.PortalID == 0 &&
!string.IsNullOrEmpty(t.Description)
&& t.TabName != "Home"
select t)
.OrderByDescending(p => p.LastModifiedOnDate ?? p.CreatedOnDate)
.Take(20);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
return recentPages;
}
TabController.cs
public class TabController : ApiController
{
// GET: Dashboard
public IEnumerable<Tab> Get()
{
var recentTabsInstance = new DashboardActions();
var recentTabs = recentTabsInstance.GetTabs();
return recentTabs;
}
}
You have just a query, not data. To get data from db you have to add .ToList()
recentPages = (from t in context.Tabs
join tp in context.TabPermissions on t.TabID equals tp.TabID
where tp.RoleID == -1 && tp.AllowAccess && tp.PermissionID == 3
&& !t.IsDeleted && t.IsVisible && t.PortalID == 0 &&
!string.IsNullOrEmpty(t.Description)
&& t.TabName != "Home"
select t)
.OrderByDescending(p => p.LastModifiedOnDate ?? p.CreatedOnDate)
.Take(20)
.ToList();
I have to two function which is used to find tags inside the tags like, there is a tag A=B(C(D(E))) so i have to find all the tags inside B then all the tags inside C and so on. I write two function but getting the error System.StackOverflowException. In the first function i am providing the tag ID and against that tag id i am getting getNestesCalTagsId and then calling the getNestedCalTagsIngredients() function. But when there are lot of recursion calls i get the error System.StackOverflowException. Below is my whole code.
public List<int?> getNestedCalTags(int? calTagId)
{
var getNestesCalTagsId = db.Dependencies_Metrix.Where(x => x.Cal_Tag_P_Id == calTagId && x.Status == true && x.Cal_Tag_Id_FK!=null).Select(x => x.Cal_Tag_Id_FK).ToList();
if (getNestesCalTagsId.Count > 0)
{
nestedCalFTags.AddRange(getNestesCalTagsId);
foreach (var item in getNestesCalTagsId)
{
if (item != null)
{
getNestedCalTagsIngredients(item.Value);
}
}
}
if (nestedCalFTags.Count > 0)
{
int countedTags = nestedCalFTags.Count;
List<int?> tags = new List<int?>(nestedCalFTags);
for (int i = 0; i < tags.Count; i++)
{
if (tags[i] != null)
{
getNestedCalTagsIngredients(tags[i].Value);
}
}
}
return nestedRawTags;
}
public bool getNestedCalTagsIngredients(int nestCalTagId)
{
var getCalTags = db.Dependencies_Metrix.Where(x => x.Cal_Tag_P_Id == nestCalTagId && x.Status == true).ToList();
if (getCalTags.Count > 0)
{
foreach (var item in getCalTags)
{
if (item.Cal_Tag_Id_FK != null)
{
var getNestedCalTagParent = db.Dependencies_Metrix.Where(x => x.Cal_Tag_P_Id == item.Cal_Tag_Id_FK && x.Status == true && x.Cal_Tag_Id_FK!=null).Select(x => x.Cal_Tag_Id_FK).ToList();
if (getNestedCalTagParent != null)
{
nestedCalFTags.AddRange(getNestedCalTagParent);
getNestedCalTags(item.Cal_Tag_Id_FK);
}
}
else
{
var rawTagId = db.Dependencies_Metrix.Where(x => x.Cal_Tag_P_Id == item.Cal_Tag_P_Id && x.Real_Tag_Id_FK!=null).Select(x => x.Real_Tag_Id_FK).ToList();
if (rawTagId != null)
{
foreach (var rawItem in rawTagId)
{
if (rawItem!=null)
{
if (nestedRawTags.IndexOf(rawItem.Value) == -1)
{
nestedRawTags.Add(rawItem.Value);
}
}
}
}
nestedCalFTags.Remove(nestCalTagId);
}
}
}
return true;
}
How can I reduce this code? I have transferId, weekId and lineId and I want to be able to query the database based on if the url parameters where given or not.
Example:
route/Query?transferId=5325&lineId=10
or
route/Query?weekdId=11
and so on...
public async Task<ICollection<TransferEntity>> GetQueryAsync(string transferId, int? weekId, int? lineId)
{
if (Helpers.NullOrWhiteSpaceAll(transferId, weekId.ToString(), lineId.ToString()))
{
return null;
}
else if (!Helpers.NullOrWhiteSpaceAny(transferId, weekId.ToString(), lineId.ToString()))
{
return await _context.Transfers.Where(x => x.TransferId == transferId && x.WeekId == weekId && x.LineId == lineId).ToListAsync();
}
if (Helpers.NullOrWhiteSpace(transferId))
{
if (!Helpers.NullOrWhiteSpaceAny(weekId, lineId))
{
return await _context.Transfers.Where(x => x.WeekId == weekId && x.LineId == lineId).ToListAsync();
}
else if (!Helpers.NullOrWhiteSpace(weekId))
{
return await _context.Transfers.Where(x => x.WeekId == weekId).ToListAsync();
}
else
{
return await _context.Transfers.Where(x => x.LineId == lineId).ToListAsync();
}
}
else if (Helpers.NullOrWhiteSpace(weekId))
{
if (!Helpers.NullOrWhiteSpaceAny(transferId, lineId.ToString()))
{
return await _context.Transfers.Where(x => x.TransferId == transferId && x.LineId == lineId).ToListAsync();
}
else if (!Helpers.NullOrWhiteSpace(transferId))
{
return await _context.Transfers.Where(x => x.TransferId == transferId).ToListAsync();
}
else
{
return await _context.Transfers.Where(x => x.LineId == lineId).ToListAsync();
}
}
else
{
return await _context.Transfers.Where(x => x.TransferId == transferId && x.WeekId == weekId).ToListAsync();
}
}
Linq-to-SQL will combine multiple Where clauses into single query - so you can simply add them as needed and execute at the end:
var transfers = _context.Transfers;
if (!Helpers.NullOrWhiteSpaceAny(transferId))
{
transfers = transfers.Where(x => x.TransferId == transferId);
}
if (weekId.HasValue)
{
transfers = transfers.Where(x => x.WeekId == weekId);
}
return _context.Transfers == transfers? null : // did not add anything
await transfers.ToListAsync();
Thank you for the help guys but this is a better way to do it.
I have transferId, weekId, lineId
I want to only search by those that are not null or whitespace
I realized that whitespace can be ignored because the input that I get will never be whitespace
This is my solution
public async Task<ICollection<TransferEntity>> GetQueryAsync(string transferId, int? weekId, int? lineId) =>
await _context.Transfers.Where(x => (transferId == null || x.TransferId == transferId) && (weekId == null || x.WeekId == weekId) && (lineId == null || x.LineId == lineId)).ToListAsync();
Try this. First, make a flag enum of all singular parameters and their possible combinations:
public enum ParamSet { none = 0, transferId = 1, weekId = 2, lineId =4,
// composite values
transferId_weekId = 3
}
here first line are individual param flags: if param is not null, the flag is set, otherwise not. Second line is for allowed parameter combinations. For instance, transfer and week ids. It's 3 because 1+2;
Then, analyse the current parameter values:
ParamSet currentParams = (string.IsNullOrEmpty(transferId) ? ParamSet.none : ParamSet.transferId) | (weekId == null ? ParamSet.none : ParamSet.weekId) | (lineId == null ? ParamSet.none : ParamSet.lineId);
Finally, get a switch:
switch(currentParams)
{
case ParamSet.transferId_weekId: break;
case ParamSet.transferId: break;
default: throw new Exception("Unsoppurted param config");
}
Just trying to get a cleaner code on a delete method. I need to delete records from a database if a certain column value matches one of two columns in another table.
Is there a better way to delete multiple records, with a "OR"-like expression, so that I can have only one for each loop instead of the following two?
public static void DeleteStageById(int StageId, int ApplicationId)
{
using (IPEntities ip = IPEntities.New())
{
var stage = ip.mkStages;
var stageCultures = ip.appObjectCultures;
var stageStates = ip.mkStatesInStages;
foreach (var stageCulture in stageCultures.Where(sC => sC.ObjectCultureId == stage.Where(s => s.StageId == StageId && s.ApplicationId == ApplicationId).FirstOrDefault().OCId_Name))
{
stageCultures.DeleteObject(stageCulture);
}
foreach (var stageCulture in stageCultures.Where(sC => sC.ObjectCultureId == stage.Where(s => s.StageId == StageId && s.ApplicationId == ApplicationId).FirstOrDefault().OCId_Description))
{
stageCultures.DeleteObject(stageCulture);
}
...
ip.SaveChanges();
}
}
my linq would look like this one
var stage = ip.mkStages;
var stageCultures = ip.appObjectCultures;
var stageStates = ip.mkStatesInStages;
//store this result into a temp variable so it only needs to run once
var temp = stage.Where(s => s.StageId == StageId && s.ApplicationId == ApplicationId).FirstOrDefault();
if (temp != null)
{
foreach (var stageCulture in stageCultures.Where(sC => sC.ObjectCultureId == temp.OCId_Name || sC.ObjectCultureId == temp.OCId_Description))
{
stageCultures.DeleteObject(stageCulture);
}
...
ip.SaveChanges();
}
I recommend avoiding confusing expressions, but here you go:
foreach (var stageCulture in stageCultures.Where(sC => {
var v = stage.Where(s => s.StageId == StageId && s.ApplicationId == ApplicationId).FirstOrDefault();
return sC.ObjectCultureId == v.OCId_Name || sC.ObjectCultureId == v.OCId_Description;
})
{
stageCultures.DeleteObject(stageCulture);
}
I will explain my Problem
so Firstly, I use Predicates Linq for Build Where clause dynamically.
I have to build dynamically because I don't know how many parameters will come. Let me give an example. For the A column can be one parameters however, for the B column can be 2 parameters like either value 'Gas' or 'Oil' which select but that's big problem I can not combine for these 2 column correctly.
So as a result, this code work but It return 0 Items. But there are I know.
public List<CarEntity> GetSearchByKCriteria(int cityId, List<string> fuelType, List<string> gearType, List<string> budget,
List<string> caroser, List<string> enginePower)
{
Expression<Func<Car, bool>> query = null;
Expression<Func<Car, bool>> combine = null;
foreach (var bud in budget)
{
if (budget.Count >= 1)
{
if (bud == "1")
{
if (budget.Count > 1)
{
query = car => car.Budget >= 20000 && car.Budget <= 34999;
}
else
{
query = car => car.Budget >= 20000 && car.Budget <= 34999;
}
}
else if (bud == "2")
{
if (query != null)
{
combine = car => (car.Budget >= 35000 && car.Budget <= 49999);
query = query.Or(combine);
}
else
{
query = car => car.Budget >= 35000 && car.Budget <= 49999;
}
}
}
}
foreach (var caros in caroser)
{
if (caros != "-1" && !string.IsNullOrEmpty(caros))
{
if (query != null)
{
if (query.Expand().ToString().ToLower().Contains("karoser"))
{
combine = car => (car.Karoser == caros);
query = query.And(combine);
}
else
{
combine = car => car.Karoser == caros;
query = query.And(combine);
}
}
else
{
query = car => car.Karoser == caros;
}
}
}
foreach (var fuel in fuelType)
{
if (fuel != "-1" && !string.IsNullOrEmpty(fuel))
{
if (query != null)
{
if (query.Expand().ToString().ToLower().Contains("yakituru"))
{
combine = car => (car.YakitTuru==fuel);
query = query.Or(combine);
}
else
{
combine = car => car.YakitTuru == fuel;
query = query.And(combine);
}
}
else
{
query = car => car.YakitTuru == fuel;
}
}
}
foreach (var gear in gearType)
{
if (gear!="-1"&& !string.IsNullOrEmpty(gear))
{
if (query != null)
{
if (query.Expand().ToString().ToLower().Contains("sanzimantipi"))
{
combine = car => (car.SanzimanTipi == gear);
query = query.Or(combine);
}
else
{
combine = car => car.SanzimanTipi == gear;
query = query.And(combine);
}
}
else
{
query = car => car.SanzimanTipi == gear;
}
}
}
foreach (var engine in enginePower)
{
if (enginePower.Count >= 1)
{
if (engine == "1")
{
if (query != null)
{
if (query.Expand().ToString().ToLower().Contains("silindirhacmi"))
{
combine = car => (car.SilindirHacmi >= 0 && car.SilindirHacmi <= 1600);
query = query.Or(combine);
}
else
{
combine = car => (car.SilindirHacmi >= 0 && car.SilindirHacmi <= 1600);
query = query.And(combine);
}
}
else
{
query = car => car.SilindirHacmi >= 0 && car.SilindirHacmi <= 1600;
}
}
if (engine == "3")
{
if (query != null)
{
if (query.Expand().ToString().ToLower().Contains("silindirhacmi"))
{
combine = car => (car.SilindirHacmi >= 1601 && car.SilindirHacmi <= 1800);
query = query.Or(combine);
}
else
{
combine = car => (car.SilindirHacmi >= 1601 && car.SilindirHacmi <= 1800);
query = query.And(combine);
}
}
else
{
query = car => car.SilindirHacmi >= 1601 && car.SilindirHacmi <= 1800;
}
}
}
using (var context = DataContextFactory.CreateContext())
{
var result = (from fkCar in context.Car.Where(query)
join pkCarBrand in context.CarBrand on fkCar.Marka equals pkCarBrand.ID
where fkCar.IsActive == true
select new
{
entity = fkCar,
joinEntity = pkCarBrand
});
List<CarEntity> theCarList = new List<CarEntity>();
foreach (var item in result)
{
CarEntity theEntity = Mapper.Map(item.entity);
theEntity.CarBrand = Mapper.Map(item.joinEntity);
theCarList.Add(theEntity);
}
return theCarList;
}
}
So thanks for reply,
I faced a similar challenge a while back, where I wanted to have a list of allowed values for an attribute where, if matched, the associated instance would pass the filter. I came up with the following extension method:
static public Expression<Func<TElement, bool>> BuildContainsExpression<TElement, TValue>(Expression<Func<TElement, TValue>> valueSelector, IEnumerable<TValue> values)
{
if (null == valueSelector)
{
throw new ArgumentNullException("valueSelector");
}
if (null == values) { throw new ArgumentNullException("values"); }
ParameterExpression p = valueSelector.Parameters.Single();
if (!values.Any())
{
return e => false;
}
var equals = values.Select(value => (Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue))));
var body = equals.Aggregate<Expression>((accumulate, equal) => Expression.Or(accumulate, equal));
return Expression.Lambda<Func<TElement, bool>>(body, p);
}
This is based on the discussion and code posted at http://www.velocityreviews.com/forums/t645784-linq-where-clause.html