Persisting Object Graph to SQL Server in a single transaction - c#

I am having an issue coming up with a solution that I think must be a common problem to be solved by anyone writing to a database. I keep thinking that there is an obvious solution that I'm overlooking and that's why I can't find an appropriate existing question here.
Imagine a situation where you need to let a user create a "Class", with "Students", and each "Student" is assigned one or more books. You have a well defined hierarchy, Class->Student->Book.
You have the following tables:
CREATE TABLE Classes (
ClassId int identity(1,1) primary key,
ClassName nvarchar(255)
)
CREATE TABLE Students (
StudentId int identity(1,1) primary key,
ClassId int,
StudentName nvarchar(255),
StudentImage image
)
CREATE TABLE StudentBooks (
StudentBookId int identity(1,1) primary key,
StudentId int,
BookName nvarchar(255)
)
With this contrived scenario, what are my options for saving this entire graph of new objects, while letting SQL server assign the identity columns, and keeping it all in one transaction? Assuming that a class has maybe 30 students, and each student has several books assigned.
I could create a transaction and make a separate call for each row in each table, returning SCOPE_IDENTITY for each new parent object so I can save each child while keeping RI intact.
I could use XML. I would like to avoid that, as the graph includes a byte array.
Any other options? I thought about passing each level of the hierarchy in a table parameter. I'm not sure how or if that would work. (Wouldn't I have to define a table type for each of my tables, essentially duplicating the schema?)
I can use SQL server 2012 for this.
Thank you!

You can use Entity Framework to achieve what you want.
There are lots of tutorials out there, but a good starting point is this one:
MSDN on getting started with Entity Framework
or the linked page
MSDN overview page on getting started
I would recommend the EDMX approach for your use-case.

Related

How should I handle one to many relationships to fill a data model

I'm trying to determine how I should handle a one to many relationship in my DB, when using the data to build a model in C#. Ideally, I'd like to make a single call to the DB. However, it seems that two (or more) calls might be required.
For simplicity, assume my tables look like this...
CREATE TABLE [dbo].[Users]
(
[userId] INT PRIMARY KEY IDENTITY(0,1),
[userName] NVARCHAR(500) NOT NULL
)
CREATE TABLE [dbo].[Tasks]
(
[taskId] INT PRIMARY KEY IDENTITY(0,1),
[description] NVARCHAR(1000) NOT NULL,
[userId] INT FOREIGN KEY REFERENCES [dbo].[Users](userId)
)
So each user can have many tasks. I have a stored procedure that will return the details of a user, that looks like this...
CREATE PROCEDURE [dbo].[sp_GetUserDetail]
#userId INT
AS
BEGIN
SELECT
[dbo].[Users].[userName] AS 'User',
[dbo].[Tasks].[description] AS 'Task Description'
FROM
[dbo].[Users]
INNER JOIN
[dbo].[Tasks]
ON
[dbo].[Tasks].[userId] = [dbo].[Users].[userId]
WHERE
[dbo].[Users].[userId] = #userId
END
This procedure returns as many rows, as tasks that are assigned to a user. The model I'm trying to fill, would look something like this.
public interface User
{
string Name { get; set; }
List<string> Tasks { get; set; }
}
I see my options as follows:
Use this code, and loop through the rows that are returned from the DB to build the Tasks list.
Call one stored procedure to return the data from the Users table, then another to get the data from the Tasks table.
Some (unknown to me magic) way to have a single stored procedure return all the data in a single row.
Some other option I don't even know about.
How is this problem typically handled by experienced Developers?
There are some language/framework specific answers which I won't cover (because C# is not my forte), but it's worth looking at "data binding", which is one of the features of the .Net framework. You could also look at ORM tools for C#.
The example you give - "how do I load child information for my parent" - is common, and you have to trade the number of database queries against the amount of data each query returns, and the complexity of your user interface code. For instance, if tasks have foreign keys to sub tasks (i.e. a self join), and task_type, and project_id, you have either:
1 query per table (your option 1): simplest to implement in the UI layer, simplest to implement in the database layer, but could easily cause dozens of database calls per screen.
1 query to retrieve all data for the screen (your option 2): single database hit, so should be faster, but complex UI and database logic; could potentially load the entire database into memory if you keep following foreign key relationships. Not all data may be necessary for the screen.
There is no "right" answer to this - it really depends on your application design.
However, there is an option you haven't mentioned (this is SQL Server-specific): a stored procedure can return multiple result sets. So, you could have one result set to provide the "header" data (user information), and one to provide task information.

How do I working with objects that can change from one client to another

