Identify which tree nodes are leaves in lazy load WCF REST API - sql

Using: SQL Server 2008, WCF 4 REST, EF
I have an adjacency list table representing a tree
TABLE Category
(
CatId int IDENTITY(1,1) NOT NULL PRIMARY KEY,
ParentId int NULL,
Name nvarchar(50) NOT NULL
)
I am creating a WCF REST API to allow the client to build the tree in a lazy load way. Doing the query to get the children of a node (nodeid below) is straight-forward. What i'm running into is the need to identify which nodes are leaf nodes. (Note: I've removed all error handling, count = 0 handling, null handling, etc. from the code below)
tree = _context.Categories
.Where(c => c.ParentId == nodeid)
.Select(p => new TreeNode
{
id = p.CatId,
parentId = p.ParentId ?? -1, // -1 = NULL in data struct
name = p.Name,
isleaf = true // how to figure this out?
}).ToList();
Any ideas? I'm OK going to a stored proc for this query and have thought of using CTE, but i don't want to recurse through the entire tree - just get the children of the specified node.
EDIT (20 Jan, 10:40am)
I've decided to alter the DB Schema to add the "IsLeaf" bit column. I then did an update to set the IsLeaf accordingly - which means i don't have to dynamically figure it out at runtime. Probably more efficient, but I'm still curious how i would go about it. Please advise.

There is no recursive functions in LINQ (yet?). So while it's somewhat easy in MSSQL, in LINQ I'd suggest doing the following (pseudocode):
// These two lines is one LINQ statement
level = select all root nodes (ParentID == NULL)
add level nodes to result
// C# loop
while level is not empty
// The loop body except for the assignment in the end is one LINQ statement
next = select all nodes that have parentId in level
for each node in next
find parent node
add to result with updated parent info
// C# assignment
level = next
As you may see it's a combination of LINQ and C#. Also it may worth bringing the content of the table locally before doing all this.
Also you may need to update the algo to check for cycles if necessary.

Related

LinQ to SQL : Update on table data not working

I have a LinQ query which is intended to Update the table concerned.
The code is as follows:
LINQHelperDataContext PersonalDetails = new LINQHelperDataContext();
var PerDetails1 = (from details in PersonalDetails.W_Details_Ts
where details.UserId == userId
select details).First();
PerDetails1.Side = "Bridge";
PerDetails1.TotalBudget = 4000000;
PersonalDetails.SubmitChanges();
However, this change/update does not get reflected in the DB. Also,this does not throw any exception.Please suggest.
Make sure W_Details_Ts has one (or more) member properties marked as primary key. L2S can't generate update or delete statements if it does not know the underlying table's PK member(s).

Structuring many update statements in SQL Server

I'm using SQL Server. I'm also relatively new to writing SQL... in a strong way. It's mostly self-taught, so I'm probably missing key ideas in terms of proper format.
I've a table called 'SiteResources' and a table called 'ResourceKeys'. SiteResources has an integer that corresponds to the placement of a string ('siteid') and a 'resourceid' which is an integer id that corresponds to 'resourceid' in ResourceKeys. ResourceKeys also contains a string for each key it contains ('resourcemessage'). Basically, these two tables are responsible for representing how strings are stored and displayed on a web page.
The best way to consistently update these two tables, is what? Let's say I have 5000 rows in SiteResources and 1000 rows in ResourceKeys. I could have an excel sheet, or a small program, which generates 5000 singular update statements, like:
update SiteResources set resoruceid = 0
WHERE siteid IS NULL AND resourceid IN (select resourceid
from ResourceKeys where resourcemessage LIKE 'FooBar')
I could have thousands of those singular update statements, with FooBar representing each string in the database I might want to change at once, but isn't there a cleaner way to write such a massive number of update statements? From what I understand, I should be wrapping all of my statements in begin/end/go too, just in-case of failure - which leads me to believe there is a more systematic way of writing these update statements? Is my hunch correct? Or is the way I'm going about this correct / ideal? I could change the structure of my tables, I suppose, or the structure of how I store data - that might simplify things - but let's just say I can't do that, in this instance.
As far as I understand, you just need to update everything in table SiteResources with empty parameter 'placement of a string'. If so, here is the code:
UPDATE a
SET resourceid = 0
FROM SiteResources a
WHERE EXISTS (select * from ResourceKeys b where a.resourceid = b.resourceid)
AND a.siteid IS NULL
For some specific things like 'FooBar'-rows you can add it like this:
UPDATE a
SET resourceid = 0
FROM SiteResources a
WHERE EXISTS (select * from ResourceKeys b where a.resourceid = b.resourceid and b.resourcemessage IN ('FooBar', 'FooBar2', 'FooBar3', ...))
AND a.siteid IS NULL
Let me see if I understood the question correctly. You'd like to update resourceid to 0 if the resourcemessage corresponds to a list of strings ? If so, you can build your query like this.
UPDATE r
SET resourceid = 0
FROM SiteResources r
JOIN ResourceKeys k ON r.resourceid = k.resourceid
WHERE k.resourcemessage IN ('FooBar', ...)
AND r.siteid IS NULL;
This is using an extended UPDATE syntax in transact-sql allowing you to use a JOIN in the UPDATE statement. But maybe it's not exactly what you want ? Why do you use the LIKE operator in your query, without wildcard (%) ?
With table-valued parameters, you can pass a table from your client app to the SQL batch that your app submits for execution. You can use this to pass a list of all the strings you need to update to a single UPDATE that updates all rows at once.
That way you don't have to worry about all of your concerns: the number of updates, transactional atomicitty, error handling. As a bonus, performance will be improved.
I recommend that you do a bit of research what TVPs are and how they are used.

sql to return nested result set?

