EF Code First Navigation Property to same table - c#

I'm new to EF and struggling to implement the following scenario. I have an entity I'd like to have a navigation property to another of the same entity. E.g.
public class Stage {
public int ID { get; set; }
public int? NextStageID { get; set; }
public string Name { get; set; }
public virtual Stage NextStage { get; set;}
}
The only example I've found so far was where the entity had a parent / child relationship, i.e. the navigation property was an ICollection of the same entity. I tried adapting this but couldn't get it to work in my instance. Also, I only need it to be one way, i.e. the entity doesn't have a 'PreviousStage' property, just a 'NextStage' one. I'm configuring using Fluent API. Could someone advise if / how this can be achieved?
I am getting this error:
Unable to determine the principal end of an association between the types 'namespace.Stage' and 'namespace.Stage'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations
Edit
Just realised in my slightly simplified example, I didn't show that NextStageID is optional (int?).

You can explicitly define the relation as follows:
public class Stage {
public int ID { get; set; }
public int NextStageID { get; set; }
public string Name { get; set; }
[ForeignKey("NextStageID ")]
public virtual Stage NextStage { get; set;}
}

you need to add a parentId and Parent navigation property
and Children navigation property so entity framework understands that is a recursive relation
check the answer in this stack Overflow link

Related

Handling relations when mapping DTOs to EF entities

In my DTOs, I send Ids instead of entire objects to assign/relate one object to another. The problem is my mapping code will need access to the database to handle this because my entity classes don't have BarId property.
public class FooDTO
{
public int FooId { get; set; }
public int BarId { get; set; }
}
public class Foo
{
public int FooId { get; set; }
public Bar Bar { get; set; }
}
This can probably be solved by adding additional BarId property to my entity class, that way I don't couple data access with my mapper.
But the question arises: if bar with specified id doesn't exist can this be handled in some reasonable way to return the custom error message?
public class Foo
{
public int FooId { get; set; }
public int? BarId { get; set; }
public Bar Bar { get; set; }
}
Is it fine to access database in my mapping code and handle these assignments manually or is it better to leave it to the ORM by explicitly adding a foreign key property (BarId) in my entity class?
Also see: https://learn.microsoft.com/en-us/ef/core/modeling/relationships#no-foreign-key-property
While it is recommended to have a foreign key property defined in the dependent entity class, it is not required.
It seems like adding foreign key property is recommended so I guess I will choose this route.
no, not fine. if you are accessing the db this is most definitely not mapping code.

Dynamic Entity Navigation Property

I have an entity called Asset, similar to below:
public class Asset
{
public int Id { get; set; }
public int TypeId { get; set; }
public int AddedById { get; set; }
public DateTime DateTimeAdded { get; set; }
public virtual AssetType Type { get; set; }
public virtual ITUser AddedBy { get; set; }
}
I want to be able to have a navigation property that is linked to a single table, but that table is dependent on what type of Asset it is. For instance, if the Asset is of the type "Printer" then I want the navigation property to link to the PrinterDetail entity. My initial way of going about this was to have unused columns in the Asset entity, but I figured that was wasteful or bad practice. Is there something that I am overlooking or is this just something that cannot be done?
Thanks for any advice given.
if you want navigate printerDetail by type you can use entityfraemwork inheritance strategy:
Table per Hierarchy (TPH)
Table per Type (TPT)
Table per Concrete class (TPC)
you have to create Model per each type and use TPT strategy for that.
and then you can use fluent api for config mapping for that.
parent Model (Asset) must define as abstract class and AssesTypes Must be Drive from the Parent.
more information

Entity Framework one-to-one with a class that is sometimes independent

