Materilizing related entities from SPs using EF Extensions - entity-framework

I have a stored procedure that returns a collection of my entity class objects. Since I want my navigation properties populated as well I'm using EF Extensions and I've written my own Materializer class.
But. My entity class has a navigation property of Type that points to a different entity. Stored procedure of course returns an ID from the lookup table (foreign key).
I would like to populate my types as if I was eagerly loading a related entity. How do I do that in my Materializer? Can I do it without having a stored procedure that returns two result sets?
I would like to implement something similar to what Include() extension method does on source selection in LINQ statement.

I've solved this myself some time ago. I'm just answering my own question if anyone else would need it.
So what kind of results should stored procedures return? It hugely depends on relation type. Lets say we have two tables: TableOne and TableTwo.
1:0..1 relation
In this case stored procedure should return both at once:
select t1.*, t2.*
from TableOne t1
[left] join TableTwo t2
on t2.key = t1.key
When they are 1:1, you can easily omit left.
1:MANY relation
In this case it's much easier to write stored procedures that return more results. Starting from those at side one so they will be prepared when you bind the many side table.
/* relation one */
select *
from TableOne
/* relation many */
select *
from TableTwo
But if you would still like to return a single result set, you should check for each record whether you already have a certain entity loaded. There's a method called FindOrAttach() that can help you. So each result will return both entities and you have to chech if they are loaded. If they are not, materialize... But as mentioned: it's much easier to return two result sets.
MANY:MANY relation
You should also write your stored procedure to return more results. In this case 3 of them.
/* first table */
select *
from TableOne
/* second table */
select *
from TableTwo
/* relation *:* */
select *
from TableOne2Table2
Materilize first two tables as usual and then call Attach for each record from thirs result loading by keys from TableOne and TableTwo. This will also populate entity set navigation properties.
I home this will help others as it helped me.

Related

DataAdapter.Update() and its use of the dataset relationship schema

I have a dataset with multiple tables and the necessary relations in place to call SQL statements in the proper order.
When the Adapter.Update() method is called, I presume it scours the relationships between all tables to determine the order in which it makes SQL calls.
For example:
A delete in Table A requires first a delete in Table B.
An insert in Table B first requires an insert into Table A.
How can I leverage the mechanism it uses to implement my own update strategy?
Reason being, rather than being able to allow the Adapter to perform the Updates, I instead need to call Stored Procedures.
* * * * * * EDIT * * * * * *
The dataSet is passed from the UI client to a back-end server component.
On the back end server, the DataAdapter.Update(dataSet) occurs.
Maybe you could use the RowUpdating Event on your Tables and call your Stored Procedure from there ... also set SqlRowUpdatingEventArgs.Status to SkipCurrentRow to prevent the standard Update Sql Command from being triggered and call SqlRowUpdatingEventArgs.Row.AcceptChanges() to set the RowState back to Unchanged ...

Table Valued Functions in EF Core

