Many-to-Many with same entity Entity Framework - c#

I have a strange relation in my database.
Basically it's an entity connected to entities of the same type.
What I have is this:
Model:
public class Article
{
-- Properties --
ICollection<Article> ConnectedArticles { get; set; }
}
Fluent API:
modelBuilder.Entity<Article>()
.HasMany(ca => ca.ConnectedArticles)
.WithMany(ca => ca.ConnectedArticles)
.Map(m =>
{
m.MapLeftKey("ArtNo");
m.MapRightKey("ArtNo");
m.ToTable("ConnectedArticles");
});
This does not play well with Entity Framework and results in the error: The navigation property 'ConnectedArticles' declared on type 'ServiceSystem.Models.CommonArticle' cannot be the inverse of itself.
I could solve this issue by creating a new model which contains the related articles like so:
public class ConnectedArticle
{
public Article article1 { get; set; }
public Article article2 { get; set; }
}
But I'm hoping Entity Framework can do this on it's own, just like it does with many-to-many for separate entities.
Is it possible to solve this nicely with Entity Framework or am I missing something critical?
Thanks,
Robin Dorbell

Related

How to migrate an already set up one-to-many relationship in Entity Framework?

I have a C# WPF application using an SQLite database with Entity Framework. I have a Contact class which can have multiple messages, so there is a one-to-many relationship, set up the following way (simplified version):
public class Message {
public int PK { get; set; }
public int SenderKey { get; set; }
public Contact Sender { get; set; }
}
public class Contact {
public int PK { get; set; }
public ICollection<Message> Messages { get; set; }
}
Then I set the relationship using the Fluent API, the following way:
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
...
modelBuilder.Entity<Message>()
.HasOptional(e => e.Sender)
.WithMany(e => e.Messages)
.HasForeignKey(e => e.SenderKey);
}
I'm going to have lots of data (and also have lots of troubles with this circular dependency, especially on editing the objects in detached mode) so it's not a good idea to have that Messages collection in the memory for every contact. To avoid this, I'd like to get rid of that list, so it would be great to implement the 'Convention 1' from the docs (to just have the Contact object in the Message class and that's all).
The problem with this solution is that my app is already published, so I can't just simply change the structure, I need a migration. My question is that how can I migrate this kind of relation set up by the Fluent API?
I tried to remove the relationship from the OnModelCreating, but I got the following exception when I started the app:
System.Data.SQLite.SQLiteException: SQL logic error no such column: Extent1.Sender_PK (what is that Extent1 table?)
Finally I found the solution. I didn't have to implement a specific migration, just modify the following things:
Remove the relationship setup from the OnModelCreating
The previous step caused that "sql logic error" posted in the question. This was because the foreign key column name wasn't specified and the Entity Framework searched for a default column, which is in fact Sender_PK. So, in order to solve this, I added an annotation in the Message class, which tells the Entity Framework what is the foreign key column name for that Contact object:
public class Message {
public int PK { get; set; }
public int SenderKey { get; set; }
[ForeignKey("SenderKey")]
public Contact Sender { get; set; }
}
Remove the message list reference (public ICollection<Message> Messages { get; set; }) from the Contact class.
So, after this three step modification I had the one-to-many relationship between the two tables and I could get rid of that list. Nothing else needed, it works perfectly with the old databases.

How to map new class to an existing table using c# Fluent API?

