LINQ Syntax for Current Revision - c#

How can I write this SQL statement using C# and LINQ? I am quering an Oracle database and the table has multiple revisions of the records. Therefore, I want onyl the current revision of each record contained in the table.
The SQL looks like this:
select TP_ID, TP_TEXT, TP_DEFN_SAKEY
from TP_DEFN tp1
where tp1.TP_ACTIVE_FLAG = 'Y' and
tp1.FAMILY_ID = 1 and
tp1.TP_DEFN_REV_DTS = (select max(TP_DEFN_REV_DTS)
from TP_DEFN tp2
where tp2.family_id = tp1.family_id and tp2.tp_id = tp1.tp_id )
order by TP_ID
TP_DEFN_REV_DTS is the date time field that stores the current revision.
I am a beginner with LINQ and have been struggling to find an workable solution. Every time that I try grouping in the LINQ query I get an error
GroupBy is not supported

Try something like this:
var res =
from tp1 in TP_DEFN
where tp1.TP_ACTIVE_FLAG == "Y" &&
tp1.FAMILY_ID == 1 &&
tp1.TP_DEFN_REV_DTS == (from tp2 in TP_DEFN
where tp2.FAMILY_ID == tp1.FAMILY_ID &&
tp2.TP_ID == tp1.TP_ID
select tp2.TP_DEFN_REV_DTS).Max()
orderby tp1.TP_ID
select new
{
tp1.TP_ID,
tp1.TP_TEXT,
tp1.TP_DEFN_SAKEY
};

Off the top of my head, and not knowing which LINQ provider you're using...
var q = from tp1 in Context.TP_DEFN
where tp1.TP_ACTIVE_FLAG == "Y"
&& tp1.FAMILY_ID == 1
&& tp1.TP_DEFN_REV_DTS
== Context.TP_DEFN.Where(tp2 => tp2.FAMILY_ID == tp1.FAMILY_ID
&& tp2.TP_ID == tp1.TP_ID)
.Max(tp2 => tp2.TP_DEFN_REV_DTS)
orderby tp1.TP_ID
select new
{
tp1.TP_ID,
tp1.TP_TEXT,
tp1.TP_DEFN_SAKEY
};

If you're using entity framework or linq-to-sql, you can just pass the direct sql if you want (although that'll prevent change tracking, at least by default).
For EF, use ObjectContext.ExecuteStoreQuery: http://msdn.microsoft.com/en-us/library/dd487208.aspx
For L2S, use DataContext.ExecuteQuery: http://msdn.microsoft.com/en-us/library/system.data.linq.datacontext.executequery.aspx

Related

Convert SQL query to linq in C# with Entity Framework

I need to convert below query to linq in C#. Can someone share a free online tool or if you have good command in linq then please rewrite in C# supportive linq.
select
PaymentHolidayReason as Reason,
EmploymentStatus,
Cast(count(1) as Decimal) as Count,
Cast((select count(1)
from MortgageApplications MortgageApps
where PaymentHolidayReason is not null
and EmploymentStatus is not null) as Decimal) as Total
from
MortgageApplications MortgageApps
where
PaymentHolidayReason is not null
group by
PaymentHolidayReason,
EmploymentStatus
I would suggest you try to use the LinqPad and refer to the link below: http://www.linqpad.net
If not satisfied try: http://www.sqltolinq.com but I prefer LinqPad
i have write below query and it's working as same above sql statement.
var totalCount = _caseManagerContext.MortgageApplications.Where(x => x.PaymentHolidayReason != null && x.EmploymentStatus != null).Count();
var result = from mapp in _caseManagerContext.MortgageApplications
where mapp.EmploymentStatus!=null && mapp.PaymentHolidayReason!=null &&
mapp.LastModifiedDate >= initialDate && mapp.LastModifiedDate <= finalDate
group mapp by new { mapp.PaymentHolidayReason, mapp.EmploymentStatus } into g
select new { g.Key.PaymentHolidayReason, g.Key.EmploymentStatus, MyCount = g.Count(), Total=totalCount };

using String.Split in a LINQ Where clause not working