I am using EF Core 1.1 and have a query like
var list=from l in context.Users
where l.SomeProp==someVal
select l;
I have a UDF which returns a table of Id's and I basically want to generate the following query:
select * from Users where SomeProp=#someVal and SomeId in (select id from fn_myfunc(#id))
Is this possible to do?
I think you are limited to running a raw SQL query against the database to be able to use a table valued function. For example:
var query = #"SELECT * FROM Users WHERE SomeProp = {0}
AND SomeId IN (SELECT id FROM fn_myfunc({1})";
var users = context.Users
.FromSql(query, someProp, someId)
.ToList();
There are some limitations with this method. From the docs:
SQL queries can only be used to return entity types that are part of your model. There is an enhancement on our backlog to enable returning ad-hoc types from raw SQL queries.
The SQL query must return data for all properties of the entity type.
The column names in the result set must match the column names that properties are mapped to. Note this is different from EF6.x where property/column mapping was ignored for raw SQL queries and result set column names had to match the property names.
The SQL query cannot contain related data. However, in many cases you can compose on top of the query using the Include operator to return related data.
You can return related data (i.e. Include) like this:
var users = context.Users
.FromSql(query, someProp, someId)
.Include(u => u.OtherThings)
.ToList();
If you need to do anything more complex, then you would need to either drop down to using raw data access (like ADO.Net) or another ORM. I know people who use EF Core for the bulk of the work and then occasionally drop into Dapper for performance or raw SQL that doesn't suit EF.

PostgreSQL: A function to add parent & child records

I am writing a function to add a parent record and children records.
As far as I am aware, I should create appropriate data types, which I have simplified.
create type parenttype as (
data varchar -- data
);
create type childtype as (
parent integer, -- foreign key
details varchar -- data
);
This is a simplified version, omitting a number fields which add nothing to the question. However, they both also omit the primary key which will be generated.
I think the function would take the following form:
function adddata(parentdata parenttype, childdata childtype[])
-- etc
-- LANGUAGE plpgsql
I think I know what to do what to do with the data inside once it gets there.
The question is, how do I set the data before when I call the function? That is, how do I set values for the the parenttype and the array of childtype?
I have asked asked a related question for MS SQL Server, but I know that this requires a different approach.
Depends from what you call that function and where does this data come from.
Example 1
SELECT * FROM adddata(
row('somedata')::parenttype,
array[row(1::integer,'somedata'), row(2::integer,'somedata22')]::childtype[]
);
Example 2
SELECT adddata(
row(parent.column)::parenttype,
array_agg(row(child.parent_id,child.column)::childtype)
)
FROM parent
JOIN child ON child.parent_id = parent.id
GROUP BY parent.column;

How to update a value using string_agg without using a function?

I solved this problem with a function and I like my solution, but I want to know if there is a way to solve this problem without using functions. Here is the thing:
There are four tables that are relevant to this:
entities: entities of the system (tenants)
members: members of an entity
member_sets: sets of members
members_and_sets: table to join members and sets (many to many)
The member_sets table has a column named bits which is a binary representation of the set, so, for example, if an entity has 5 members and one specific set has the third member, the value of the bits column is 00100, the entity has three special kinds of sets: universe, empty and unit, their binary repesentation is: 11111, 00000 and 10000 respectively, assuming the unit set has the first member.
The challenge is keep this binary representation of the set up to date; Whenever one member is added to the entity, all binary representations must be updated. This is easy to do with a trigger and a function, my solution is this:
CREATE FUNCTION setbits(INTEGER) RETURNS VARBIT AS
$$SELECT STRING_AGG(belongs, '')::VARBIT AS setbits FROM (
SELECT LEAST(COALESCE(members_and_sets.set_id, 0), 1)::text AS belongs
FROM members LEFT JOIN members_and_sets
ON members.id=members_and_sets.member_id
AND members_and_sets.set_id=$1
GROUP BY members.id,members_and_sets.set_id
ORDER BY members.id)
AS bitsring;$$
LANGUAGE SQL
RETURNS NULL ON NULL INPUT;
-- calling this function in a trigger after inserting a new member:
UPDATE member_sets ms
SET bits=setbits(ms.id)
WHERE ms.entity_id=NEW.entity_id;
Now my question is: Can I do this without using a function? I tried with CTE but apparently I'm to noob to accomplish this; I couldn't pass the set_id to the must inner query so my solution was to wrap the query in a function and pass the set_id as an argument to the function. Again, this solution works perfectly, I just want to know if there is no way I can do this without a function call.
As your function body is simply a SELECT, you should be able to replace the function call with a subquery:
UPDATE member_sets ms
SET bits= (
SELECT STRING_AGG(belongs, '')::VARBIT AS setbits FROM (
SELECT LEAST(COALESCE(members_and_sets.set_id, 0), 1)::text AS belongs
FROM members LEFT JOIN members_and_sets
ON members.id=members_and_sets.member_id
AND members_and_sets.set_id=ms.id
GROUP BY members.id,members_and_sets.set_id
ORDER BY members.id)
AS bitsring
)
WHERE ms.entity_id=NEW.entity_id;

SQL Views - no variables?

Is it possible to declare a variable within a View? For example:
Declare #SomeVar varchar(8) = 'something'
gives me the syntax error:
Incorrect syntax near the keyword 'Declare'.
You are correct. Local variables are not allowed in a VIEW.
You can set a local variable in a table valued function, which returns a result set (like a view does.)
http://msdn.microsoft.com/en-us/library/ms191165.aspx
e.g.
CREATE FUNCTION dbo.udf_foo()
RETURNS #ret TABLE (col INT)
AS
BEGIN
DECLARE #myvar INT;
SELECT #myvar = 1;
INSERT INTO #ret SELECT #myvar;
RETURN;
END;
GO
SELECT * FROM dbo.udf_foo();
GO
You could use WITH to define your expressions. Then do a simple Sub-SELECT to access those definitions.
CREATE VIEW MyView
AS
WITH MyVars (SomeVar, Var2)
AS (
SELECT
'something' AS 'SomeVar',
123 AS 'Var2'
)
SELECT *
FROM MyTable
WHERE x = (SELECT SomeVar FROM MyVars)
EDIT: I tried using a CTE on my previous answer which was incorrect, as pointed out by #bummi. This option should work instead:
Here's one option using a CROSS APPLY, to kind of work around this problem:
SELECT st.Value, Constants.CONSTANT_ONE, Constants.CONSTANT_TWO
FROM SomeTable st
CROSS APPLY (
SELECT 'Value1' AS CONSTANT_ONE,
'Value2' AS CONSTANT_TWO
) Constants
#datenstation had the correct concept. Here is a working example that uses CTE to cache variable's names:
CREATE VIEW vwImportant_Users AS
WITH params AS (
SELECT
varType='%Admin%',
varMinStatus=1)
SELECT status, name
FROM sys.sysusers, params
WHERE status > varMinStatus OR name LIKE varType
SELECT * FROM vwImportant_Users
also via JOIN
WITH params AS ( SELECT varType='%Admin%', varMinStatus=1)
SELECT status, name
FROM sys.sysusers INNER JOIN params ON 1=1
WHERE status > varMinStatus OR name LIKE varType
also via CROSS APPLY
WITH params AS ( SELECT varType='%Admin%', varMinStatus=1)
SELECT status, name
FROM sys.sysusers CROSS APPLY params
WHERE status > varMinStatus OR name LIKE varType
Yes this is correct, you can't have variables in views
(there are other restrictions too).
Views can be used for cases where the result can be replaced with a select statement.
Using functions as spencer7593 mentioned is a correct approach for dynamic data. For static data, a more performant approach which is consistent with SQL data design (versus the anti-pattern of writting massive procedural code in sprocs) is to create a separate table with the static values and join to it. This is extremely beneficial from a performace perspective since the SQL Engine can build effective execution plans around a JOIN, and you have the potential to add indexes as well if needed.
The disadvantage of using functions (or any inline calculated values) is the callout happens for every potential row returned, which is costly. Why? Because SQL has to first create a full dataset with the calculated values and then apply the WHERE clause to that dataset.
Nine times out of ten you should not need dynamically calculated cell values in your queries. Its much better to figure out what you will need, then design a data model that supports it, and populate that data model with semi-dynamic data (via batch jobs for instance) and use the SQL Engine to do the heavy lifting via standard SQL.
What I do is create a view that performs the same select as the table variable and link that view into the second view. So a view can select from another view. This achieves the same result
How often do you need to refresh the view? I have a similar case where the new data comes once a month; then I have to load it, and during the loading processes I have to create new tables. At that moment I alter my view to consider the changes.
I used as base the information in this other question:
Create View Dynamically & synonyms
In there, it is proposed to do it 2 ways:
using synonyms.
Using dynamic SQL to create view (this is what helped me achieve my result).