C# Recursive Query - c#

I'm working on a project where i've used C# to populate a single MSSQL table of URLs from multiple sources.
The table contains link redirect info (example structure below).
RequestedURL, RedirectedURL
www.123.com, www.123.com/123
www.123.com/123, www.123.com/1234/link.asp
www.123.com/1234/link.asp, www.123.com/12345/link.asp
I'm very new to C# and need to write some sort of recursive Query to go through each redirectedurl, if it is in the requestedurl then to find the associate redirectedurl. Some URLs may have multiple redirects.

Since you have this data in your SQL Server database, one possible approach would be CTE's with recursion. This explanation looks a little confusing at first, but I think if you scroll down to the example it will be clear how to do this.
Without repeating the entire explanation here, this is an example of such a query:
USE AdventureWorks2008R2;
GO
WITH DirectReports (ManagerID, EmployeeID, Title, DeptID, Level)
AS
(
-- Anchor member definition
SELECT e.ManagerID, e.EmployeeID, e.Title, edh.DepartmentID,
0 AS Level
FROM dbo.MyEmployees AS e
INNER JOIN HumanResources.EmployeeDepartmentHistory AS edh
ON e.EmployeeID = edh.BusinessEntityID AND edh.EndDate IS NULL
WHERE ManagerID IS NULL
UNION ALL
-- Recursive member definition
SELECT e.ManagerID, e.EmployeeID, e.Title, edh.DepartmentID,
Level + 1
FROM dbo.MyEmployees AS e
INNER JOIN HumanResources.EmployeeDepartmentHistory AS edh
ON e.EmployeeID = edh.BusinessEntityID AND edh.EndDate IS NULL
INNER JOIN DirectReports AS d
ON e.ManagerID = d.EmployeeID
)
-- Statement that executes the CTE
SELECT ManagerID, EmployeeID, Title, DeptID, Level
FROM DirectReports
INNER JOIN HumanResources.Department AS dp
ON DirectReports.DeptID = dp.DepartmentID
WHERE dp.GroupName = N'Sales and Marketing' OR Level = 0;
GO

You could create a dictionary with RequestedUrl as the key and RedirectedUrl as the value. So once you find the requestedUrl you could find its redirectedURL and if that redirectedURL has a redirectedURL, you could find that too.

If I get you right, you want a neat small C# function to find the last redirection, right?
In that case this should do it:
string GetRedirectionFromDatabase(string requestUrl)
{
// Fetch redirect form DB, or if none exists return null
}
string GetFinalUrl(string requestUrl)
{
var redirection = GetRedirectionFromDatabase(requestUrl);
return redirection != null ? GetFinalUrl(redirection) : requestUrl;
}

Related

Group data and retrieve every line of the grouping with Entity Framework

