I have a Team table and a Matches table.
Team table:
Id Name
------------
1 TeamA
2 TeamB
If TeamA desires to play TeamB we will add a row in the Matches table
Matches table:
Id HomeTeamId RivalTeamId
-------------------------
1 1 2
If TeamB desires to challenge TeamA we will go about do the following
Matches table:
Id HomeTeamId RivalTeamId
-----------------------------
1 1 2
2 2 1
My Team and Match POCO (only relevant code) look like this:
public class Team : BaseEntity
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Match> HomeMatches { get; set; }
public ICollection<Match> RivalMatches { get; set; }
}
public class Match : BaseEntity
{
public int Id { get; set; }
public string Description { get; set; }
//navs
public Team HomeTeam { get; set; }
public int HomeTeamId { get; set; }
public Team RivalTeam { get; set; }
public int RivalTeamId { get; set; }
}
Problem: I guess the experts can already notice a possibility of a cycle up there that I'll come across during query. My problem is that I would like to query TeamA's matches. I would do the following for that
Team team= dbContext.Team.
Include("HomeMatches").
In the above team object I notice that BOTH the properties HomeMatches and RivalMatches appear filled.
I would just want the HomeMatches properties filled. I am only interested in those matches which TeamA has chosen to play, not in those where TeamA is a rival.
My question is that when I am clearly mentioned Include("HomeMatches"), why is the RivalMatches property also filled?
You can try using Explicit loading as in here
Related
I have two tables with one-to-one relationship.
public class Name
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Category { get; set; }
public int? NameId { get; set; }
[ForeignKey("NameId ")]
public virtual Name Name { get; set; }
}
I already have data in those tables.
I know the database relations are not supported to be changed.
Is it possible to change one-to-one relationships to many-to-many relationships?
What is the most suitable approach to overcome this requirement?
Yes, you can still change that, using migrations.
Step 1 is to create a linking table, like NameCategories, which looks something like this:
public class NameCategories
{
public int Id { get; set; }
public int NameId { get; set; }
public Name Name { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set; }
}
Step 2 is to reference this table in the tables you already have. In Name it would look like this
public class Name
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<NameCategory> Categories { get; set; }
}
Step 3 is to add a migration. You'll have some AddColumn() and some DropColumn() statements. In between them, when all the add stuff was executed but the drops not yet, you can add SQL code to carry over all the existing relations into the newly created table. After that, the old data will be deleted by the DropColumn() code. In your example, this would look something like this
INSERT INTO NameCategories (NameId, CategoryId)
SELECT (n.Id, c.Id) FROM Names n
JOIN Categories c on c.NameId = n.Id
WHERE ..
You can execute the SQL in the migration like this:
var sql = #"...";
Sql(sql);
I hope this helps you out!
I'm testing the following scenario (code below): create a shopping mall, with shops, with cashiers, with cash desks and persons operating it.
So I've created the following classes
Table Mall
Table Shop
Table CashDesk
Table Person
Basic classes with an ID and a name.
Then I need a derived class from Person being PersonCashier or PersonCustomer.
Since everything is related, I need to create intersection tables for the many to many scenarios.
I've created the following intersection tables:
The Mall contains shops: MallShop
The shop contains CashDesks: MallShopCashDesk
And the CashDesk containing cashiers: MallShopCashDeskPersonCashier
This just doesnt feel right. Can anyone help me out on a best practice
public class Mall
{
public int Id { get; set; }
[Required]
[MaxLength(100)]
public string Name { get; set; }
[Required]
public int NumberOfShopSpaces { get; set; }
}
public class Shop
{
public int Id { get; set; }
[Required]
[MaxLength(100)]
public string Name { get; set; }
}
public class CashDesk
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
}
public class Person
{
public int Id { get; set; }
[Required]
public string FullName { get; set; }
[Required]
public string Gender { get; set; }
}
public class PersonCashier : Person
{
[Required]
public int ShopId { get; set; }
public virtual Shop Shop { get; set; }
}
These are the base classes. How should i add a shop to a mall, a cashdesk to a shop, and a cashier to a cashdesk? (i've tried alot but posting the code would make the question look like spaghetti)
If this is code first then EF will make the relationship tables for you. You just need to add the relationships in your classes.
Exactly what they should be is not clear from your description because it depends on what relationships you want; if you want a many-many between PersonCashier and CashDesk it would be like this:
public class CashDesk
{
public List<PersonCashier> Cashiers { get; set; }
}
public class PersonCashier : Person
{
public List<CashDesk> CashDesks { get; set; }
}
(not showing all the fields for brevity)
public class Mall
{
public int Id { get; set; }
[Required]
[MaxLength(100)]
public string Name { get; set; }
[Required]
public int NumberOfShopSpaces { get; set; }
List<Shop> CurrentShops { get; set; }
}
public class Shop
{
public int Id { get; set; }
[Required]
[MaxLength(100)]
public string Name { get; set; }
public List<CashDesk> CashDesks { get;set; }
}
I added the list of shops to the Mall class and CashDesks to Shops. This gives you a list of shops in the mall, and a list of cashdesks in each shop and you can follow this method for everything else you need.
If you have a database, you will have a Mall table and a Shop table.
The Shop table can have a Foreign Key to the Mall table, that's how you link them and this will work with the class structure at the top.
Or, you can have another table called MallShops where you have 2 fields, one being the MallID, the other the ShopID. This is called a Link table.
Both approaches will work with the second allowing a more complex structure with lots of Malls linked to Lots of Shops.
I would start with the database structure first, make sure you cover all you need, then you can do the classes etc in a way that makes sense. If you use something like EntityFramework then it will create all the classes for you once your database structure is ready.
I'd like to define relationship where Student can have only one favorite Course. I expect it would look like this in DB:
STUDENT
ID
Name
FavoriteCourseID
COURSE
ID
Name
How to achieve this with entity framework? I'd prefer to specify it just by attributes. I tried:
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
public Course FavoriteCourse { get; set; }
public int? FavoriteCourseID { get; set; }
}
public class Class
{
public int ID { get; set; }
public string Name { get; set; }
}
which gave me this DB model:
STUDENT
ID
Name
FavoriteCourseID
COURSE
ID
Name
StudentID // how to remove this?
Note, that it may happen that several students have the same favorite class and therefore this is unacceptable solution.
Another question: what type of relationship this is? (1:1 / 1:N ?)
To specify 1 to 1 relationship, it is assumed, that primary key for the related entity matches the primary key of first entity. Also you should specify a virtual property to related entity:
public class Student
{
[Key]
public int ID { get; set; }
public string Name { get; set; }
public Course FavoriteCourse { get; set; }
public int? FavoriteCourseID { get; set; }
}
public class Class
{
[Key]
[ForeignKey("Student")]
public int ID { get; set; }
public string Name { get; set; }
public virtual Student Student { get; set; }
}
And it will be one-to-zero-or-one relationship. Check this tutorial.
If you will mark FavouriteCourse property with RequiredAttribute, it seems, that it will result in strong one to one relationship.
It will result in adequate database structure:
STUDENT
ID
Name
FavoriteCourseID
COURSE
ID
Name
However, if many students could have one favourite course, this structure will be a problem, as you want one-to-many instead of one-to-one. And you will have a duplicate records in database, because one course can refer only to one student. You have to think about your db design.
You can try this:
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
[ForeignKey("FavoriteCourseId")]
public Course FavoriteCourse { get; set; }
public int? FavoriteCourseId { get; set; }
}
Normally, you define one of the following relations:
Optional:Optional
Required:Optional
Optional:Many
Required:Many
Many:Many
Having Required:Required is not a usual relation, inserting the first entry with such a relation needs special treatment.
I Suppose you want Required:Many as in "Each student has one favorite course but many students may chose the same favorite course".
Consider the following database tables. Unfortunately the tables cannot be altered in any way.
Houses has an auto-increment ID field named Id, a string field named Name and an integer field named AreaId. The latter is not a foreign key to the Areas table.
Areas has a composite key consisting of AreaId, CountryId and LangId. An Area with the same AreaId can exist but with different CountryId and LangId. E.g.: There can be two rows with the same AreaId but different LangId.
NOTE: Why does a House have multiple Areas? A House doesn't have multiple Area's, it only has oneArea. TheArea`s table has a composite key, meaning that a specific row will have multiple translations. E.g.: Area ID 5 might have LangId 5 for English and LangId 3 for Spanish.
The two tables are described by the following two C# classes.
public class House
{
public int Id { get; set; }
[MaxLength(80)]
public string Name { get; set; }
public int? AreaId { get; set; }
[ForeignKey("AreaId")]
public List<Area> Areas { get; set; }
}
public class Area
{
public int AreaId { get; set; }
public int CountryId { get; set; }
public string LangId { get; set; }
public string Name { get; set; }
}
The composite key is defined in the context, exactly as stated in the docs.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Area>()
.HasKey(a => new { a.AreaId, a.CountryId, a.LangId });
}
For example let's get a list of all the Houses in the database, including their respective areas.
_context.Houses.Include(h => h.Areas).ToList();
The following SQL is generated in the output window and the resulting List contains Houses incorrectly matched with the Areas.
SELECT [a].[AreaId], [a].[CountryId], [a].[LangId], [a].[Name]
FROM [Areas] AS [a]
WHERE EXISTS (
SELECT 1
FROM [Houses] AS [h]
WHERE [a].[AreaId] = [h].[Id])
ORDER BY [a].[Id]
As you can see, EntityFramework relates [a].[AreaId] with [h].[Id] and not [h].[AreaId]. How can I express this relationship in EF?
You won't be able to map this correctly in EF. If you want House to refer to Area, the foreign key should consist of the same fields as Area's composite key, otherwise EF won't accept the mapping. A work-around could be to skip the mapping and to join the entities manually when necessary, but that conceals the real issue: poor design.
The major design flaw is that you have to duplicate an Area when translations are added. Now the question is -- and always will be -- Which record represents my physical Area entity? The basic premise of a relational database is that entities are represented by unique records. Your design violates that core principle.
Unfortunately the tables cannot be altered in any way.
Well, they should be! Leaving it this way shouldn't even be considered. You shouldn't work with a warped relational model, it's too pivotal for smooth application development.
The model, as I can piece it together from your description, should probably be something like this:
public class House
{
public int Id { get; set; }
public string Name { get; set; }
public int? AreaId { get; set; }
public Area Area { get; set; }
}
public class Area
{
public int Id { get; set; }
public int CountryId { get; set; }
public Country Country { get; set; }
public string Name { get; set; } // E.g. the name in a default language
public ICollection<AreaTranslation> AreaTranslations { get; set; }
}
public class AreaTranslation
{
public int AreaId { get; set; }
public int LanguageId { get; set; }
public string LocalizedName { get; set; }
}
public class Country
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Language
{
public int Id { get; set; }
public string Name { get; set; }
}
For this model you need one explicit mapping instruction (EF will infer the rest):
modelBuilder.Entity<AreaTranslation>()
.HasKey(a => new { a.AreaId, a.LanguageId });
You see that Area now genuinely represents a physical area out there. A House now naturally has one Area, not this weird collection of Areas that must be considered as one area somehow. The various languages come into play by the AreaTranslation junction class. I assume that an Area belongs to one Country.
Working in one project (Catering theme ) when I was designing the database I didn't take care about some thing , and now Is very hard to avoid some kine of errors(Circular error).
Suppose I have following scenario :
I have Meal object that should be composed from a list of semi-finished products (we will call it Product ) and list of simple Resources.
One Product is composed from a list of Resoruces and list of products.
So in real example this will look like this:
Meal: Pizza that contains list of Resoruces(cheese,dough) and list of Products : in our case will be just :Sauce.
Sauce will be composed from List of Resources(salt,Some cheese ,tomato Sauce) and a List of Products (in our case will be just one "Chopped tomatoes with salt")
So now I have following classes:
public class Resource
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ProductToProduct
{
public int Id { get; set; }
public Product MainProduct { get; set; }
public Product Component { get; set; }
public double Quantity { get; set; }
}
public class ProductToResource
{
public int Id { get; set; }
public Product Product { get; set; }
public Resource Resource { get; set; }
public double Quantityt { get; set; }
}
public class Meal
{
public int Id { get; set; }
public string Name { get; set; }
public IList<MealToProduct> MealToProducts { get; set; }
public IList<MealToResource> MealToResources { get; set; }
}
public class MealToResource
{
public int Id { get; set; }
public Meal Meal { get; set; }
public Resource Resource { get; set; }
public double Quantity { get; set; }
}
public class MealToProduct
{
public Meal Meal { get; set; }
public Product Product { get; set; }
public double Quantity { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public IList<ProductToResource> ProdcutToResources { get; set; }
public IList<ProductToResource> ProductToProducts { get; set; }
}
My problem is in relation between Product to Product.
Suppose I will have Product1, Product2 , Product3 , Product4.
Product 1 will be composed from something and Prodcut2, Product4.
Product2 will be composed from something and Prodcut3.
Prodcut 3 will be composed from something and Prodcut4.
Prodcut 4 will be composed from something and Prodcut1 , in this case when I will try to calcualte Cost for Product1 , or Product 4 I will get an Circular error.
So my problem is in ProductToProduct table.
My question is how I must to design tables to avoid this kind of errors .
I AM VERY SORRY FOR MY EXPLICATION BUT IT IS VERY HARD TO EXPLAIN THIS PROBLEM.
PLEASE ASK ME IF SOMETHING IS UNCLEAR.
THANKS FOR YOUR ATTENTION.
Note:This is not so important for this case but I am working in ASP.Net mvc , orm is Fluent Nhibernate.
Here's an example of a function you could use to detect whether a parent-child relationship exists. I have assumed that the product relationships are described in a table called ProductLink, which has two foreign keys to Product: ParentProductId and ChildProductId.
This function uses a recursive query to determine the complete list of products which are children of the product denoted by the argument #ParentProductId, then does a simple test to see whether #ChildProductId appears in that list.
create function dbo.ProductRelationshipExists
(
#ParentProductId int,
#ChildProductId int
)
returns bit
as
begin
declare #ChildExists bit = 0;
with ProductChildCTE as
(
-- Base case: Get the parent's direct children.
select ChildProductId from ProductLink where ParentProductId = #ParentProductId
-- Recursive case: Get the children's children.
union all
select
ProductLink.ChildProductId
from
ProductChildCTE
inner join ProductLink on ProductChildCTE.ChildProductId = ProductLink.ParentProductId
)
select #ChildExists = 1 from ProductChildCTE where ChildProductId = #ChildProductId;
return #ChildExists;
end
When someone tries to insert a record into ProductLink, you could use a test like this to determine whether the proposed parent and child are already in the table as child and parent, respectively, and disallow the insertion if so.
This was just a quick write-up to illustrate one possible approach; I should mention that I don't know how well the performance of this thing will scale as the table gets larger. Hopefully it will suffice for your case. If not, let me know how to improve it.