Is it possible to have a single sql query return a nested structure rather than doing recursive db calls to build up the array or object?
I'm using something similar to this pseudo code to build up :
parentCategory = 'SELECT *
FROM Category
WHERE child_category IS NULL
AND ParentIDNo IS NULL';
while parentCategory do
childCategory = 'SELECT *
FROM Category
WHERE parent_id = parentCategory.id';
if (parentCategory.id)
do recursive 'SELECT *
FROM Category
WHERE parent_id = parentCategory.id';
end
Cat_1
-child_1
-child_2
--grandchild_1
Cat_2
-child_1
-child_2
--grandchild_1
Check out recursive ctes assumes sql 2005 or later
If you modify your table to contain a delimited path to the top of the tree then you don't have to do this recursively.
For example if you had the tree path be "cat_1|child_2|grandchild_1" for the grandchild_1 node of the tree then you would be able to split the string for loading into a tree as well as be able to determine the level in the tree you were at. Also when you select from the table you would be able to order by the path and the tree would come out exactly as you wanted to draw it.
the negative is that you would have to maintain this path on any changes to the tree.
That's very possible if you are using SQL 2005 and up. For a primer on hierarchical query see this: http://www.ienablemuch.com/2010/04/simple-hierarchical-query-display.html

Entity Framework 4 returns a mangled mess if underlying data doesn't reflect the Entity Primary Key

We have a stored procedure that has a select statement in it:
select convert(int, c.ID) as ID,
convert(nvarchar(255), c.name) as name,
convert(varchar(32), a.state) as state
from customer c join address a on
c.addressid = a.ID
where c.name like #custNameSpec
This produces two records when executed in a T-SQL window:
ID Name State
1 Robert PA
2 Rob VA
When executed in Entity Framework 4 as a function import, it returns two records, but the first record is duplicated:
ID Name State
1 Robert PA
1 Robert PA
We deleted the function import and the imported function, recreated it, etc. We also added those SQL convert() statements above to guarantee Entity Framework understands the data types coming back from the server.
What could we do to fix it? What causes duplicates like that?
Our tests include:
var myresult3 = myUOW.DC.GetAdDir(todaysdate: null, store_nbr: 14,
adtype: null).ToList();
var myresult4 = DB.GetAdDir(todaysdate: null, store_nbr: 14,
adtype: null).ToList();
Both return the same incorrect result. The SQL profiler shows this call:
exec [dbo].[GetCust] #todaysdate=NULL,#custNameSpec='Rob',#adtype=NULL
EDIT:
Apparently, the business rules changed. The POCO generated from Entity Framework had a primary key improperly set, so it returned the correct quantity of fields, but "removed" duplicates by making all duplicates the same (based on the POCO primary key fields.)
There was MergeOption referenced in other remotely related questions that may explain why this happens.
When you create a unique restriction on an Entity, like a primary key, and you have duplicates coming from the data, Entity Framework will repeat the first duplicate record for all fields for each duplicate found.
In other words, Entity Framework returns a mangled mess if your underlying data doesn't reflect the primary key correctly.

Help with SQL/LINQ Debugging

I'm having trouble with the following statement, which is returning the error "Sequence contains no elements":
var vUser = (from u in this.dcLAUNCHOnline.aspnet_Users
where u.UserName.Equals(this.wCreateUser.UserName)
select u).Single();
The SQL being generated:
SELECT [t0].[ApplicationId],
[t0].[UserId],
[t0].[UserName],
[t0].[LoweredUserName],
[t0].[MobileAlias],
[t0].[IsAnonymous],
[t0].[LastActivityDate],
[t0].[FirstName],
[t0].[LastName],
[t0].[Address_Street],
[t0].[Address_City],
[t0].[Address_Province],
[t0].[Address_Country],
[t0].[Address_PostalCode],
[t0].[Telephone_Main_AreaCode],
[t0].[Telephone_Main_Prefix],
[t0].[Telephone_Main_LineNumber],
[t0].[Telephone_Main_Extension],
[t0].[Telephone_Mobile_AreaCode],
[t0].[Telephone_Mobile_Prefix],
[t0].[Telephone_Mobile_LineNumber],
[t0].[Telephone_Mobile_Extension],
[t0].[Telephone_Other_AreaCode],
[t0].[Telephone_Other_Prefix],
[t0].[Telephone_Other_LineNumber],
[t0].[Telephone_Other_Extension],
[t0].[Gender],
[t0].[BirthDate]
FROM [dbo].[aspnet_Users] AS [t0]
WHERE [t0].[UserName] = #p0
-- #p0: Input NVarChar (Size = 20; Prec = 0; Scale = 0) [six.string#gmail.com]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 3.5.30729.4918
When run in SQL server management studio, the script does return the row I expect (which is in the table)
I defined p0 with this line:
DECLARE #p0 NVarChar(20) = 'six.string#gmail.com'
Any ideas why this is failing? Thanks!
.Single() always fails if the collection it is called upon is empty or contains more than one element. SQL Server does not return any rows, which must be the case here.
You could either use .FirstOrDefault() or .SingleOrDefault() and check the return value against null, depending on whether you expect a single element to be returned by your query.
E.g. you have a unique constraint on the row "UserName" you filter upon, you should use .SingleOrDefault(). If null is returned, no row has been found. Multiple rows wil never be returned.
The InvalidOperationException that you are getting is thrown only when the query yielded no results.
The only thing that comes to my mind, (since you say that you are sure that the row exists on the database), is that you are maybe connecting to other database.
Check your DataContext's connection string and make sure you are querying the same database as in Management Studio.
Edit: BTW, you are querying directly the SqlMembershipProvider aspnet_Users table, to find users by UserName, you might want to give a look to the Membership.FindUsersByName method.