I was thinking that maybe, once the grouped data are retrieved in the C# part, I would be able loop through the list of items that were grouped.
var res = db.Commandes.Where(t => t.idMatiere == mod.idMatiereChoisie).GroupBy(t => t.UA_idCa);
foreach(var group in res)
{
foreach(var groupedLines in group)
{
// Always a single line, this loop is useless
}
}
It seems the logic applied here is more like SQL than C#: the grouping result in a single line and you won't see all the grouped items.
It's not a problem that I can't overcome
Tactic I will use: instead of grouping, I'll just query all the lines, and then, while looping, I will verify if UA_idCa is different form the previous data and that will means the next "group" has been reached.
But I wonder... How does someone normally do this cleanly, if it's possible?
Do you have to query again to retrieve a group's content?
Or is the "Tactic I will use" closer to what's best?
This problem is a matter of the combination of SQL server AND Entity Framework.
Seems like one of the value in the grouped part (a value that is different for all the line inside the group) must be marked as not null.
Because when looking for what could be a key, entity doesn't give a damn about nullable values : they could be unique, they could be never null, EF won't even check that.
Once it is marked as NOT NULL in the sql part, EF suddenly understand that there could multiple different unique values in the grouped part...
So basically This :
ALTER view [dbo].[Commandes] as
SELECT top(50000000)
isnull(ex.unitAdm, '000') UnitAdm
,c.id as idCahier
,isnull(ex.unitAdm, '000') + cast(c.id as nvarchar(6)) as UA_idCa
,c.NomCahier
,[Qte]
,c.prix as PrixCahier
,sc.id, 0 as idSousCahier /* THIS IS WHAT I COULD NOT COMPLETELY RETRIEVE
because it could be null ? */
,sc.NomCahier as sousCahier
,sc.prix as PrixSC
,m.id as idMatiere
,m.Code
,m.NomMatiere
,ep.id as idEpreuve
,ep.Titre
FROM [CahierExamen] cex
join Cahier c on c.id = cex.Fk_Cahier
join Examen ex on cex.FK_Examen = ex.id
join epreuve ep on ex.FK_Epreuve = ep.id
join Matiere m on ep.FK_Matiere = m.id
left join SousCahier sc on c.id = sc.FK_Cahier
order by code, unitAdm, idCahier
GO
As been changed to this:
ALTER view [dbo].[Commandes] as
SELECT top(50000000)
isnull(ex.unitAdm, '000') UnitAdm
,c.id as idCahier
,isnull(ex.unitAdm, '000') + cast(c.id as nvarchar(6)) as UA_idCa
,c.NomCahier
,[Qte]
,c.prix as PrixCahier
,isnull(sc.id, 0) as idSousCahier /* WOW, NOW EF UNDERSTAND
THERE COULD BE MULTIPLE DIFFERENTS VALUES ONCE DATA ARE GROUPED*/
,sc.NomCahier as sousCahier
,sc.prix as PrixSC
,m.id as idMatiere
,m.Code
,m.NomMatiere
,ep.id as idEpreuve
,ep.Titre
FROM [CahierExamen] cex
join Cahier c on c.id = cex.Fk_Cahier
join Examen ex on cex.FK_Examen = ex.id
join epreuve ep on ex.FK_Epreuve = ep.id
join Matiere m on ep.FK_Matiere = m.id
left join SousCahier sc on c.id = sc.FK_Cahier
order by code, unitAdm, idCahier
GO

Linq correlated subquery to same table on multiple columns

