I have a table that store "Tags" like so:
ProductTags
TagID PK
Name nvarchar(50) [not null]
and a M2M map table like this:
ProductTagMap
ProductID PK
TagID PK
Now let's say I remove a tag relationship (or all of them) from a Product like so:
// get our Product we are working on...
Product product = dataContext.Products.Where(p > p.ProductID = 1);
// this remove the link between the product and its tags
dataContext.ProductTagMaps.DeleteAllOnSubmit(product.ProductTagMaps);
//*** If these product-specific Tag/s is/are no longer used ***/
//*** by any other Products I'd like to delete them ***/
//*** How can this be done here? ***/
dataContext.SubmitChanges();
If these Tags (that were/are related to the specific product) are no longer related to any Products I'd like them to be deleted.
How can this be done in the code above (see the comment)?
Since that would require checking for other mappings in other records, you'd have to do a query to check for orphans:
var tags = from t in dataContext.Tags
where t.ProductTags.Count() == 0
select t;
dataContext.Tags.DeleteAllOnSubmit(tags);
And this gives you the tags you can delete.
Related
I'm new to entity framework,
To update a cell value, if the current field has a relationship with another sql table (entity class),
I want to get that foreign table (to show in a daragridview etc. to select a new value from there)
But I've no idea how to do that? Can you give me a little example to go on please?
Additional:
Unfortinatelly I found a solution based on sql (ADO.NET) instead of EF, to check a column that has a relationship or not
and if has, with which table?
the answer is following query for me;
select
tp.name 'ParentTable', cp.name as ForeignInParent, tr.name 'ForeignTable', cr.name ForeignValueMember, fk.name FK_Name
FROM sys.foreign_keys fk
INNER JOIN sys.tables tp ON fk.parent_object_id = tp.object_id
INNER JOIN sys.tables tr ON fk.referenced_object_id = tr.object_id
INNER JOIN sys.foreign_key_columns fkc ON fkc.constraint_object_id = fk.object_id
INNER JOIN sys.columns cp ON fkc.parent_column_id = cp.column_id AND fkc.parent_object_id = cp.object_id
INNER JOIN sys.columns cr ON fkc.referenced_column_id = cr.column_id AND fkc.referenced_object_id = cr.object_id
where tp.name='Payments' and cp.name = 'UserID'
I hope I can do this in EF
Imagine these two classes:
public class User
{
public int Id {get; set;}
public string Name{ get;set;}
....
}
public class Payment
{
public int Id {get; set;}
public int UserId {get; set;}
....
}
Now UserId is a foreign key:
If you are using entity framework (DB First) and classes are generated automatically then:
a. each User will have Payments as its subitems when put a . after it:
User user = SomeQuery...
you have user.Payments
b.Each payment has a user property:
Payment payment = somequery...
you can access the user who made the payment by payment.User
find the file with .edmx extension in your project. it shows a diagram of tables. each table has some fields, and at the bottom of fields, there is a Navigation Properties that shows Properties that are accessible because of the Foreign keys:
Find .tt files in your project. You can find auto-generated classes from the database there. all fields that are virtual are because of the foreign keys, as they are only navigational and do not really belong to that table (based on one-to-one, one-to-many, ... relations) they can be virtual Something, or virtual ICollecion.
Edit:
To check if a field is a foreign key in runtime as you stated in comments, you must use Reflection. check out this answer.
My project is a program like movie collection.
Main table is for storing movie data with fields like name,director,release year and ...
But each record must have another attribute named genre (genres).
Each movie may be categorized by one or several genres (Comedy, Horror, History, Romance,...)
Genre table contain two fields.ID:integer and Genre:vchar
For each movie, some of genre fields can be selected.
How I can do this?
thank you
The movie table must have some movie_id in order to uniquely identify each movie.
It must also have a column called genre_id (same as that in the genre table) to link the movie with each genre.
Using the following query, you can choose movie belonging to each genre:
SELECT M.movie_id
,M.movie_name
,G.genre_id
FROM movie M
LEFT JOIN genre G ON M.genre_id = G.genre_id
You need 3 tables to do this work correctly.
Table Moovie :
Moov_ID
Moovie_Name
Moov_DirectorID
...
Table Genres:
Genre_ID
Genre_Name
...
Table Moovie_Genre (Whic will link moovies and genres)
Mg_ID
Mg_MoovieID
Mg_GenreID
Like this you can have a many to many relation.
You can use a string field in your moovie table in order to use comma seperated values to stock genreIDs but, I prefere to have a many to many relation.
Just to expand on Oskon's answer; The script might look like this: (I've used temporary tables here - just remove the # to make them permanent)
----create the tables
CREATE TABLE #tblMovie
(
MovieID int NOT NULL
,MovieName varchar(100) NULL
,MovieDirectorID int NULL
----add more fields here if you want
)
CREATE TABLE #tblGenre
(
GenreID int NOT NULL
,GenreName varchar(100) NULL
)
CREATE TABLE #tblMovieGenre ----this is where the genres of the movies are listed
(
MovieGenreID int NOT NULL --id for this table (just for indexing)
,MovieID int NOT NULL --movie id from #tblmovie
,GenreID int NOT NULL --genre id from #tblGenre
)
----insert the information
INSERT INTO #tblMovie (MovieID,MovieName,MovieDirectorID)
VALUES (1,'Movie 1 Name',3)
,(2,'Movie 2 Name',10)
,(3,'Movie 3 Name',2)
INSERT INTO #tblGenre (GenreID, GenreName)
VALUES (1,'Comedy')
,(2,'Horror')
,(3,'Action')
,(4,'Thriller')
----add more genres
INSERT INTO #tblMovieGenre (MovieGenreID,MovieID,GenreID)
VALUES (1,1,1) --movie 1 is a comedy
,(2,2,3) --movie 2 is an action movie
,(3,3,3),(3,3,4) --movie 3 is an action/thriller movie
----Now find the genres for a movie (run this bit as many times as you want)
SELECT
T3.GenreName
FROM
#tblMovie T1
INNER JOIN #tblMovieGenre T2
ON T1.MovieID = T2.MovieID
LEFT OUTER JOIN #tblGenre T3
ON T2.GenreID = T3.GenreID
WHERE
T1.MovieName = 'Movie 3 Name' --put your movie name here
I have a table called Students and a table called Majors, Students and Majors are joined by MajorId I have set this relationship already and have set the foreign key in the schema. When I access my Student object how can I return the MajorName column (this comes from the Majors table)? The only options I have in intellisense is Major_1, Major_1Reference, MajorId .
Major_1 should be a navigation property leading to the appropriate Major entry, so you should be able to access the Major's properties like this:
from s in ctx.Students
select s.Major_1.MajorName
You can use linq join statement like this to make query on the two tables...
var q = from s in Students
join m in Majors on s.MajorId equals m.MajorId
select new { m.MajorName };
I have three tables
Employee (ID numeric, Name varchar)
Login (ID numeric, UserName varchar, Password varchar)
EmployeeLogin (ID numeric, EmployeeID, LoginID)
Relation is one employee can have multiple login. How will I get all the Login Name of a particular Employee.
I am able to fetch single record using the code given below but how will I get multiple records
using (var context = new AllEntities())
{
var query = from c in context.Employees
where c.ID == 9
select c;
}
The EmployeeLogin table seems redundant if you only have a one-to-many relationship between Employee and Login. You could just place a column EmployeeId in the Login table. The setup you have right now supports many-to-many between Employee and Login.
If you change your model according to my suggestion you could then get all Logins for a EmployeeId like this:
var query = from c in context.Logins
where c.EmployeeID == 9
select c;
If you keep your current model you could get all logins for an employee id like this:
var query = from l in context.Logins
join el in context.EmployeeLogins
on l.LoginId equals el.LoginId
where el.EmployeeID == 9
select l;
You should have all of the Logins in a navigation property on the Employee entity. See this tutorial:
http://www.asp.net/web-forms/tutorials/getting-started-with-ef/the-entity-framework-and-aspnet-getting-started-part-1
You can let the Entity Framework get the related data automatically or you can do it manually; for descriptions of lazy vs. eager loading see these tutorials:
http://www.asp.net/web-forms/tutorials/getting-started-with-ef/the-entity-framework-and-aspnet-getting-started-part-2
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/reading-related-data-with-the-entity-framework-in-an-asp-net-mvc-application
I am trying to get a single property from a joined table where a non-PK in my main table is joined to the PK of the foreign table. Below is an oversimplified example of what I am trying to accomplish (I do not want to reference the foreign entity):
Tables:
CREATE TABLE Status
(
Id int,
Body text,
CategoryId int
)
CREATE TABLE Category
(
Id int,
Name text
)
SQL to generate:
SELECT Id, Body, CategoryId, Category.Name AS CategoryName
FROM Status
LEFT JOIN Category ON Category.Id = Status.CategoryId
I am trying to map the join like this in the StatusMap but it seems to be joining on the two primary keys (where Status.Id = Category.Id):
Join("Category" m =>
{
m.Optional();
m.KeyColumn("CategoryId");
m.Map(x => x.CategoryName, "Name");
});
As far as I know the only way around this using Fluent is to map to a view as you currently are doing. Join() will always map to the primary key of the parent table. The KeyColumn method specifies the key column for the child table only, which in your case is the Category table.
To achieve the desired SQL using your simplified version above you'd probably want to use References to define a many-to-one relationship between status and category.