Entity Framework many-to-many navigation without junction entity - entity-framework

With the following table structure.
How can I create a navigation property for Sprockets, filtered by CompanyId, in my Widget class without having to add and use a navigation property for Sprockets in the Company entity.
class Widget {
public int CompanyId {get; set;}
public virtual ICollection<Sprocket> Sprockets {get; set;}
}

I beileve the answer is, no, at least not yet. "Skip collections" were added in EF Core 5, but currently they only support Many-to-Many relationships where the linking entity is on the many side of both relationships. See https://github.com/dotnet/efcore/issues/21673
Of course you add a NotMapped property, you just can't use it in LINQ-to-Entities.
public class Widget
{
public int WidgetId { get; set; }
public string Name { get; set; }
public virtual Company Company { get; set; }
[NotMapped]
public ICollection<Sprocket> Sprockets
{
get
{
return this.Company.Sprockets;
}
}
}

Related

.NET Core Entity Framework linking subtable to property

This is an existing .NET Core 3.1 project I inherited.
I have a class referring to a database table
public class SupportContract
{
public int Id { get; set; }
public DateTime StartDate { get; set; }
public int SupportContractStatusId { get; set; }
public virtual SupportContractStatus SupportContractStatus { get; set; }
}
and a sub table with a foreign key
public class SupportContractStatus
{
public int Id { get; set; }
public string SupportContractStatusName { get; set; }
}
This works fine I can get
supportContract.SupportContractStatus.SupportContractStatusName
But if I rename SupportContractStatusId to ContractStatusId in C# and the database, I get an error "SupportContractStatusId missing".
I cannot find any link between the column SupportContractStatusId and table SupportContractStatus anywhere in code nor is there any mention of the foreign key.
There is no link in the DbContext either.
Is this naming convention assumed by Entity Framework? How does the framework know of the foreign key?
Yes, the naming convention that EF expects by default is based on the class name, not the property name. It will look for ClassNameId or ClassName_Id. You can link the FK either through annotation or configuration.
I.e.
public int ContractStatusId { get; set; }
[ForeignKey("ContractStatusId")]
public virtual SupportContractStatus ContractStatus { get; set; }
Configuration is done through IEntityTypeConfiguration implementations or by implementing the OnModelCreating method in the DbContext and configuring the relationship within the modelBuilder. For occasional deviations from convention, the attribute approach can generally cover everything.

Entity Framework Core - retrieve entire DbSet without using include()

Let's say I have the following 2 classes that will represent a relationship between 2 DB Tables (created using EF Core Migrations):
public class Author
{
public int Id { get; set; }
public string Name { get; set; }
public Book Book { get; set; }
}
public class Book
{
public int Id { get; set; }
public String name { get; set; }
public string Genre { get; set; }
}
These 2 classes are added in as a DbSet in my ApplicationDbContext class.
When I retrieve the Author record, I want the Book object to also be populated info. It seems I have to retrieve it in the following way:
return _context.Author.Include(x => x.Book).ToList();
If I had a dozen of different objects in the Author class does that mean I have to chain the .Include() method calls for each object? Is there a catch-all method that will tell me to populate all of the objects inside the Author class? Something like .IncludeAll() perhaps?
You can use lazy loading enabled on your EF core. This way you don't need to include it every time.
Basically you need to add virtual keyword to your book entity:
public class Author{
public int Id {get; set;}
public string Name {get; set; }
public virtual Book Book {get; set; }
}
public class Book {
public int Id {get; set;}
public String name {get; set;}
public string Genre {get; set; }
}
Then you need to enable lazy loading by installing the Microsoft.EntityFrameworkCore.Proxies package and enabling it with a call to UseLazyLoadingProxies. For example:
In you startup.cs class modify the dbcontext as below
services.AddDbContext<BloggingContext>(
b => b.UseLazyLoadingProxies()
.UseSqlServer(myConnectionString));
Please refer the following link to understand how lazy loading works
https://learn.microsoft.com/en-us/ef/core/querying/related-data

How to make working my structure with EF Core using Fluent Api?

I'm trying to represent some kind of tree using ef core and postgre sql. I have two classes:
public class ProtocolNode
{
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<ProtocolCriteria> Criterias { get; set; }
public ProtocolNode()
{
Criterias = new List<ProtocolCriteria>();
}
}
public class ProtocolCriteria
{
public Guid Id { get; set; }
public Guid? ParentId { get; set; }
public ProtocolNode Parent { get; set; }
public Guid? ChildrenId { get; set; }
public ProtocolNode Children { get; set; }
}
After that, I'm trying to run migrations for creating a database, but getting the error:
Unable to determine the relationship represented by navigation property 'ProtocolCriteria.Parent' of type 'ProtocolNode'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
I assume that I need to make some rules using Fluent API, but after some attempts I'm stuck...
I would be thankful for any help.
In general, a top-down tree structure consists of equal nodes which have two major properties, parent & siblings
public class node {
public int Id {get; set;}
public int? ParentId {get; set;} // top node has no parent
public virtual Node Parent {get; set;}
public virtual ICollection<Node> Children {get; set;}
}
builder.Entity<Node>() // ModelBuilder
.HasOptional(c => c.Parent)
.WithMany()
.HasForeignKey(c => c.ParentId);
EF makes parsing the children much easier than maintaining Linked-Lists. This assumes you are NOT talking about Binary trees which are a special case.
Note: EF Core has not implemented the HasOptional() functionality yet.

Cascade delete in one to one relationship

