Linq Expression Query with In clause [duplicate] - c#

This question already has answers here:
Linq to Entities - SQL "IN" clause
(9 answers)
Closed 8 years ago.
How to make a where in clause similar to one in SQL Server?
I made one by myself but can anyone please improve this?
public List<State> Wherein(string listofcountrycodes)
{
string[] countrycode = null;
countrycode = listofcountrycodes.Split(',');
List<State> statelist = new List<State>();
for (int i = 0; i < countrycode.Length; i++)
{
_states.AddRange(
from states in _objdatasources.StateList()
where states.CountryCode == countrycode[i].ToString()
select new State
{
StateName = states.StateName
});
}
return _states;
}

This expression should do what you want to achieve.
dataSource.StateList.Where(s => countryCodes.Contains(s.CountryCode))

This will translate to a where in clause in Linq to SQL...
var myInClause = new string[] {"One", "Two", "Three"};
var results = from x in MyTable
where myInClause.Contains(x.SomeColumn)
select x;
// OR
var results = MyTable.Where(x => myInClause.Contains(x.SomeColumn));
In the case of your query, you could do something like this...
var results = from states in _objectdatasource.StateList()
where listofcountrycodes.Contains(states.CountryCode)
select new State
{
StateName = states.StateName
};
// OR
var results = _objectdatasource.StateList()
.Where(s => listofcountrycodes.Contains(s.CountryCode))
.Select(s => new State { StateName = s.StateName});

I like it as an extension method:
public static bool In<T>(this T source, params T[] list)
{
return list.Contains(source);
}
Now you call:
var states = _objdatasources.StateList().Where(s => s.In(countrycodes));
You can pass individual values too:
var states = tooManyStates.Where(s => s.In("x", "y", "z"));
Feels more natural and closer to sql.

public List<Requirement> listInquiryLogged()
{
using (DataClassesDataContext dt = new DataClassesDataContext(System.Configuration.ConfigurationManager.ConnectionStrings["ApplicationServices"].ConnectionString))
{
var inq = new int[] {1683,1684,1685,1686,1687,1688,1688,1689,1690,1691,1692,1693};
var result = from Q in dt.Requirements
where inq.Contains(Q.ID)
orderby Q.Description
select Q;
return result.ToList<Requirement>();
}
}

The "IN" clause is built into linq via the .Contains() method.
For example, to get all People whose .States's are "NY" or "FL":
using (DataContext dc = new DataContext("connectionstring"))
{
List<string> states = new List<string>(){"NY", "FL"};
List<Person> list = (from p in dc.GetTable<Person>() where states.Contains(p.State) select p).ToList();
}

from state in _objedatasource.StateList()
where listofcountrycodes.Contains(state.CountryCode)
select state

This little bit different idea. But it will useful to you. I have used sub query to inside the linq main query.
Problem:
Let say we have document table. Schema as follows
schema : document(name,version,auther,modifieddate)
composite Keys : name,version
So we need to get latest versions of all documents.
soloution
var result = (from t in Context.document
where ((from tt in Context.document where t.Name == tt.Name
orderby tt.Version descending select new {Vesion=tt.Version}).FirstOrDefault()).Vesion.Contains(t.Version)
select t).ToList();

public List<State> GetcountryCodeStates(List<string> countryCodes)
{
List<State> states = new List<State>();
states = (from a in _objdatasources.StateList.AsEnumerable()
where countryCodes.Any(c => c.Contains(a.CountryCode))
select a).ToList();
return states;
}

Related

LINQ Dynamic Query with group by and string variable