I have a problem where I data for an object, for example billing file, where the data elements are different from client to client. More specifically the number of fields within the data and of different names. I am looking for a solution when working with the objects in C#.
Currently I have created tables for each client. The fields are specific to the client and I use a mapping process when uploading data. I also have dynamic queries in SQL Server to handle all crud processes. It all works pretty well but I believe there is a better solution and I believe saving Json data would be one of them. Pulling the Data I first query the headers of the table and then map the data to those headers for data grids and such. Again, I already have a working solution, but I believe there is a better solution and I am looking for suggestions with examples. By the way, I have thought about dynamic object, in C#, but it would appear you have to know what fields of the object are upfront.
I suggest that you should create mapping table, but there is no need to use something like dynamic sql, here are tables:
create table d_billing_object -- one row here means one field from your question
(
id int not null identity (1, 1) primary key
,name nvarchar(255) not null
)
create table d_billing_client
(
id int not null identity (1, 1) primary key
,name nvarchar(255) not null
)
create table d_billing_mapping
(
billing_client_id int not null
,client_billing_object_id int not null
,billing_object_id int not null
,constraint PK_d_billing_mapping primary key (billing_client_id, client_billing_object_id, billing_object_id)
,constraint FK_d_billing_mapping_d_billing_object foreign key (client_billing_object_id) references d_billing_object (id)
,constraint FK_d_billing_mapping_d_billing_object_2 foreign key (billing_object_id) references d_billing_object (id)
,constraint FK_d_billing_mapping_d_billing_client foreign key (billing_client_id) references d_billing_client (id)
)
After that you just need to create all billing objects and use them in mapping table for all clients you have.

Relational SQL Server Databases

I just registered here and this is my very first question. I hope I can explain myself clearly 'cause being a self-taught developer I don't quite speak the jargon.
My question: my project database holds employee information and has things like firstName, lastName, jobTitle, etc.. (static things).
My problem begins when I want to also start collecting all the clockIn & clockOut events of each employee. I can't think of an elegant way of adding these to my current database.
I don't know too much about databases but I think such things should either go in a totally different database or perhaps in a different table within the same database.
No idea which is the right answer.
The other thing is that these 2 databases or 2 tables need to somehow relate to each other somehow. When I select an employee from the list [data coming from the 1st DB or Tbl] and choose to view that specific employee's clock event history [data coming from the 2nd DB or Tbl] I should see that employee's clock in's & out's and NOT some other employee's clock events history.
A step-by-step tutorial is not necessary, but appreciated. Can you give me a quick explanation/outline of how this can be done ? If needed, I can add more details about
the project.
Thanks.
Like so:
CREATE TABLE Employees (
EmployeeId bigint IDENTITY(1,1) PRIMARY KEY,
FirstName nvarchar(50),
LastName nvarchar(50),
etc
)
CREATE TABLE EmployeeClocks ( -- rename as appropriate
EmployeeId bigint, -- foreign key
DateTime datetimeoffset(7),
ClockInType tinyint, -- values defined by an enum inside your program code
CONSTRAINT FK_EmployeeClocks_Employees FOREIGN KEY (EmployeeId) REFERENCES Employees (EmployeeId) ON DELETE CASCADE ON UPDATE CASCADE
)
Now for some queries:
Getting all of the clock-ins for an employee (assuming ClockInType = 1 for clock-ins, and 2 for clock-outs):
SELECT
DateTime
FROM
EmployeeClocks
INNER JOIN Employees ON EmployeeClocks.EmployeeId = Employees.EmployeeId
WHERE
Employees.FirstName = "Dick" AND
Employees.LastName = "Butt"
Read about database schema
You have to use two tables in a single database, Dai's answer showed the tables. just adding a few word if you re new to database then first read about it make a design idea than make database. because it very difficult to change in database when project is done.
Read some articles from here
database architecture
sql server database

Database design for compiling records from another table

