I'd like to do something like a conditional include or select based on the value of a column in my parent table.
Parent table:
Guid Id
nvarchar(50) Name
int Type
Guid ObjectId
Child 1
Guid Id
nvarchar(50) Name
Child 2
Guid Id
nvarchar(50) Name
When I populate my model, I need to do a condition based on what type id is in the parent table.
For example
If the row Type == 1, then the include / select needs to be done on Child 1 for Id and Name.
If the type == 2 then from Child 2.
You would of course not be able to do a normal include, so would a select be the better option here? How would I go about it?
Related
I have DB migration where FormTemplateId - foreign key and I need to set default value as Id of the latest record in [dbo].[FormTemplates]:
migrationBuilder.AddColumn<int>(
name: "FormTemplateId",
table: "ContainerMasterTypes",
type: "int",
nullable: false,
defaultValueSql: "SELECT TOP 1 [Id] FROM [dbo].[FormTemplates] ORDER BY [Id] DESC");
and have error:
Subqueries are not allowed in this context. Only scalar expressions are allowed.'
What's wrong? How can I fix that error?
Entity Framework only allows you to do what you can do already in SQL Server. For example, with the following table setup:
create table dbo.FormTemplates (
Id int not null identity(1,1),
Name nvarchar(50) not null
);
insert dbo.FormTemplates (Name) values (N'Foo'), (N'Bar'), (N'Baz');
create table dbo.ContainerMasterTypes (
Id int not null identity(1,1),
Name nvarchar(50) not null
);
Attempting to modify the ContainerMasterTypes table to add the new column with a subquery in the default constraint as you are doing would fail...
alter table dbo.ContainerMasterTypes
add FormTemplateId int not null
constraint DF_ContainerMasterTypes_FormTemplateId
default (SELECT TOP 1 [Id] FROM [dbo].[FormTemplates] ORDER BY [Id] DESC);
... with the error message that you are seeing in .NET:
Msg 1046 Level 15 State 1 Line 3
Subqueries are not allowed in this context. Only scalar expressions are allowed.
To do this in SQL Server you would instead wrap the query in a Scalar User-Defined Function and reference that from the default constraint, i.e.:
create function dbo.LatestFormTemplateId()
returns int as
begin
return (SELECT TOP 1 [Id] FROM [dbo].[FormTemplates] ORDER BY [Id] DESC)
end
go
alter table dbo.ContainerMasterTypes
add FormTemplateId int not null
constraint DF_ContainerMasterTypes_FormTemplateId
default (dbo.LatestFormTemplateId());
Testing that with data...
insert dbo.ContainerMasterTypes (Name, FormTemplateId) values (N'Something', 1);
insert dbo.ContainerMasterTypes (Name) values (N'Else');
select * from dbo.FormTemplates;
select * from dbo.ContainerMasterTypes;
Would yield the results:
Id
Name
1
Foo
2
Bar
3
Baz
Id
Name
FormTemplateId
1
Something
1
2
Else
3
You need to do the same thing from Entity Framework as well, creating a Scalar UDF that wraps your query and then adding your new column with the default constraint referencing the UDF.
Suppose that I have a table Product
CREATE TABLE Product
(
Id INT PRIMARY KEY,
Name VARCHAR(100) NOT NULL,
PersonId INT FOREIGN KEY REFERENCES Person(Id)
)
CREATE TABLE Person
(
Id INT PRIMARY KEY,
FirstName VARCHAR(100) NOT NULL,
LastName VARCHAR(100) NOT NULL,
SupervisorId INT FOREIGN KEY REFERENCES Person(Id)
)
Person is a rather vanilla example of a table that references itself.
Suppose that I want to query my Product table and populate a class with the relavant product information, as well as the ID, name, and supervisor, of every person in the chain of command regarding that product.
Suppose that I naively write the following C# (assuming I'm using AutoMapper, as well):
var dbQuery = context.Products;
var entities = dbQuery.ToArray();
var products = Mapper.Map(entities);
This will work okay only so long as the number of products is small. This is because Entity Framework will initially only query the Product table, and only fill in the Person entities as they are queried. Using something like AutoMapper here is essentially like doing SQL in a loop, and causes performance issues when the number of rows in Product becomes large.
So suppose instead, I write this:
var dbQuery = context.Products.Include(x => x.Person);
var entities = dbQuery.ToArray();
var products = Mapper.Map(entities);
Now, I'm including the first Person row associated with a row in Product, but it's still problematic because each Person has one or more supervisors to be queried.
Is there some way I can tell Entity Framework to recursively query my database so that I get the entire command-chain of people and supervisors associated with a single product so that when AutoMapper tries to map these onto my classes, no new queries are required?
How can I auto-increment a number that is part of a string value in a SQL Server database?
For example, here is my table:
EMP_ID EMPNAME EMPSECTION
EMP_1 ROSE S-11
EMP_2 JANE R-11
When I add a new record, what I would like to do is automatically increment the number that follows EMP_. For example, EMP_3, EMP_4, etc.
one option is to have a table that has an autoincrement id field. Then you can write a trigger on this table that on insert, fires an insert on the autoincrement table and fetches the current value. Then concat that value on to the end of EMP_
By C# It's very to do , each time you want to insert a new row before inserting that row you should generate the key by following these steps :
1- get a list of your ID field
2- Do a for each loop to find tha maximum key value , something like this :
int maxID=1;
for each(var l in list)
{
if(int.Parse(l.ID.Replace("EMP_",""))>maxID)
{
maxID=int.Parse(l.ID.Replace("EMP_",""));
}
}
maxID=maxID+1;
string ID="EMP_"+maxID.Tostring();
And ID is your new ID !
but if your application is accessed by multiple programs (example : consider It's a website) I really don't suggest you to do something like this cause : 1. It's time consuming 2. In some condition same key value from multiple clients might be generated and you will have error while inserting .
You can have identity column in your table and display 'EMP_' appended to its value in your user interface. If you want to do it custom way, you'll need a sequence table
Create a sequence table
Sequence
-------------------
Seq_Name | Seq_Val
-------------------
EMPLOYEE | 0
Then you need a Stored Procedure to perform this
BEGIN
declare #curVal int
Select #curVal = Seq_Val+1 From Sequence Where Seq_Name='EMPLOYEE'
UPDATE Sequence SET Seq_Val = Seq_Val+1 Where Seq_Name='EMPLOYEE'
Insert into Employee Values ('EMP_'+Cast(#curVal As Varchar), 'Rose', 'S-11')
END
You can do something like:
create table dbo.foo
(
id int not null identity(1,1) , -- actual primary key
.
.
.
formatted_id as 'emp_' + convert(varchar,id) , -- surrogate/alternate key
constraint foo_PK primary key ( id ) ,
constraint foo_AK01 unique ( formatted_id ) ,
)
But I can't for the life of me think of just why one might want to do that.
I need a little of help. This is my design to organize several categories.
Category 1
Sub Category 1.1
Sub Category 1.1.1
Sub Category 1.2
Sub Category 1.3
It would be a collection. I want to store it in a database, but I don't know how can I model it in a database table. I'm using SQL Server CE.
UPDATE:
I forgot put the objective number in the class (1.1, 1.1.1).
You'd have a table something like this:
category
id (primary key, not null)
name (text, not null)
parent_category_id (foreign key to category.id, nullable)
Then, if a category has a parent, you reference the id of that other row. So the table is self-referential. Toplevel categories have a null parent_category_id.
When building tables like this you do need to be careful that you don't create a circular reference.
I'd use a simple Recursive Relation. Each Category should have a unique ID (primary key) and an optional field specifying its parent, which would be a foreign key mapping back to the same table. Categories with a NULL parent are top-level categories.
The page I linked to also has information on how you can query this structure to find top-level or mid-level Categories.
For this, you can have a table where an item can reference its parent, if any.
If the ParentId column is NULL, the category is a root one. If not, the parent is referenced.
You can then find the subcategories of a category by walking through the table and searching for items with ParentId equal to Id of the category.
Note that ParentId must be indexed for better performance, and that there must be a foreign key of ParentId to Id to ensure the validity of your data.
Storing recursively the categories:
private void SaveCategoryRecursively(Category category)
{
foreach (var subCategory in category.SubCategories)
{
query(#"
insert into [dbo].[Categories] ([Id], [ParentId], ...)
values (#id, #parentId, ...)", ...);
this.SaveCategoryRecursively(subCategory);
}
}
public void SaveCategories(IEnumerable<Category> rootCategories)
{
foreach (var category in rootCategories)
{
query(#"
insert into [dbo].[Categories] ([Id], [ParentId], ...)
values (#id, NULL, ...)", ...);
this.SaveCategoryRecursively(category);
}
}
Here is my recommendation. Create Three tables. I am assuming that each table has different columns
Category { Id, Name,...}
SubCategory {Id, Name, ..., **ParentID**} [ParentID is a FK from Category table Id Column]
SubSubCategory {Id, Name, ..., **SubParentID**} [SubParentID is a FK from SubCategory table ParentID Column]
Using role based permissions, and say each row in a table represents an entity e.g. the Products table, each row represents a Product entity.
How could you provide Product level security based on roles?
e.g. the Sales group has access to Products with ID's 1,234,432,532,34
Multiple roles can be given permissions on any given product.
The goal is to provide an effecient database call for a query like:
var products = ProductDao.GetProductsByRole(234); // roleID = 234
Many-to-Many relations are stored in a separate table:
create table Products(
ProductId int not null identity (1,1),
Name nvarchar(256) not null,
Description nvarchar(max),
constraint PK_Products primary key (ProductId),
constraint UNQ_Products_Name unique (Name));
create table Roles(
RoleId int not null identity (1,1),
Name nvarchar(256) not null,
Description nvarchar(max),
constraint PK_Roles primary key (RoleId),
constraint UNQ_Roles_Name unique (Name));
go
create table ProductRolePermissions (
ProductId int not null,
RoleId int not null,
constraint FK_ProductRolePermissions_Products
foreign key (ProductId)
references Products(ProductId),
constraint FK_ProductRolePermissions_roles
foreign key (RoleId)
references Roles(RoleId));
go
create unique clustered index CDX_ProductRolePermissions
on ProductRolePermissions (RoleId, ProductId);
create unique nonclustered index NDX_ProductRolePermissions
on ProductRolePermissions (ProductId, RoleId);
go
create function dbo.GetProductsByRole( #roleId int)
returns table
with schemabinding
as return (
select ProductId
from dbo.ProductRolePermissions
where RoleId = #roleId);
go
insert into Products (Name)
values ('P1'), ('P2'), ('P3'), ('P4');
insert into Roles (Name)
values ('G1'), ('G2');
insert into ProductRolePermissions (ProductId, RoleId)
values (1,1), (3,1), (2,2), (3,2);
go
select 'Products permitted for G1', p.*
from dbo.GetProductsByRole(1) r
join Products p on r.ProductId = p.ProductId;
select 'Products permitted for G2', p.*
from dbo.GetProductsByRole(2) r
join Products p on r.ProductId = p.ProductId;
Things get a little more complicated if you want to follow the classical grant/deny/revoke permission model for read/write/full access with multiple role memberships.