I want to create a dynamic query with LINQ-to-SQL and everything works except for one thing: group by. I looked at the other questions but haven't found a reason so far why it does not work.
I use using System.Linq.Dynamic; with the following query (that works):
int numberOfRankedItems = 3;
string minMaxChoice = "max";
string orderBy = "";
if (minMaxChoice == "max")
{
orderBy = "queryGroup.Sum(row => row.POSITIONVALUE) descending";
} else if (minMaxChoice == "min")
{
orderBy = "queryGroup.Sum(row => row.POSITIONVALUE) ascending";
}
string minMaxFeatureColumnName = "BRANDNAME";
string selectMinMaxFeature = "new { minMaxFeature = queryGroup.Key." + minMaxFeatureColumnName + ", sumPosition = queryGroup.Sum(row => row.POSITIONVALUE)}";
var query = (from tableRow in Context.xxx
where /* some filters */
group tableRow by tableRow.BRANDNAME into queryGroup
orderby orderBy
select selectMinMaxFeature).Take(numberOfRankedItems).ToList();
The use of variables for orderby and select works fine, but for group by not no matter what I try to replace with a variable. The query actually works if I e.g. use a variable instead of tableRow.BRANDNAME but the query returns the wrong result. The goal is to be able to set the column for the grouping dynamically.
Ideas?
Thanks
Edit: there seem to be multiple issues also with the other variables. So I generalize the question a bit: how can I generalize the following query in terms of
The column to group by
The column to order by + ASC or DESC
In the select statement the columnname of the first statement (BRANDNAME)
Here is the query:
var query = (from tableRow in ctx.xxx
where /* filter */
group tableRow by new { tableRow.BRANDNAME } into queryGroup
orderby queryGroup.Sum(row => row.POSITIONVALUE) descending
select new { minMaxFeature = queryGroup.Key.BRANDNAME, sumPosition = queryGroup.Sum(row => row.POSITIONVALUE) }).Take(numberOfRankedItems).ToList();
Would be great without expression trees ;)
I have now elaborated a solution to the question:
The solution uses as before using System.Linq.Dynamic; allows now to set certain variables. In the end it returns a dictionary.
// set variables
int numberOfRankedItems;
if (queryData.NumberOfRankedItems != null)
{
numberOfRankedItems = (int) queryData.NumberOfRankedItems;
} else
{
numberOfRankedItems = 0;
}
string minMaxChoice = queryData.MinMaxChoice;
string minMaxFeatureColumnName = queryData.MinMaxFeatureColumnName;
string orderByPrompt = "";
if (minMaxChoice == "max")
{
orderByPrompt = "it.Sum(POSITIONVALUE) descending";
} else if (minMaxChoice == "min")
{
orderByPrompt = "it.Sum(POSITIONVALUE) ascending";
}
string groupByPrompt = queryData.MinMaxFeatureColumnName;
// execute query
IQueryable query = (from tableRow in Context.xxx
where /* some filters */
select tableRow).
GroupBy(groupByPrompt, "it").
OrderBy(orderByPrompt).
Select("new (it.Key as minMaxFeature, it.Sum(POSITIONVALUE) as sumPosition)").
Take(numberOfRankedItems);
// create response dictionary
Dictionary<int?, Dictionary<string, int?>> response = new Dictionary<int?, Dictionary<string, int?>>();
int count = 1;
foreach (dynamic item in query)
{
string minMaxFeatureReturn = item.GetType().GetProperty("minMaxFeature").GetValue(item);
int? sumPositionReturn = item.GetType().GetProperty("sumPosition").GetValue(item);
response[count] = new Dictionary<string, int?>() { { minMaxFeatureReturn, sumPositionReturn } };
count++;
}
return response;

How do you reuse mapping functions on Nested entities in Entity Framework?

