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]
Related
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?
I have a database with a table with only foreign keys. I am able to manually add data into this table, and then remove it with code. However, I am unable to add data to it. This may be because I don't actually know how to access my table, or I'm doing it wrong. Help is appreciated.
My foreign key only table:
CREATE TABLE UserCategory
(
CatID INT FOREIGN KEY REFERENCES Category(ID),
UserID NVARCHAR(128) FOREIGN KEY REFERENCES [User](ID),
CONSTRAINT PK_UserCategory PRIMARY KEY (CatID, UserID)
);
The code that links it in the model:
modelBuilder.Entity<Category>()
.HasMany(e => e.Users)
.WithMany(e => e.Categories)
.Map(m => m.ToTable("UserCategory").MapLeftKey("CatID").MapRightKey("UserID"));
As you can see, I have a table called what I created, and with the values expected.
However, when I try to access this table through code, I cannot access those values directly. I can delete values like this:
var userCats = thisUser.Categories.ToList();
foreach (var cat in userCats)
{
thisUser.Categories.Remove(cat);
}
But when I try and access the UserCategories properties, I get this:
What can I do to add new UserCategories to my database? If I just create a new Category, and add that the same way I delete one (but with Add, obviously) I am required to fill out Name. Then when I fill out Name, it tells me there's a key conflict, because it appears to be adding it to the Category table, not the UserCategory.
Here are the other two tables in question:
CREATE TABLE [User]
(
ID NVARCHAR(128) PRIMARY KEY,
FirstName VARCHAR(255) NOT NULL,
LastName VARCHAR(255),
JoinDate DATETIME,
ZipCode VARCHAR(25),
SearchRadius INT,
LoginToBusinessSide BIT
);
/*The categories businesses can fall under. */
CREATE TABLE Category
(
ID INT IDENTITY(1,1) PRIMARY KEY,
[Name] VARCHAR(255) UNIQUE NOT NULL,
);
Am I trying to access it wrong? How can I access it correctly? Thank you.
I'm not totally sure why this works, but I was able to finally save entries into my UserCategory table. This is my code:
for(var i = 0; i < categories.Length; ++i)
{
var user = db.Users.Find(thisUser.ID);
var cat = db.Categories.Find(categories[i]);
user.Categories.Add(cat);
}
I have to an active connection to both tables that the foreign keys are linked to, and then when I add one to the Categories table, it goes to the correct place.
If you understand exactly why this works, or if there's a better way, please do comment.
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?
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.
I am trying to learn linq to sql/objects as quick as possible. I have a database with a Category table, and a sub_category table. A Category can have many Sub Categories. As a lesson, I am getting a list of categories. User enters the primary key from a displayed category, and then I wnt to display all sub categories.
Displaying the categories was easy... But Entity Framework has removed the foreign keys from Sub Category! I expect to see a category Id in the sub category table (As there is in the SQL server database model).
Instead, I have some CategoryReference property... How do I manage this?
static void Main(string[] args)
{
BudgieMoneyEntities db = new BudgieMoneyEntities();
var categories = (
from category in db.categories
select category).ToList();
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Categories:");
Console.ForegroundColor = ConsoleColor.White;
foreach (category cat in categories)
{
Console.WriteLine(string.Format("{0:00} {1}", cat.category_id,
cat.description));
}
int categoryId = WaitForKey();
var subcategories = (
from subcategory in db.sub_category
where subcategory ?? ?
select subcategory).ToList();
}
The ?? is where I have got stuck... How do I do this where clause?
Try this:
var subcategories = (
from subcategory in db.sub_category
where subcategory.category.category_id == categoryId
select subcategory).ToList();
The names of the properties category and category_id can be different. You can look at your model in the EF designer what the actual names are. If the category property is missing, make sure you've got a foreign key relationship between the categories table and the subcategories table.
You also want to change the default names of entities and properies in the EF designer. For instance, use plural names for sub collections. For instance, use db.SubCategories instead of db.sub_category. That makes it much more readable.