I have below LINQ query:
List<string> paths = (from an in modelDb.MyTable
where an.Id == 0 &&
an.fileName.Split('_')[0] == "19292929383833abe838ac393" &&
an.fileName.Split('_')[1].ToUpper() == "FINANCE" &&
an.fileName.ToUpper().Contains("SIGNED")
select an.filePath).ToList();
... which is throwing below error on run-time:
Entity Framework 'ArrayIndex' is not supported in LINQ to Entities
fileName field in the LINQ query is a column in MyTable of string data type and contains strings like:
8845abd344ejk3444_FINANCE_SIGNED.pdf
4565abd34ryjk3454_FINANCE_UNSIGNED.pdf
477474jkedf34dfe4_MARKETING_UNSIGNED.pdf
and so on...
As stays in the message split will be not supported in the linq to sql and if you want to get result without additional methods, using Contains() should give you required result
List<string> paths = (from an in modelDb.MyTable
where an.Id == 0 &&
an.fileName.Contains("19292929383833abe838ac393") &&
an.fileName.Contains("FINANCE") &&
an.fileName.ToUpper().Contains("SIGNED")
select an.filePath).ToList();
Another way is to load all in memory and do split after that, e.g.
List<string> temp = (from an in modelDb.MyTable
where an.Id == 0
an.fileName.ToUpper().Contains("SIGNED")
select an.filePath).ToList();
paths = temp.Where(an =>
an.fileName.Split('_')[0] == "19292929383833abe838ac393" &&
an.fileName.Split('_')[1].ToUpper() == "FINANCE").ToList();

How to create dynamic Multiple And linq conditions in foreach loop

if (rowCount == 1)
{
query =
(from x in partJoinTableRepository.GetPartJoinQuery()
join y in partRepository.GetPartsQuery() on x.PartId equals y.Id
join z in partProductTypeReposiotry.GetPartProductTypesQuery() on x.PartId equals z.PartId
where y.IsSkipped == 0 && (y.IsDisabled != "Y" || y.IsDisabled == null) && z.CreatedDate == x.CreatedDate
&& x.CreatedDate == Convert.ToDateTime(fromDate) && cpaclassids.Contains(x.ProductTypeId.ToString())
select x).Cast<PartJoinTable>().AsQueryable();
predicate = PredicateBuilder.True(query);
}
else
{
query = query.Join(partJoinTableRepository.GetPartJoinQuery(), "PartID", "PartID", "inner", "row1", null).Cast<PartJoinTable>().AsQueryable();
// predicate = PredicateBuilder.True(query);
} //query contains multiple dynamic inner joins
//repids contains the list ,I used the predicate builder for the linq to create AND Queries
foreach(var item in repids)
{
predicate = PredicateBuilder.True(query);
if (typeid == "3")
{
predicate = predicate.And(z => ids.Contains(z.ProductTypeId.ToString()) &&
z.CreatedDate == Convert.ToDateTime(fromDate));
}
}
var count = query.Where(predicate).Distinct().Count();
the above line is taking long time to execute,ids contains the lists and query contains the linq query.basically I need to form a multiple "AND" conditions
//The Query is taking lot of time to execute and multiple and conditions are not working
Remove ToList to improve performance. Because ToList execute your query and retrieve object list to memory. But you need only count. you don't need objects.
var count = query.Where(predicate).Distinct().Count();
If I understood you right, your problem is that this query has a long running time. Let's see your code in the last line:
var count = query.Where(predicate).Distinct().ToList().Count();
In LINQ to SQL (and to entities), your query doesn't execute thought you use ToList(), ToArray() etc.. For example, consider the following query:
var strings = Db.Table
.Where((string s) => s.Contains("A")) // Will convert to something like WHERE s LIKE '%A%'
.Select(s => s.ToUpper()) // Will convert to something like SELECT upper(s)
.ToList(); // Here the query sends to the DB and executes
The final query is SELECT upper(s) FROM [Table] WHERE s LIKE '%A%'.
In you case, first you send the query to the DB and get all the objects corresponding to the condition (.Where()), and then get their count inside your app.
Instead, if you'll get from the DB only the count, the query will be faster:
var count = query.Where(predicate).Distinct().Count(); // No .ToList()! Here, .Count() executes the query.