I have seen multiple questions that are similar to this one but I think my case is slightly different. I'm using EF6 to query the database and I'm using data projection for better queries.
Given that performance is very important on this project I have to make sure to just read the actual fields that I will use so I have very similar queries that are different for just a few fields as I have done this I have noticed repetition of the code so I'm been thinking on how to reuse code this is currently what I Have:
public static IEnumerable<FundWithReturns> GetSimpleFunds(this DbSet<Fund> funds, IEnumerable<int> fundsId)
{
IQueryable<Fund> query = GetFundsQuery(funds, fundsId);
var results = query
.Select(f => new FundWithReturns
{
Category = f.Category,
ExpenseRatio = f.ExpenseRatio,
FundId = f.FundId,
Name = f.Name,
LatestPrice = f.LatestPrice,
DailyReturns = f.FundDailyReturns
.Where(dr => dr.AdjustedValue != null)
.OrderByDescending(dr => dr.CloseDate)
.Select(dr => new DailyReturnPrice
{
CloseDate = dr.CloseDate,
Value = dr.AdjustedValue.Value,
}),
Returns = f.Returns.Select(r => new ReturnValues
{
Daily = r.AdjDaily,
FiveYear = r.AdjFiveYear,
MTD = r.AdjMTD,
OneYear = r.AdjOneYear,
QTD = r.AdjQTD,
SixMonth = r.AdjSixMonth,
ThreeYear = r.AdjThreeYear,
YTD = r.AdjYTD
}).FirstOrDefault()
})
.ToList();
foreach (var result in results)
{
result.DailyReturns = result.DailyReturns.ConvertClosingPricesToDailyReturns();
}
return results;
}
public static IEnumerable<FundListVm> GetFundListVm(this DbSet<Fund> funds, string type)
{
return funds
.Where(f => f.StatusCode == MetisDataObjectStatusCodes.ACTIVE
&& f.Type == type)
.Select(f => new FundListVm
{
Category = f.Category,
Name = f.Name,
Symbol = f.Symbol,
Yield = f.Yield,
ExpenseRatio = f.ExpenseRatio,
LatestDate = f.LatestDate,
Returns = f.Returns.Select(r => new ReturnValues
{
Daily = r.AdjDaily,
FiveYear = r.AdjFiveYear,
MTD = r.AdjMTD,
OneYear = r.AdjOneYear,
QTD = r.AdjQTD,
SixMonth = r.AdjSixMonth,
ThreeYear = r.AdjThreeYear,
YTD = r.AdjYTD
}).FirstOrDefault()
}).OrderBy(f=>f.Symbol).Take(30).ToList();
}
I'm trying to reuse the part where I map the f.Returns so I tried created a Func<> like the following:
private static Func<Return, ReturnValues> MapToReturnValues = r => new ReturnValues
{
Daily = r.AdjDaily,
FiveYear = r.AdjFiveYear,
MTD = r.AdjMTD,
OneYear = r.AdjOneYear,
QTD = r.AdjQTD,
SixMonth = r.AdjSixMonth,
ThreeYear = r.AdjThreeYear,
YTD = r.AdjYTD
};
and then use like this:
public static IEnumerable<FundListVm> GetFundListVm(this DbSet<Fund> funds, string type)
{
return funds
.Where(f => f.StatusCode == MetisDataObjectStatusCodes.ACTIVE
&& f.Type == type)
.Select(f => new FundListVm
{
Category = f.Category,
Name = f.Name,
Symbol = f.Symbol,
Yield = f.Yield,
ExpenseRatio = f.ExpenseRatio,
LatestDate = f.LatestDate,
Returns = f.Returns.Select(MapToReturnValues).FirstOrDefault()
}).OrderBy(f=>f.Symbol).Take(30).ToList();
}
The compiler is ok with it but at runtime, it crashes and says: Internal .NET Framework Data Provider error 1025
I tried to convert the Func into Expression like I read on some questions and then using compile() but It didn't work using AsEnumerable is also not an option because It will query all the fields first which is what I want to avoid.
Am I trying something not possible?
Thank you for your time.
It definitely needs to be Expression<Func<...>>. But instead of using Compile() method (not supported), you can resolve the compile time error using the AsQueryable() method which is perfectly supported (in EF6, the trick doesn't work in current EF Core).
Given the modified definition
private static Expression<Func<Return, ReturnValues>> MapToReturnValues =
r => new ReturnValues { ... };
the sample usage would be
Returns = f.Returns.AsQueryable().Select(MapToReturnValues).FirstOrDefault()

get count in linq query

i want to get count in linq query its work fine without comparing date in where condition but after comparing date it gives exception.
public static IList GetAllCategoryData()
{
using (var objEntity = new BlueCouponEntities())
{
return (from TBL in objEntity.CategoryMasters.AsEnumerable()
let IIT = TBL.CategoryImageTransactions
select new
{
TBL.CategoryID,
TBL.CategoryName,
CategoryCount = objEntity.OfferCategoryMasters.Where(Lg => Lg.CategoryID == TBL.CategoryID && Lg.OfferMaster.EndDate > DateTime.Now.Date).Count(),
}
).ToList();
}
}
Error is : The specified type member ;Date; is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
You have to use EntityFunctions.TruncateTime to get the Date part of DateTime
using (var objEntity = new BlueCouponEntities())
{
return (from TBL in objEntity.CategoryMasters.AsEnumerable()
let IIT = TBL.CategoryImageTransactions
select new
{
TBL.CategoryID,
TBL.CategoryName,
CategoryCount = objEntity.OfferCategoryMasters.Where(Lg => Lg.CategoryID == TBL.CategoryID && Lg.OfferMaster.EndDate > EntityFunctions.TruncateTime(DateTime.Now)).Count(),
}
).ToList();
}
Since DateTime.Now is Constant at the moment, you could also write this
using (var objEntity = new BlueCouponEntities())
{
DateTime now = DateTime.Now.Date;
return (from TBL in objEntity.CategoryMasters.AsEnumerable()
let IIT = TBL.CategoryImageTransactions
select new
{
TBL.CategoryID,
TBL.CategoryName,
CategoryCount = objEntity.OfferCategoryMasters.Where(Lg => Lg.CategoryID == TBL.CategoryID && Lg.OfferMaster.EndDate > now).Count(),
}
).ToList();
}
It has nothing to do with the Count() function. As the exception says, its
a DateTime problem. Use DbFunctions.TruncateTime (EntityFunctions.TruncateTime is deprecated since EF6)
public static IList GetAllCategoryData()
{
using (var objEntity = new BlueCouponEntities())
{
return (from TBL in objEntity.CategoryMasters.AsEnumerable()
let IIT = TBL.CategoryImageTransactions
select new
{
TBL.CategoryID,
TBL.CategoryName,
CategoryCount = objEntity.OfferCategoryMasters.Where(Lg => Lg.CategoryID == TBL.CategoryID && EntityFunctions.TruncateTime(Lg.OfferMaster.EndDate) > EntityFunctions.TruncateTime(DateTime.Now.Date)).Count(),
}
).ToList();
}
}
Add following lines:
var today = DateTime.Now.Date;
Then use today instead of DateTime.Now.Date into linq query.

LINQ query with addition to WHERE clause

I want to run the following LINQ query twice but with an addition to the Where clause:
var TickList =
(from comp in Companies
join eqRes in Equity_issues on comp.Ticker equals eqRes.Ticker
where !comp.Coverage_status.Contains("dropp")
&& !comp.Coverage_status.Contains("Repla") && eqRes.Primary_equity.Equals('N')
select new
{
LocalTick = eqRes.Local_ticker.Trim(),
Exchange = eqRes.Exchange_code.Contains("HKSE") ? "HK" : (eqRes.Exchange_code.Contains("NSDQ") ? "NASDQ" : eqRes.Exchange_code),
Ticker = comp.Ticker.Trim()
}).ToList();
This query works fine, but I need to pass an additional parameter to the Where clause:
where !comp.Coverage_status.Contains("dropp")
&& !comp.Coverage_status.Contains("Repla") && eqRes.Primary_equity.Equals('N')
&& !comp.Coverage_status.Contains("Intl") <--- new addition
Is there a way to do this without being DRY? Isn't there an efficient way of doing this without repeating the query with the new addition?
// select additional Intl field (similar to Exchange)
var TickList =
(from comp in Companies
join eqRes in Equity_issues on comp.Ticker equals eqRes.Ticker
where !comp.Coverage_status.Contains("dropp")
&& !comp.Coverage_status.Contains("Repla") && eqRes.Primary_equity.Equals('N')
select new
{
LocalTick = eqRes.Local_ticker.Trim(),
Exchange = eqRes.Exchange_code.Contains("HKSE") ? "HK" : (eqRes.Exchange_code.Contains("NSDQ") ? "NASDQ" : eqRes.Exchange_code),
Intl = comp.Coverage_status.Contains("Intl") ? 1 : 0,
Ticker = comp.Ticker.Trim()
}).ToList();
// use LINQ to objects to filter results of the 1st query
var intl = TickList.Where(x => x.Intl = 0).ToList();
See the code below if you want to be DRY:
var keywords = new string[] { "dropp", "Repla", "Intl" };
var TickList = Companies
.Join(Equity_issues, c => c.Ticker, e => e.Ticker, (c, e) => new { c, e })
.Where(ce => ce.e.Primary_equity.Equals('N')
&& keywords.All(v => !ce.c.Coverage_status.Contains(v)))
.Select(ce => new
{
LocalTick = ce.e.Local_ticker.Trim(),
Exchange = ce.e.Exchange_code.Contains("HKSE")
? "HK"
: (ce.e.Exchange_code.Contains("NSDQ")
? "NASDQ"
: ce.e.Exchange_code),
Ticker = ce.c.Ticker.Trim()
})
.ToList();
Now you can run this query with any combination of keywords.
Probably overkill here but there have been situations where I've created a full blown query object that internally held an IQueryable and used methods on the object to add the where clause (mostly for letting users filter and sort their results)
public class TickList{
IQueryable<Foo> _query;
public TickList(){
_query = from comp in Companies
join eqRes in Equity_issues on comp.Ticker equals eqRes.Ticker
select new Foo {
LocalTick = eqRes.Local_ticker.Trim(),
Exchange = eqRes.Exchange_code.Contains("HKSE") ? "HK" :(eqRes.Exchange_code.Contains("NSDQ") ? "NASDQ" : eqRes.Exchange_code),
Ticker = comp.Ticker.Trim()
};
}
public void WhereCoverageContains(string text){
_query = _query.Where(x => x.Coverage_Status.Contains(text));
}
public void WherePrimaryEquityIs(string text){
_query = _query.Where(x => x.PrimaryEquity.Equals(text));
}
public List<Foo> ToList(){
return _query.ToList();
}
}
It's super verbose so use with caution. Sometimes it's possible to be too dry.

How to change in elegant way List<> structure

I am using LINQ to entitiy in my project.
I have this LINQ:
var result = (from inspArch in inspectionArchives
from inspAuth in inspArch.InspectionAuthority
select new
{
Id = inspArch.Id,
clientId = inspArch.CustomerId,
authId = inspAuth.Id
}).ToList();
After LINQ is executed result has this value :
Is there any elegant way (for example using LINQ or change above existing LINQ) to create from the list above, new list like that:
I haven't built this to see if it compiles, but this should work. You need to aggregate the Id and AuthId fields.
var result = (from inspArch in inspectionArchives
from inspAuth in inspArch.InspectionAuthority
select new
{
Id = inspArch.Id,
clientId = inspArch.CustomerId,
authId = inspAuth.Id
})
.GroupBy(g => g.clientId)
.select(s => new {
Id = string.Join(",", s.Select(ss => ss.Id.ToString())),
ClientId = s.Key,
AuthId = string.Join(",", s.Select(ss => ss.authId.ToString()).Distinct()),
}).ToList();
You need group by and you can apply String.Join on the resulting IGrouping:-
var result = (from inspArch in inspectionArchives
from inspAuth in inspArch.InspectionAuthority
group new { inspArch, inspAuth } by inspArch.CustomerId into g
select new
{
Id = String.Join(",",g.Select(x => x.inspArch.Id),
clientId = x.Key,
authId = String.Join(",",g.Select(x => x.inspAuth.Id)
}).ToList();
The tricky part here is to group both objects i.e. new { inspArch, inspAuth } because we need to access properties from both.
Update:
Since this is entity framework, it won't be able to translate the method String.Join to SQL, so we can bring back the grouped object to memory using AsEnumerable and then project it like this:-
var result = (from inspArch in inspectionArchives
from inspAuth in inspArch.InspectionAuthority
group new { inspArch, inspAuth } by inspArch.CustomerId into g
select g).AsEnumerable()
.Select(g => new
{
Id = String.Join(",",g.Select(x => x.inspArch.Id),
clientId = x.Key,
authId = String.Join(",",g.Select(x => x.inspAuth.Id)
}).ToList();

Categories

Resources