I want to have cascade delete in 1:1 relationship, where i reference multiple entities to one. Problem is throws me an error on database update
Introducing FOREIGN KEY constraint 'FK_dbo.CategoryArticles_dbo.Articles_Article_Id' on table 'CategoryArticles' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
RoutingSeo entity is for storing seo friendly url in database for later usage. My problem is clearly M:N relationship between Article and Category. Is there something how can I deal with this problem?
Here are my entities of my model
public class Article : IEntity<int>
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Category> Categories { get; set; }
[Required]
public virtual RoutingSeo RoutingSeo { get; set; }
public int RoutingSeoId { get; set; }
}
public class Category : IEntity<int>
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Article> Articles { get; set; }
[Required]
public virtual RoutingSeo RoutingSeo { get; set; }
public int RoutingSeoId { get; set; }
}
public class SpecificProduct : IEntity<int>
{
public int Id { get; set; }
public string Name { get; set; }
[Required]
public RoutingSeo RoutingSeo { get; set; }
public int RoutingSeoId { get; set; }
}
public class RoutingSeo : IEntity<int>
{
public int Id { get; set; }
public string SeoRoute { get; set; }
public Article Article { get; set; }
public SpecificProduct SpecificProduct { get; set; }
public Category Category { get; set; }
}
Here is my fluent api code where i specify cascade delete
modelBuilder.Entity<Article>()
.HasRequired(x => x.RoutingSeo)
.WithOptional(x=>x.Article)
.WillCascadeOnDelete();
modelBuilder.Entity<Category>()
.HasRequired(x => x.RoutingSeo)
.WithOptional(x=>x.Category)
.WillCascadeOnDelete();
modelBuilder.Entity<SpecificProduct>()
.HasRequired(x => x.RoutingSeo)
.WithOptional(x=>x.SpecificProduct)
.WillCascadeOnDelete();
You are right, it is your many-to-many relation ship between Article and Category: one Article has zero or more Categories and every Category may be used by zero or more Articles.
If you delete an Article, its Categories can't be deleted automatically, because the Category might be used by other Articles, and even if it isn't used right now, entity framework doesn't know whether you want to use it tomorrow. After all, you specified that every Category might be used by zero or more Articles.
Similarly, if you remove a Category, entity framework can't automatically remove the Articles belonging to this category.
This differs from a one-to-many relationship. For example, if you have a one-to-many relationship of a Book and its Pages, then every Book has zero or more Pages and every Page belongs to exactly one Book.
If you remove the Book, then entity framework knows that it should automatically remove all Pages of the Book, which are all Pages with a foreign key BookId. If Entity Framework would only remove the Book, then we would have a bunch of Pages with foreign key value pointing to a non-existing Book. So in one-to-many relations, entity framework can cascade on delete.
Alas, in many-to-many this is not possible.
On the bright side, you have the advantage that you can delete the last Article of a Category, and keep the Category intact. Tomorrow you can add a new Article that uses this Category.
So if you want to remove an article, you manually have to remove it from the 'Categories` it uses:
many-to-many following the standard naming conventions:
class Article
{
public int Id {get; set;}
// an Article belongs to zero or more Categories:
public virtual ICollection<Category> Categories {get; set;}
...
}
class Category
{
public int Id {get; set;}
// a Category is used by zero or more Articles:
public virtual ICollection<Article> Articles{get; set;}
...
}
Don't forget to declare your ICollections virtual!
class MyDbContext : DbContext
{
public class DbSet<Article> Articles {get; set;}
public class DbSet<Category> Categories {get; set;}
}
You don't have to mention the junction-table, entity framework will make it automatically for you, but you won't have to use it for joins if you want Articles with their Categories, or Categories with their Articles, just use the ICollections
Note: As Categories is not the expected plural of Category, you'll have to tell entity framework the proper table name. Out of scope of this question.
Delete an Article, but keep all Categories it belongs to alive:
using (var dbContext = new MyDbContext(...))
{
Article articleToRemove = ...
dbContext.Articles.Remove(articleToRemove);
dbContext.SaveChanges();
}
Entity framework will automatically perform the proper joins, and remove the articleToRemove from every Category. However, the Categories won't be removed.
In fact, internally the Categories table doesn't change at all. All records with Article.Id will be removed from the junction table.

Entity Framework code first FK field

I have two classes:
public class Fighter
{
public int FighterID { get; set; }
public int DivsionID { get; set; }
public string Name { get; set; }
//...
public virtual Division Division { get; set; }
}
public class Division
{
public int DivisionID { get; set; }
public string Name { get; set; }
public int? FromWeight { get; set; }
public int? ToWeight { get; set; }
public ICollection<Fighter> Fighters { get; set; }
}
Why do I have Division_DivisionID on my Fighters table ? I thought the DevisionID should be the FK.
I wrote an article on how this works, take a look at http://blog.staticvoid.co.nz/2012/07/entity-framework-navigation-property.html - See How does Entity Framework detect Navigation Properties
In short this is due to a convention which says FKs are named
<LocalPropertyName>_<ForeignIdPropertyName>
Also see Entity Framework Navigation Property generation rules
to make EF name the FK DivisionID, add the following to your modelbuilder
modelBuilder.Entity<Fighter>()
.HasRequired(f => f.Division)
.WithMany(d => d.Fighters)
.HasForeignKey(f => f.DivisionID);
You are mixing the EF FK Association concept with your database FK concept, they are not the same. The FK Association concept on EF was introduced so you could do things like lookups and data binding more easily (like DropDownList data binding for example).
The ER FK concept that is created in your table is a mapping for the composition you have on the Fighter class, in this case the Division property. The naming of that table column follows EF's rules.
For more on the EF FK Association read this article.