In my application, there is a many-to-many relationship between two entities (ChartOfAccount & GL). Once I create the relationship a new table got created (ChartOfAccountGLs). Now I tried to create a new class to map those two entities with one-to-many relationship (ChartOfAccountGL-->ChartOfAccount & ChartOfAccounGL-->GL). But when I tried to update, database package manager console gave this error
"There is already an object named 'FK_dbo.ChartOfAccountGLs_dbo.ChartOfAccounts_ChartOfAccountId' in the
database. Could not create constraint or index. See previous errors."
Below shows my ChartOfAccountGL class:
[Table("ChartOfAccountGLs")]
public class ChartOfAccountGL
{
public int ChartOfAccountId { get; set; }
public int GLId { get; set; }
public ChartOfAccount ChartOfAccount { get; set; }
public GL GL { get; set; }
}
ChartOfAccountGLConfiguration :
public class ChartOfAccountGLConfiguration : EntityTypeConfiguration<ChartOfAccountGL>
{
public ChartOfAccountGLConfiguration()
{
//Define two composite keys
HasKey(k => new { k.ChartOfAccountId, k.GLId });
Property(a => a.ChartOfAccountId)
.HasColumnName("ChartOfAccountId");
Property(b => b.GLId)
.HasColumnName("GLId");
//Mapping classes
HasRequired(a => a.ChartOfAccount)
.WithMany(b => b.ChartOfAccountGL);
HasRequired(f => f.GL)
.WithMany(b => b.ChartOfAccountGL);
}
}
Does anyone has an idea where have I done the mistake??
It's a known problem for when you upgrade the model. In EF6 you can use ignore changes but in ef core, check this:
https://github.com/dotnet/efcore/issues/4237
There is no equivalent for IgnoreChanges when generating migrations right now. Not on powershell, or using ef migrations add. This needs to come to EF7 and is a fundamental part of the migration work, specially for apps that are working with existing tables.
If you are using ef core, try this (from a comment in the issue):
This is probably just stating the obvious... but as a workaround for the moment you can generate a migration and then delete the code from the Up/Down methods.

Entity Framework Core Generating SQL With Ambiguous Column Names