What's the best way to design a table that reference multiple records from another table?
For example, there is a table called diary that stores subjects, descriptions and keywords, then another table called DiaryCompilation for combining all selected records into a book by just referencing the id from the diary.
What's the best way to create the DiaryCompilation?
I was thinking of consisting it into two fields: id, references
where in all selected records are placed in references, but is it a good practice or are there better approaches?
<--- Each record is a new entry of diary --->
Diary: ID, Subject, Description, Keywords
<--- Single record per compilation for summary info --->
DiaryCompilation: ID, Title
<--- Pages of Diary Compilation --->
DiaryCompilationEntries: DiaryCompilationID, DiaryID
Diary and DiaryCompilation would have a 1 to many relationship. Just make sure that the Id in the DairyCompilation is setup as the Primary Key and put a Foreign Key constraint on it so that it ties back to an Id in the Diary table. This will prevent you from deleting a diary and orphaning a record in the DiaryCompilation table. As long as you have a normalized data model you should be fine.
Keep your fields atomic. Placing several values in references field would make it much harder to query and to enforce referential constraints.
A 1:N relationship between parent and child is modeled by migrating parent's primary key into child. In your case, this would look something like this:
COMPILATION (
COMPILATION_ID PK
-- Other fields...
)
DIARY (
DIARY_ID PK
COMPILATION_ID FK(COMPILATION)
SUBJECT
DESCRIPTION
)
-- Not a good idea to have several keywords in a single field, so we need a separate table for keywords.
DIARY_KEYWORD (
DIARY_ID PK, FK(DIARY)
KEYWORD PK
)
If you actually want N:N relationship (i.e. diary can be part of more than one compilation), you'll need a dedicated table to hold these connections, something like this:
COMPILATION (
COMPILATION_ID PK
)
DIARY_IN_COMPILATION (
COMPILATION_ID PK, FK(COMPILATION)
DIARY_ID PK, FK(DIARY)
)
DIARY (
DIARY_ID PK
SUBJECT
DESCRIPTION
)
DIARY_KEYWORD (
DIARY_ID PK, FK(DIARY)
KEYWORD PK
)
What's the best way to create the DiaryCompilation?
Sounds like the best way might be to use a view instead of a table.

How can I Insert/Update into two related tables in one command?

A database exists with two tables
Data_t : DataID Primary Key that is
Identity 1,1. Also has another field
'LEFT' TINYINT
Data_Link_t : DataID PK and FK where
DataID MUST exist in Data_t. Also has another field 'RIGHT' SMALLINT
Coming from a microsoft access environment into C# and sql server I'm looking for a good method of importing a record into this relationship.
The record contains information that belongs on both sides of this join (Possibly inserting/updating upwards 5000 records at once). Bonus to process the entire batch in some kind of LINQ list type command but even if this is done record by record the key goal is that BOTH sides of this record should be processed in the same step.
There are countless approaches and I'm looking at too many to determine which way I should go so I thought faster to ask the general public. Is LINQ an option for inserting/updating a big list like this with LINQ to SQL? Should I go record by record? What approach should I use to add a record to normalized tables that when joined create the full record?
Sounds like a case where I'd write a small stored proc and call that from C# - e.g. as a function on my Linq-to-SQL data context object.
Something like:
CREATE PROCEDURE dbo.InsertData(#Left TINYINT, #Right SMALLINT)
AS BEGIN
DECLARE #DataID INT
INSERT INTO dbo.Data_t(Left) VALUES(#Left)
SELECT #DataID = SCOPE_IDENTITY();
INSERT INTO dbo.Data_Link_T(DataID, Right) VALUES(#DataID, #Right)
END
If you import that into your data context, you could call this something like:
using(YourDataContext ctx = new YourDataContext)
{
foreach(YourObjectType obj in YourListOfObjects)
{
ctx.InsertData(obj.Left, obj.Right)
}
}
and let the stored proc handle all the rest (all the details, like determining and using the IDENTITY from the first table in the second one) for you.
I have never tried it myself, but you might be able to do exactly what you are asking for by creating an updateable view and then inserting records into the view.
UPDATE
I just tried it, and it doesn't look like it will work.
Msg 4405, Level 16, State 1, Line 1
View or function 'Data_t_and_Data_Link_t' is not updatable because the modification affects multiple base tables.
I guess this is just one more thing for all the Relational Database Theory purists to hate about SQL Server.
ANOTHER UPDATE
Further research has found a way to do it. It can be done with a view and an "instead of" trigger.
create table Data_t
(
DataID int not null identity primary key,
[LEFT] tinyint,
)
GO
create table Data_Link_t
(
DataID int not null primary key foreign key references Data_T (DataID),
[RIGHT] smallint,
)
GO
create view Data_t_and_Data_Link_t
as
select
d.DataID,
d.[LEFT],
dl.[RIGHT]
from
Data_t d
inner join Data_Link_t dl on dl.DataID = d.DataID
GO
create trigger trgInsData_t_and_Data_Link_t on Data_t_and_Data_Link_T
instead of insert
as
insert into Data_t ([LEFT]) select [LEFT] from inserted
insert into Data_Link_t (DataID, [RIGHT]) select ##IDENTITY, [RIGHT] from inserted
go
insert into Data_t_and_Data_Link_t ([LEFT],[RIGHT]) values (1, 2)

Categories