How can we manage the Relation between two table in POCO Entity? - razor

I am a newbie to POCO.I have two tables like tb1 and tb2.Suppose we have a PK and FK relation between these tables.When it come to POCO CF how can we manage this relations?I have a done a sample by following a article.
public abstract class Person
{
public string Name { get; set; }
public int DepartmentId { get; set; }
public virtual Department Department { get; set; }
}
public class Collaborator : Person
{
public int CollaboratorId { get; set; }
public string ManagerCode { get; set; }
public virtual Manager Manager { get; set; }
}
Why they have used the abstract and virtual keywords? Can any one explain me the how can we manage the relations?

I assume you are using a model-first approach. You will want to use the Fluent API to define the relationships. Here is a good article on how to do this.

Related

How can I get a JOIN result from a dbcontext class to format and to send as a JSON string?

I'm doing some practice with Entity Framework and I want to create a web-api backend, which is able to manage requests by interacting with a MySql database and by responding with JSON strings in the message body of the replies. I'm quite new to it and I'm trying to learn it solving problem by problem in the project.
An example of the working code at the moment is:
public string getFilmById(int id)
{
return JsonSerializer.Serialize(ctx.Films.ToList().Where(filmToGet => filmToGet.Id == id));
}
Which generates the string:
[{"Id":7,"Title":"Dune","Genre":"Sci-fi","Duration":155,"Direction":[],"Interpretation":[]}]
Now, what I want to do is to include in the string the directors and the actors of the film. I've created all the models of the backend with a database-first approach, by importing and converting MySql tables into classes and by generating the dbcontext class automatically. This implies that I also have the Direction class to link Film and Director, because the relationship is Many to Many (the same is for Interpretation and Actor). Here the code:
public partial class Film
{
public Film()
{
Direction = new HashSet<Direction>();
Interpretation = new HashSet<Interpretation>();
}
public int Id { get; set; }
public string Title { get; set; }
public string Genre { get; set; }
public int? Duration { get; set; }
public virtual ICollection<Direction> Directions { get; set; }
public virtual ICollection<Interpretation> Interpretations { get; set; }
}
public partial class Direction
{
public int Id { get; set; }
public int? IdDirector { get; set; }
public int? IdFilm { get; set; }
public virtual Film IdFilmNavigation { get; set; }
public virtual Director IdDirectorNavigation { get; set; }
}
public partial class Director
{
public Director()
{
Directions = new HashSet<Direction>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public virtual ICollection<Direction> Directions { get; set; }
}
In the end: how can I include Directors and Actors in the Film instance and make them appear in that json string? Technically this is a simple JOIN after all, but I don't really know how to do it using these classes.
Thank you for the help!
Here's the repo with all the code if can help:
https://github.com/marco-savino/film_archive_project.git

Deleted parent entity if all children were deleted

I'm using ASP.NET Core 2.2 and EF. And I have these entities:
public class ActionCategory
{
public short Id { get; set; }
[Required]
public string Name { get; set; }
public IEnumerable<Action> Actions { get; set; }
}
public class Action
{
public string Id { get; set; }
[Required]
public string Name { get; set; }
public short CategoryId { get; set; }
[ForeignKey("CategoryId")]
public ActionCategory Category { get; set; }
}
I want to automatically delete ActionCategory if all Actions were deleted. I understand that I can implement this logic myself but I wonder if there is some EF feature allows to do that. And if there is no such feature could I write a trigger for MySQL database? Wouldn't it be a bad way? I mean split my business logic to controllers and database? Or vice versa it will be a good practice to make this logic on database level?

EF Core 2 Stopping Circular Dependency on Many to Many Relationship

I am using the Sakila Sample Database from MySql on a MySql server. The Diagram looks as follows.
The important tables are the store, inventory and film tables. The is a many-to-many relationship between the tables and the linker table is the inventory table.
I scaffolded this Database in a new dotnetcore project using EFCore 2.
I am trying to get a list of stores and their list of films.
The Entities are defined as follows:
Store
public class Store
{
public Store()
{
Customer = new HashSet<Customer>();
Inventory = new HashSet<Inventory>();
Staff = new HashSet<Staff>();
}
public byte StoreId { get; set; }
public byte ManagerStaffId { get; set; }
public short AddressId { get; set; }
public DateTimeOffset LastUpdate { get; set; }
public Address Address { get; set; }
public Staff ManagerStaff { get; set; }
public ICollection<Customer> Customer { get; set; }
public ICollection<Inventory> Inventory { get; set; }
public ICollection<Staff> Staff { get; set; }
}
Inventory
public partial class Inventory
{
public Inventory()
{
Rental = new HashSet<Rental>();
}
public int InventoryId { get; set; }
public short FilmId { get; set; }
public byte StoreId { get; set; }
public DateTimeOffset LastUpdate { get; set; }
public Film Film { get; set; }
public Store Store { get; set; }
public ICollection<Rental> Rental { get; set; }
}
Film
public partial class Film
{
public Film()
{
FilmActor = new HashSet<FilmActor>();
FilmCategory = new HashSet<FilmCategory>();
Inventory = new HashSet<Inventory>();
}
public short FilmId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public short? ReleaseYear { get; set; }
public byte LanguageId { get; set; }
public byte? OriginalLanguageId { get; set; }
public byte RentalDuration { get; set; }
public decimal RentalRate { get; set; }
public short? Length { get; set; }
public decimal ReplacementCost { get; set; }
public string Rating { get; set; }
public string SpecialFeatures { get; set; }
public DateTimeOffset LastUpdate { get; set; }
public Language Language { get; set;
public Language OriginalLanguage { get; set; }
public ICollection<FilmActor> FilmActor { get; set; }
public ICollection<FilmCategory> FilmCategory { get; set; }
public ICollection<Inventory> Inventory { get; set; }
}
My context looks as follows:
modelBuilder.Entity<Inventory>(entity =>
{
entity.ToTable("inventory", "sakila");
entity.HasIndex(e => e.FilmId)
.HasName("idx_fk_film_id");
entity.HasIndex(e => new { e.StoreId, e.FilmId })
.HasName("idx_store_id_film_id");
And lastly the repo looks as follows:
public IEnumerable<Store> GetStores()
{
return _context.Store.
Include(a => a.Inventory).
ToList();
}
Problem:
When I call this method from a Controller to get the list of stores I don´t get any json response on Postman. Yet if I debug into the list that is returned from the Controller I find the list of stores.
The problem is that the list contains:
store->inventory->film->store->inventory->film->store... Etc. Creating a circular dependency that fills up the allowed Process memory of the request.
Possible Solutions:
I think it has to do with the fact that on the Context both the Foreign Keys are defined as HasIndex instead of HasKey
entity.HasIndex(e => new { e.StoreId, e.FilmId })
.HasName("idx_store_id_film_id");
When I define it as HasKey then I get an Error:
'The relationship from 'Rental.Inventory' to 'Inventory.Rental' with
foreign key properties {'InventoryId' : int} cannot target the primary
key {'StoreId' : byte, 'FilmId' : short} because it is not compatible.
Configure a principal key or a set of compatible foreign key
properties for this relationship.'
To answer #hamzas comment, I did find a solution to this problem. I used EFCore to build the entities and the DBContext through scaffolding (DB First). As a best practice you should be using Models (Dtos) to represent the Data for the client. EFCore is very helpful in giving us the flexibility to access this M to N relationship however we want. This gives us the flexibility to represent this Data to the client however we want.
Whatever your use case might be. You have to convert the M to N relationship into an 1 to N model.
Use Case #1: You want to show all the movies for a specific store.
Solution
Step #1: You create a StoreDto (Model)
public class StoreDto
{
int StoreId { get; set; }
ICollection<FilmDto> Films { get; set; }
= new List<FilmDto> ();
}
Step #2: Create a FilmDto
public class FilmDto
{
int FilmId { get; set; }
int StoreId { get; set; }
string FilmName { get; set; }
}
Step #3: You provide a Mapping with auto mapper
public class MappingProfiles : Profile
{
public MappingProfiles()
{
CreateMap<Store, StoreDto>();
CreateMap<Film, FilmDto>();
}
}
Step #4: Query the data correctly, Unfortunately I don´t have this example anymore to test this code, so here is where you´ll have to experiment a bit
public Store GetFilmsForStore(byte StoreId)
{
return _context.Store.
Include(a => a.Inventory).
ThenInclude(i => i.Film)
ToList();
}
On the "Include" part you want to only get the Inventory entries where StoreId == Inverntory.StoreId and then Include the Films Object from the resulting list.
I hope you get the jist of it. You want to break up your m to n relationships and make them seem like 1 to m for your clients.

Relationship Using Code first with Existing database

When defining a relationship between two types is it important to include a navigation property on both types, such as in the following example:
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public Category Category { get; set; }
}
public class Category
{
public int CategoryId { get; set; }
public string Name { get; set; }
public ICollection<Product> Products { get; set; }
}
Can I do without including the navigation property in Category ?
If you just want it infered by code first convention then yes you need both on either side. I'd also make the collection "virtual" to support lazy loading.
You can set it up using the fluent configuration when the model is built. It would be something like this
modelBuilder.Entity<Product>()
.HasMany(x => x.Category)

Configuring EF code first without a foreign key

I have the following model:
public class Product
{
public long Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Catalog> Matches { get; set; }
}
public class Catalog
{
public long Id { get; set; }
public string Title { get; set; }
}
Using Entity Framework code first I configure this using:
public DbSet<Product> Products { get; set; }
public DbSet<Catalog> Catalogs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// not rules setup yet
}
Currently when EF creates my database it creates a nullable foreign key in the Catalogs table called Product_Id. Is there a way to configure EF to not create any foreign key in the Catalog table?
The catalog table contains imported items from a catalog that should have no relation to the Products table. At run time a search query will be fired for each product and the result will be added to the catalog collection of the product object.
For your purpose I would exclude the Matches collection from the model, either by data annotation...
public class Product
{
public long Id { get; set; }
public string Name { get; set; }
[NotMapped]
public virtual ICollection<Catalog> Matches { get; set; }
}
...or in Fluent code:
modelBuilder.Entity<Product>()
.Ignore(p => p.Matches);