Unable to form the proper Linq query using an "IN" list

I have the below SQL Query
;with cte as(
select a.*
from [dbo].[AccountViewModel] a
where a.COLLECTORID = 724852
and a.MONTH = 12
and a.YEAR=2015)
select *
from cte c
where c.DispCode in ('Deceased','DND','WN','WI','NC','NORESPONSE','SKIP','SHIFTED','SFU')
OR (c.DispCode in('PTP','DIB','WCE','DP') and convert(varchar(11), c.PTPDate) >=convert(varchar(11), getdate()))
OR (MONTH(c.LastPaymentDate) = 12 and YEAR(c.LastPaymentDate)=2015)
I need to convert this into an equivalent Linq query (C#).
The Cte part is working fine with the below program (I have cross checked the records)
private List<AccountViewModel> GetAllAcountsForLoggedInAgents()
{
var allAcountsForLoggedInAgents = new List<AccountViewModel>();
allAcountsForLoggedInAgents = new ViewModelDatabase()
.Accounts
.Where(a =>
a.COLLECTORID == 724852 &&
a.MONTH == DateTime.Now.Month &&
a.YEAR == DateTime.Now.Year
)
.ToList();
return allAcountsForLoggedInAgents;
}
However the part outside CTE is not working correctly (means improper records)
GetAllAcountsForLoggedInAgents()
.Where
(
a =>
("Deceased,DND,WN,WI,NC,NORESPONSE,SKIP,SHIFTED,SFU".Split(',').Any(x => x.Contains(a.DispCode)))
|| ("PTP,DIB,WCE,DP".Split(',').Any(b => b.Contains(a.DispCode)) && a.PTPDate >= DateTime.Now)
|| (a.LastPaymentDate.Value.Month == 12 && a.LastPaymentDate.Value.Year == 2015)
)
I believe that may be I am using "ANY" in a wrong way.
This condition is not the same as the IN clause
("Deceased,DND,WN,WI,NC,NORESPONSE,SKIP,SHIFTED,SFU".Split(',').Any(x => x.Contains(a.DispCode)))
because it searches a.DispCode in one of the strings. You should use equality instead:
("Deceased,DND,WN,WI,NC,NORESPONSE,SKIP,SHIFTED,SFU".Split(',').Any(x => x == a.DispCode))
This is not ideal, because Split operation is not free, so you don't want to do it as part of your query. Making a static array of strings:
static readonly string[] DispCodeFilter = new string[] {
"Deceased", "DND", "WN", "WI", "NC", "NORESPONSE", "SKIP", "SHIFTED", "SFU"
};
...
(DispCodeFilter.Any(x => x == a.DispCode))
Your In condition is incorrect. It can be fixed by adding an extension method. I am using a generic method, but you could make it type specific if you only need/want it for strings. I am using params, so you can either provide the items one by one or via a split.
public static bool In<T>(this T item, params T[] items) {
return items.Any(i=> Equals(item, i));
}
GetAllAcountsForLoggedInAgents().Where( a => a.DispCode.In
("Deceased","DND","WN","WI","NC","NORESPONSE","SKIP","SHIFTED","SFU")
|| (a.DispCode.In("PTP,DIB,WCE,DP".Split(',')) &&
a.PTPDate >= DateTime.Now)
|| (a.LastPaymentDate.Value.Month == 12 && a.LastPaymentDate.Value.Year == 2015)
)
One difference between this and the sql version, and a reason you may not want it to be generic, is that it is case sensitive: "wi" doesn't equal "WI".
Here are 2 simple rules for converting SQL to Linq
SQL Linq
============ ==========
IN (...) Contains
EXISTS (...) Any
where Contains is the corresponding Enumerable/Queryable method (not to be mixed with string.Contains).
According to this, your Linq criteria should be something like this
var DispCodes1 = new [] { "Deceased", "DND", "WN", "WI", "NC", "NORESPONSE", "SKIP", "SHIFTED", "SFU" };
var DispCodes2 = new [] { "PTP", "DIB", "WCE", "DP" };
GetAllAcountsForLoggedInAgents()
.Where
(
a =>
DispCodes1.Contains(a.DispCode)
|| (DispCodes2.Contains(a.DispCode)) && a.PTPDate >= DateTime.Now)
|| (a.LastPaymentDate.Value.Month == 12 && a.LastPaymentDate.Value.Year == 2015)
)
dasblinkenlight answer contains a good point, so you can make DispCodes1 and DispCodes2 static, but that's not essential.
Another thing to mention is that the way you did the "CTE part" is not equivalent to the SQL query, where cte is just a named subquery and the whole query executes in the database, while in your implementation the cte part is executed in the database, then gets materialized in the memory and the additional query is executed in the memory using Linq To Objects. To make it fully equivalent and let the whole query execute in the database, change the GetAllAcountsForLoggedInAgents result type to IQueryable<AccountViewModel> and remove ToList call.