I am using Entity Framework Core 2.2.6. I'm going to try and make this question concise and apologies in advance if it ends up being a wall of text.
The error I am seeing is an ambiguous column name in the SQL Entity Framework Core generates.
So my situation is this: I have two entities with a many-to-one relationship. The "parent" entity implements
an interface that has a property that is of type IChildEntity. Here are the interfaces:
public interface IParentEntity
{
IChildEntity Child { get; set; }
string Prop1 { get; set; }
string Prop2 { get; set; }
}
public interface IChildEntity
{
string ChildProp1 { get; set; }
string ChildProp2 { get; set; }
}
I am using ef core's fluent api and in order to set up the relationship between parent and child
I am using a concrete type of ChildEntity and defining a IChildEntity property to conform to the
interface and just passing things through to the concrete type:
public class ChildEntity : IChildEntity
{
public long ID {get; set;}
public string ChildProp1 { get; set; }
public string ChildProp2 { get; set; }
}
public class ParentEntity : IParentEntity
{
public long ID { get; set; }
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public long ChildID { get; set; }
// Navigation property so EF Core can create the relationship
public ChildEntity MappedChild { get; private set; }
// this is to adhere to the interface
// just pass this through to the backing concrete instance
[NotMapped]
public IChildEntity Child
{
get => MappedChild;
set => MappedChild = (ChildEntity)value;
}
}
Then in OnModelCreating I set up the relationship like so:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ParentEntity>()
.HasOne(e => e.MappedChild)
.WithMany()
.HasForeignKey(e => e.ChildID);
}
This works and the relationship gets set up as expected, however I am finding when I do a query it can generate
some SQL that can result in an ambigous column error in some database engines. Here is the example query:
MyContext.ParentEntity
.Include(p => p.MappedChild)
.Where(p => p.Prop1.Equals("somestring")
.FirstOrDefault()
The SQL that gets generated is similar to:
SELECT p."ID", p."ChildID", p."Prop1", p."Prop1", "p.MappedChild"."ID", "p.MappedChild"."ChildProp1", "p.MappedChild"."ChildProp2"
FROM "ParentEntity" AS p
INNER JOIN "ChildEntity" AS "p.MappedChild" ON p."ChildID" = "p.MappedChild"."ID"
WHERE p."Prop1" = 'somestring'
ORDER BY "p.MappedChild"."ID"
LIMIT 1
The problem here is we are selecting two columns with the name ID and not aliasing. Some databases will be ok with this
but some will not. A work around I can do for this is to do two separate queries to get the entity and the child entity:
var parent = MyContext.ParentEntity
.Where(p => p.Prop1.Equals("somestring")
.FirstOrDefault()
MyContext.Entry(parent).Reference(p => s.MappedChild).Load();
But this is less than ideal since it does multiple queries and is a bit less elegant than just using Include()
Because this seems like such a common use case and I couldn't find any bug reports against EF Core for this type of
behavior it is my suspicion that I am doing something wrong here that is resulting in EFCore not aliasing column names
for this type of query. I was thinking it could be the bit of trickery I have to do to ensure my entity implements it's interface
(this is something I can't due to constraints in the codebase and other integrations) but the more I look at it the less likely that
seems to me since we are directly dealing with the "mapped" property in EF related code and it's completely unaware of the interface.
My questions are - can anyone see something in my implementation that would cause this? Could anyone
suggest a better workaround than what I have here? Any advice here would be appreciated. Thanks much.
This is an old Entity framework bug with the Oracle company products bug including the MySQL database and Oracle database (12.1 and older).
I see the
ORA-00918: column ambiguously defined
error mostly when:
Selecting one entity with including parent entity.
Selecting one entity with value object own one command
This error appears when using Find, First, FirstOrDefault, Last, Single and all single entity selector commands.
I tested many solutions and check generated sql statement to find out a very unique way without any performance overhead:
// This the way of getting one entity from oracle 12.1 without throwing Oracle exception => ORA-00918: column ambiguously defined without any extra overhead
var entities = await dbSet.Where(x => x.Id == id).Take(1).ToListAsync();
var entity = entities.FirstOrDefault();
Another Sample:
var entities = await dbSet.OrderByDescending(x => x.Id).Take(1).ToListAsync();
var entity = entities.FirstOrDefault();
At the end of your IQueryable Linq add Take(1) and get all with .ToList() or .ToListAsync() to execute the statement and fetch a list with one record. Then use Enumerable Single Entity Selector to change the list to an entity.
That’s all.

Entity Framework 6 Fluent Mapping - One to Many Code First

Given the following SQL tables:
EntityGroup:
Id int, (PK)
GroupName nvarchar(100)
Entity:
Id int, (PK)
EntityGroupId int, (FK Non-nullable)
Description nvarchar(100)
And the following POCOs
public class Entity
{
public int Id { get; set; }
public int EntityGroupId { get; set; }
public int RefNumber { get; set; }
}
public class EntityGroup
{
public int Id { get; set; }
public virtual IList<Entity> Entities { get; set; }
}
How do I configure the fluent mapping correctly? I want Entity.EntityGroupId to remain as an int rather than an EntityGroup object.
I want to be able to .Include() optionally Include("Entities"). The closest I got is this, but that seems to eager-load all entities even if I dont use .Include("Entities"), which is not the behaviour I want:
modelBuilder.Entity<EntityGroup>()
.HasMany(x => x.Entities);
You must set off the lazy loading,
you can do this for just a specific unit of work or for all by setting your dbContext Like
dbContext.Configuration.LazyLoadingEnabled = false;
dbContext.Configuration.ProxyCreationEnabled = false;
or set it in Ctor of your DbContext.
The way I understand it, you want to configure one-to-many relationship between EntityGroup and Entity without navigation property in Entity class and using Entity.EntityGroupId as a FK. All that with Fluent API.
It's possible, but you have to start the configuration from the class having a navigation property (EntityGroup in your case) because Has methods require property accessor while With methods have parameterless overloads. As usual, for the last part you will use the HasForeignKey method:
modelBuilder.Entity<EntityGroup>()
.HasMany(e => e.Entities)
.WithRequired()
.HasForeignKey(e => e.EntityGroupId);
But note that EF recognizes the naming convention used in your sample classes (in particular the EntityGroupId), so you'll get the same mapping w/o any fluent configuration or data annotations.
The problem is lazy loading is enabled by default, so it will load the related entities every time you try to get access to them.Two options to solve your issue could be:
Disabling lazy loading in your context:
public YourContext()
{
this.Configuration.LazyLoadingEnabled = false;
}
Removing virtual from your navigation property, which is one of
the requirements to work lazy loading and the tracking change:
public ICollection<Entity> Entities { get; set; }
If you want to learn more about the supported ways you can load related entities in EF I suggest you to read this article

Entity Framework Code First Relationships; what am I missing?

I'm experimenting with EF5 Code First and I am using the models (show below).
When I look at the database that is created, I am confused because I do not see anything in the Track table that points to the Category table. Category has a FK pointing back to Track but that means that there are going to be duplicates of the categories?
A little background: I am trying to build a model that has tracks and every track can have 1 to N Categories. All of the categories are already defined, that is they are basically a lookup and I plan to create them in the seed method when database is created.
I think I am not understanding something obvious... When I query a track, how will I know what category it contains?
Thx
public class Track : IAuditInfo
{
public Int32 Id { get; set; }
public String Name { get; set; }
public String Description { get; set; }
public String Data { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime ModifiedOn { get; set; }
public ICollection<Category> Categories { get; set; }
public Track()
{
Categories = new List<Category>();
}
}
public class Category
{
public Int32 Id { get; set; }
public Boolean IsVisible { get; set; }
public String DisplayName { get; set; }
}
Your current model is a one-to-many relationship between tracks and categories.
This usually implemented, as you have noted that entity framework does, using a foreign key on the many side (category) to the one side (track).
If I understand you correctly, what you want is a many-to-many relationship. Many tracks can be related to the same category, and a single track can belong to many categories.
To let entity framework understand that you want a many-to-many relationship you can simply add a ICollection property to your category class.
So both your classes should have a collection of the other class.
I.e. tracks have many categories and categories have many tracks.
For more information you can also see: http://msdn.microsoft.com/en-us/data/hh134698.a.nospx
Olav is right, your data model at the moment is not telling Entity Framework that there is a many-to-many relationship in there.
The simplest way to resolve this is to add
public virtual ICollection<Track> Tracks { get; set; }
to your Category class.
However... You may not want to pollute your domain model with artefacts that are not relevant to your domain. More importantly, when you do it this way, it is up to Entity Framework to figure out what to call the binding table. Prior to EF6 this naming is non deterministic (see http://entityframework.codeplex.com/workitem/1677), which may mean that two different machines compiling the same code will decide on different names for that table and cause some interesting migration problems in your production system.
The answer to both problems is to always explicitly manage many-to-many relationships with Fluent Configuration.
In your Data Context class, override the OnModelCreating, something like this:
public class MyDb : DbContext
{
public IDbSet<Track> Tracks { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Track>()
.HasMany(t => t.Categories)
.WithMany()
.Map(c => c.ToTable("CategoriesForTracks"));
}
}
If you do this, you don't need to add a navigation property to your Category class, though you still can (if you do, you should use the overload for WithMany that allows you to specify a property).
Relationships between entities and how to map that to a relational database is inherently hard. For anything other than the simplest parent-child relationships you will want to use the fluent API to make sure you actually get what you want.
Morteza Manavi has a really good blog series describing relationships in EF Code First in exhaustive detail.
NOTE
You should usually make navigation properties virtual. So, you should change your Category class like this:
public virtual ICollection<Category> Categories { get; set; }
In theory, not making it virtual should just cause eager loading rather than lazy loading to happen. In practice I have always found lots of subtle bugs appearing when my navigation properties are not virtual.

Categories

Resources