I've looked at several other questions related to correlated subqueries but it's still not clear to me how to accomplish what I need. I'm using Entity Framework and C#, and have a table called STEWARDSHIP with the following columns:
STEWARDSHIP_ID (the primary key)
SITE_ID
VISIT_DATE
VISIT_TYPE_ID
I need to identify cases where the same combination of SITE_ID, VISIT_DATE, VISIT_TYPE_ID exists more than once because it could represent a duplicate entry made by end users in error, and then I need to report on the details of these entries. In SQL I would do this by joining to the temporary result of a GROUP BY/HAVING like so:
SELECT * FROM stewardship AS s2,
(SELECT site_id, visit_type_id, CAST(visit_date AS DATE) AS visit_date
FROM stewardship
GROUP BY site_id, visit_type_id, CAST(visit_date AS DATE)
HAVING COUNT(*) > 1) AS s
WHERE s2.site_id = s.site_id
AND s2.visit_type_id = s.visit_type_id
AND CAST(s2.visit_date AS DATE) = s.visit_date
What's the best way to accomplish this in Linq?
Since you're open to a different approach that should be more performant, here is the new SQL to get what I think you're after.
select distinct s1.*
from stewardship s1
inner join stewardship s2 on
s1.stewardship_id <> s2.stewardship_id and
s1.site_id = s2.site_id and
s1.visit_type_id = s2.visit_type_id and
cast(s1.visit_date as date) = cast(s2.visit_date as date)
order by s1.site_id, s1.visit_type_id
Now, to translate that to LINQ, you can use the following statement.
var duplicates = (
from s in Stewardships
join s2 in Stewardships
on new { s.Site_id, s.Visit_type_id, s.Visit_date.Date } equals new { s2.Site_id, s2.Visit_type_id, s2.Visit_date.Date }
where s.Stewardship_id != s2.Stewardship_id
select s)
.Distinct()
.OrderBy(s => s.Site_id)
.ThenBy(s => s.Visit_type_id)
Note that you cannot use anything other than an equijoin for expression joins, so I had to put the non-equijoin (ensuring our matches aren't on the same record via PK) in the where expression. You could also accomplish this with lambdas via the Except() extension method.
The order by is there for readability of the results and to match the SQL statement above.
I hope this helps!
It would be fairly similar to what you've already got.
from s in context.stewardships
group s by new {s.site_id, s.visit_type_id, visit_date} into g
where g.Count() > 1
select g;
This would give you groups of stewardships with similar values. You could "flatten" those results with a SelectMany afterward, but you might find them more useful to work with in groups.
Note that you may need to use SqlFunctions or something to do the equivalent of the cast to date.

Writing a subquery using LINQ in C#

I would like to query a DataTable that produces a DataTable that requires a subquery. I am having trouble finding an appropriate example.
This is the subquery in SQL that I would like to create:
SELECT *
FROM SectionDataTable
WHERE SectionDataTable.CourseID = (SELECT SectionDataTable.CourseID
FROM SectionDataTable
WHERE SectionDataTable.SectionID = iSectionID)
I have the SectionID, iSectionID and I would like to return all of the records in the Section table that has the CourseID of the iSectionID.
I can do this using 2 separate queries as shown below, but I think a subquery would be better.
string tstrFilter = createEqualFilterExpression("SectionID", strCriteria);
tdtFiltered = TableInfo.Select(tstrFilter).CopyToDataTable();
iSelectedCourseID = tdtFiltered.AsEnumerable().Select(id => id.Field<int>("CourseID")).FirstOrDefault();
tdtFiltered.Clear();
tstrFilter = createEqualFilterExpression("CourseID", iSelectedCourseID.ToString());
tdtFiltered = TableInfo.Select(tstrFilter).CopyToDataTable();
Although it doesn't answer your question directly, what you are trying to do is much better suited for an inner join:
SELECT *
FROM SectionDataTable S1
INNER JOIN SectionDataTable S2 ON S1.CourseID = S2.CourseID
WHERE S2.SectionID = iSectionID
This then could be modeled very similarily using linq:
var query = from s1 in SectionDataTable
join s2 in SectionDataTable
on s1.CourseID equals s2.CourseID
where s2.SectionID == iSectionID
select s1;
When working in LINQ you have to think of the things a bit differently. Though you can go as per the Miky's suggestion. But personally I would prefer to use the Navigational properties.
For example in your given example I can understand that you have at-least 2 tables,
Course Master
Section Master
One Section must contain a Course reference
Which means
One Course can be in multiple Sections
Now if I see these tables as entities in my model I would see navigational properties as,
Course.Sections //<- Sections is actually a collection
Section.Course //<- Course is an object
So the same query can be written as,
var lstSections = context.Sections.Where(s => s.Course.Sections.Any(c => c.SectionID == iSectionID)).ToList();
I think you main goal is, you are trying extract all the Sections where Courses are same as given Section's Courses.

How to create a criteria in NHibernate that represents an OR between two EXISTS?

This one has been making my head hurt (which is easy since I'm a NHibernate newbie): how can I represent the following query (T-SQL) through the Criteria API?
DECLARE #pcode VARCHAR(8)
SET #pcode = 'somecode'
SELECT d.*
FROM document d
WHERE EXISTS (
SELECT 1
FROM project p
WHERE p.id = d.projectId AND p.code = #pcode)
OR EXISTS (
SELECT 1
FROM job j INNER JOIN project p ON p.id = j.projectId
WHERE j.id = d.jobId AND p.code = #pcode)
(A Document has two possible associations, Project or Job. Only one of them has a value at a given time; the other has null.)
The goal is to load all Documents that are directly associated with a given Project or indirectly through a Job.
Thanks.
I believe this could do the trick:
DetachedCriteria dCriteria1 = DetachedCriteria.For<Project>("project")
.SetProjection(Projections.Property("project.Id"))
.Add(Restrictions.EqProperty("doc.projectId", "project.Id"));
DetachedCriteria dCriteria2 = DetachedCriteria.For<Job>("job")
.SetProjection(Projections.Property("job.Id"))
.CreateCriteria("Projects", "p")
.Add(Restrictions.EqProperty("doc.jobId", "job.Id"))
.Add(Restrictions.Eq("p.code", "somecode"));
var documents = NHibernateSessionManager.Session.CreateCriteria<Document>("doc")
.Add(Restrictions.Or(
Subqueries.Exists(dCriteria1),
Subqueries.Exists(dCriteria2))).List<Document>();
The above code is derived from the sample query that you provided. If it is not entirely accurate you can change it a bit to make it workable in your solution.

Advice on removing multiple sub-queries (which contains a join) by optimizing Linq To SQL query

I'm working on adding globalization to my product cataloge and I have made it work. However, I feel that the underlaying SQL query isn't performing as well as it could and I could need some advice on how to change my Linq To SQL query to make it more efficient.
The tables that are used
Product contains a unique id for each product. This column is called EntityID
TextTranslation contains the globalized text. The columns in this table are CultureID (a string), TextID (a reference to the text), Value (the actuall globalized text).
Text contains the mapping between a globalized text and a product. There is also a column which indicates which type of text it is (like name, description and so on)
TextType contains the definition (id, name and description) for a text type.
var culturedTexts =
from translation in ctx.TextTranslations
join text in ctx.Texts on translation.TextId equals text.TextId
where translation.CultureId == "en-EN"
select new
{
text.EntityId,
text.TextTypeId,
translation.Value,
};
var products =
from p in ctx.Products
let texts = culturedTexts.Where(i => i.EntityId == p.EntityId)
select new Model.Product
{
Description = texts.Where(c => c.TextTypeId == (int)TextType.Description).SingleOrDefault().Value,
Name = texts.Where(c => c.TextTypeId == (int)TextType.Name).SingleOrDefault().Value
};
When this is executed I get a query which looks like
SELECT (
SELECT [t1].[Value]
FROM [Common].[TextTranslation] AS [t1]
INNER JOIN [Common].[Text] AS [t2] ON [t1].[TextId] = [t2].[TextId]
WHERE ([t2].[TextTypeId] = 2) AND ([t2].[EntityId] = [t0].[EntityId]) AND ([t1].[CultureId] = 'sv-SE')
) AS [Description], (
SELECT [t3].[Value]
FROM [Common].[TextTranslation] AS [t3]
INNER JOIN [Common].[Text] AS [t4] ON [t3].[TextId] = [t4].[TextId]
WHERE ([t4].[TextTypeId] = 1) AND ([t4].[EntityId] = [t0].[EntityId]) AND ([t3].[CultureId] = 'sv-SE')
) AS [Name]
FROM [Catalog].[Product] AS [t0]
So each globalized text (Name, Description) in the LINQ query gets its own sub query and associated join. Is it possible to streamline this a bit and remove each text type getting its own join and subquery?
Well, given that they are getting different TextTypeId values, how would you prefer the TSQL to look? If you do a single JOIN, you'll have to put in a messy SELECT CASE or similar to discriminate between type "1" and type "2".
One option would be to to simply bring back all the suitable rows and do the final projection in-memory at the client, but to be honest I expect that the SQL optimizer will make light work of that TSQL anyway... especially if that query hits a good spanning index.

Categories