I have a parent class ComponentDesign:
public class ComponentDesign
{
public string Name { get; set; }
public virtual JobFile DesignFile { get; set; }
public int? DesignFileId { get; set; }
public Pdf PdfFile { get; set; }
public int? PdfFileId { get; set; }
public JobFile SealedPdfFile { get; set; }
public int? SealedPdfFileId { get; set; }
public int Id { get; set; }
public int JobId { get; set; }
}
And a child class JobFile (of which Pdf is a subclass):
public class JobFile
{
public int ID { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public ComponentDesign ComponentDesign { get; set; }
public int? ComponentDesignId { get; set; }
public int? JobId { get; set; }
}
These classes are stored in a relational database using Entity Framework. I would like to have navigation properties on both sides of the relationship, so that I can say componentDesign.PdfFile or jobFile.ComponentDesign at will.
Each property of type JobFile in ComponentDesign is optional, and a JobFile will only ever belong to oneComponentDesign. However, aJobFilemay be free-standing, not belonging to anyComponentDesign(sojobFile.ComponentDesign` would be null).
I'm struggling with how to use the Fluent API to configure this relationship. It is one-to-one. Is it possible to have the navigation properties on both sides of the relationship? Remember that a JobFile can be free-standing (so jobFile.ComponentDesign will not always be relevant), so there are JobFiles that belong to ComponentDesigns and ones that don't. The closest I feel I've come is this:
modelBuilder.Entity<ComponentDesign>()
.HasOptional(componentDesign => componentDesign.DesignFile)
.WithRequired(jobFile => jobFile.ComponentDesign);
But this seems to me to indicate that jobFile.ComponentDesign is always required, which isn't the case. I'm hesitant to just try it because it will generate some substantial migrations, so I wanted to get input first. What should my Fluent API configuration look like for this situation?
Just so that I understand the relationships:
ComponentDesign -> JobFile = Optional.
JobFile -> ComponentDesign = Optional.
This is a Zero-or-one to zero-or-one ([0/1]-[0/1]) relationship.
This can be implemented in the following way, using the the .WithOptionalPrincipal method in fluent API. With a 1-0 relationship, it's obvious which end is the principal end; same with a 1-∞ relationship. ∞-∞ relationships don't have a principal end, due to the hidden tables created that control the relationships between each. With a 0/1-0/1, or a 1-1 relationship, it's not obvious, and so you must tell the database which end to use as the principal end of the relationship. Among many other things, the pricipal is responsible for initiating, and maintaining the relationship between the tables.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Configure ComponentDesign & JobFile entity
modelBuilder.Entity<ComponentDesign>()
// Mark JobFile property optional in ComponentDesign entity.
.HasOptional(cd => cd.DesignFile)
// Mark ComponentDesign property optional in JobFile entity.
.WithOptionalPrincipal(jf => jf.ComponentDesign);
}
Or, you can create an EntityTypeConfiguration<> class for each entity to separate out the relationships, if there are a lot to sort through. While this does decentralise the relationships, it is more scalable, as these configuration classes can be injected via MEF once the context is configured to do so. Just an idea for future development.
If you were configuring the relationship from the JobFile end, you would use .WithOptionalDependent, in order to set the navigation correctly, instead of .WithOptionalPrincipal. It all depends on which side you configure the relationship from. The ComponentDesign entity relies on the JobFile entity a lot more than the JobFile entity relies on the ComponentDesignentity; thus it should be configured as the principal.
As stated in one of the comments above, complex properties should be adorned with the virtual keyword. This lazy loads the property, and also, when the class is intansiated, it will set the initial value to be null.
Clone the database to a test server, to work on this one problem, then implement the changes on the production model; that way there is no chance of data loss when migrating the database.

Navigation property is empty

Very strange issue, I've been using EF + Code First on a lot of projects but I can't figure out what's going on here.
I have the following entities :
public class Article
{
public int ID { get; set; }
public string Title { get; set; }
public virtual Media Image{ get; set; }
public virtual Employee Author {get; set;}
public MyEnum EnumValue {get; set;}
public enum MyEnum {Value1, Value2}
}
public class Media
{
public int ID {get; set;}
public double Length { get; set; }
public string ContentType { get; set; }
public byte[] Content { get; set; }
}
public class Employee
{
public int ID {get; set;}
public string Name{get; set;}
public string Email{get; set;}
}
With the following DbContext :
public class MyContext : DbContext
{
public MyContext() : base("ConnectionString")
{
this.Configuration.LazyLoadingEnabled = true;
this.Configuration.ProxyCreationEnabled = true;
}
public DbSet<Article> Articles { get; set; }
public DbSet<Employee> Employees { get; set; }
public DbSet<Media> Medias { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Article>().HasRequired(x => x.Image);
modelBuilder.Entity<Article>().HasOptional(x => x.Author);
}
}
Nothing special here, however whenever I retrieve an Article for example like that :
var article = _db.Articles.FirstOrDefault(x => x.ID == id);
I have two problems Here is my problem :
The Image navigation property is empty. It is not null but all its properties have default values (0, null, etc...)
The Article object has a dynamic proxy but the navigation properties Image and Employees do not. If I remember correctly, navigations properties should be wrapped by dynamic proxies by default.
One important thing to note is that, the Employee navigation property has all its properties correctly loaded. This is good, however there is no dynamic proxy on it.
I've spent the last 2 hours trying to troubleshoot this and I'm running out of idea.
I would appreciate any hint / help,
Thanks !
Update : I've checked in the database and the foreign key is OK and the record in the Images table does exist.
Not sure I completely understand your question as you're playing a little fast and loose with terms here. Dynamic proxies, in the sense of Entity Framework, are classes that EF creates on the fly that inherit from actual entity classes and override virtual reference and navigation properties to enable lazy-loading. It's not something you really need to pay attention to, for the most part.
Navigation properties are always explicitly added. Entity Framework doesn't not add these for you under any circumstances. Technically what you have is two reference properties on your Article class, and that's it. As a result, Entity Framework creates a proxy of the Article class and returns an instance of that proxy from the results of your query. By attempting to access one of your reference properties, like Image, you activate the lazy-loading logic the proxy class added, causing a new query to be issued to the database to fetch that Media instance. The result will either be the object instantiated with the result of the database query or null. I can't say why you're getting an instantiated Media instance with "default" values. There's nothing regarding Entity Framework that would cause that. You must have some other code interfering with the instance.
As far as Media and Employee go, there's no navigation properties on these classes. If you would like to be able to access the set of related Articles, then you need to add something like:
public virtual ICollection<Article> Articles { get; set; }

EF Code First foreign key definition / left join / navigation properties?

Let's say I have 3 tables:
[Table("Comments")]
public class Comment {
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ForeignKey("Users")]
public int UserId { get; set; }
public virtual Users Users { get; set; }
public string Text { get; set; }
}
[Table("Users")]
public class Users {
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string UserName { get; set; }
}
[Table("CommentAgree")]
public class CommentAgree {
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int CommentId { get; set; }
public int UserId { get; set; }
}
Users post comments and other users can 'agree' with that comment, a bit like Facebook's 'like' system. I'm using lambda for my queries and I can do:
var query = db.Comments.Select(c => new {
c.Id,
c.Users.UserName,
c.Text
});
How can I create a join to CommentAgree on Comment.Id = CommentAgree.CommentId? I could write the join in Lambda but I need it to be a left join as nobody may agree with the comment but I still want it to display.
I want to do it the right way, so I'm open to suggestions whether to do it by foreign keys, lambda joins, navigation properties... or something else?
Is this possible?
Thanks
The best approach is probably to use the features of Entity Framework and create navigation properties rather than explicitly using LINQ to perform the joins just for related data.
If your types are shaped just for the purposes of data access, then adding navigation properties to both ends of the relationship is probably a good idea, along with the foreign key properties that you already have.
The collection navigation property on Comment should implement ICollection (for example List<CommentAgree>), and you would have a reference navigation property of type Comment on the CommentAgree type.
You would then have to define the relationships in your mappings, either using data annotations or (preferably) the fluent API.
To load the related data, you could either use lazy loading, or eager loading (using the Include extension method), or use explicit loading from the entry information for the entity.

Categories