A method that searches for entities, a certain property of which matches a given criteria in Entity Framework

Okay, I'm not sure whether the title of the question is clear enough, but I couldn't think of a simpler explanation in one sentence.
I'm making a search page for a ASP.NET web application. I want to allow the users to search using different filters.
I came up with a method that filters the Publications (these are the entities that the user will retrieve) based on the type of Categories that the user has chosen.
List<Publication> publications =
(from p in GetPublications()
where categories.Contains((int)p.CategoryId)
select p).ToList();
The above linq query is equivalent to the following sql one:
select * from Publication p where p.CategoryId in (#chosenCategoryIds)
Everything works perfect but the problem is that I want to allow the user to choose from more filters, for example, not only categories, but users, tags, titles and etc.
So, if I want this, I should make a lot of conditional statements checking whether the user has provided given filters or not, something like:
if (categories != null)
{
var publications = (from p in GetPublications()
where categories.Contains((int)p.CategoryId)
select p).ToList();
if (users != null)
{
publications = (from p in publications
where users.Contains((int)p.UserId)
select p).ToList();
}
}
else
{
// Handle other situations..
}
So, it turns out that I'm doing basically one and the same thing for different filters. My question is whether somebody knows or can think of a better way to achieve this? If not, let's take a look at the following method:
public List<InterpretumDAL.Publication> FilterPublications(List<InterpretumDAL.Publication> oldList, int[] values)
{
List<InterpretumDAL.Publication> newList =
(from p in oldList
where values.Contains((int)p.CategoryId)
select p).ToList();
return newList;
}
What I try to achieve with the method above is to filter a given list by a given array of values. The thing is that I don't know how to tell the method which property to compare, so I can call it something like this:
newList = FilterPublications(publications, categories, CATEGORY_PROPERTY_OF_PUBLICATION);
newList = FilterPublications(publications, users, USER_PROPERTY_OF_PUBLICATION);
newList = FilterPublications(publications, tags, TAG_PROPERTY_OF_PUBLICATION);
Edit:
According to LiquidPony's answer I did it this way:
var publications = (from p in GetPublications()
where (
(categories == null || categories.Length == 0 || (p.CategoryId.HasValue && categories.Contains((int)p.CategoryId)))
&& (users == null || users.Length == 0 || users.Contains(p.UserId))
&& (tags == null || tags.Length == 0 || tags.Contains(p.TagId))
)
select p).ToList();
Taking LiquidPony's answer, I think it would be better to do it like this by using ternary operators.
var query = from p in GetPublications()
where (
(categories != null ? categories.Contains((int)p.CategoryId) : true)
&& (users != null ? users.Contains((int)p.UserId) : true)
&& (tags != null ? tags.Contains((int)p.TagId) : true)
) select p;
If one of the variables isn't provided, that statement will simply evaluate to true and not be included in the SQL evaluation (as far as I know).
You want to avoid using OR's unless absolutely necessary. They are quite inefficient in SQL, and you can usually always find an alternative to get what you want without using them.
The simplest way would be to do something like this:
var query = from p in GetPublications()
where (
(categories == null || categories.Contains((int)p.CategoryId))
&& (users == null || users.Contains((int)p.UserId))
&& (tags == null || tags.Contains((int)p.TagId))
) select p;
That would work if you want to match all criteria. You can switch the && to || if you want to return entities which only match one criteria.

Categories