EF4 CTP5 self-referencing hierarchical entity mapping - hierarchical-data

Okay, this should be really easy, but I've been tearing my hair out. Here's my POCO (which has to do with machine parts, so a part can be contained within a parent part):
public class Part
{
public int ID { get; set; }
public string Name { get; set; }
public Part ParentPart { get; set; }
}
When the database table is created, the column names are "ID", "Name", and "PartID". How do I change the name of that last column to "ParentPartID"?

Basically, you want to rename the foreign key in an Independent Association and this is the fluent API code that will do it:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Part>()
.HasOptional(p => p.ParentPart)
.WithMany()
.IsIndependent()
.Map(m => m.MapKey(p => p.ID, "ParentPartID"));
}
However, due to a bug in CTP5, this code throw as exception in self referencing associations (which is your association type). The workaround would be to change your association to a Foreign Key Association as follows:
public class Part
{
public int ID { get; set; }
public string Name { get; set; }
public int ParentPartID { get; set; }
[ForeignKey("ParentPartID")]
public Part ParentPart { get; set; }
}

Related

Entity Framework Core DbSet is not returning any data from database

Entity Framework Core DbSet is not returning any data from database, but the database has many register.
This is the entity
public class Entity : BaseEntity
{
public int EntityStatusId { get; set; }
public int AddressId { get; set; }
public string Name { get; set; }
public string SocialReason { get; set; }
public string CNPJ { get; set; }
public EntityType Type { get; set; }
public DateTime? CreationDate { get; set; }
public bool? ReceiptDisabled { get; set; }
public EntityStatus EntityStatus { get; set; }
public Address Address { get; set; }
public List<Company> Companies { get; set; }
public List<Role> RoleList { get; set; }
}
public abstract class BaseEntity
{
public int Id { get; set; }
}
Now this is the configuration class.
public class EntityMap : IEntityTypeConfiguration<Entity>
{
public void Configure(EntityTypeBuilder<Entity> builder)
{
builder.ToTable("Entity");
builder.HasKey(entity => entity.Id);
builder
.Property(entity => entity.EntityStatusId);
builder
.Property(entity => entity.AddressId);
builder
.Property(entity => entity.Name);
builder
.Property(entity => entity.SocialReason);
builder
.Property(entity => entity.CNPJ);
builder
.Property(entity => entity.Type)
.HasConversion(x => (int)x, x => (EntityType)x);
builder
.Property(entity => entity.CreationDate);
builder
.Property(entity => entity.ReceiptDisabled);
builder
.HasOne(entity => entity.EntityStatus);
builder
.HasOne(entity => entity.Address);
builder
.HasMany(entity => entity.RoleList)
.WithOne(x => x.Entity);
builder
.HasMany(entity => entity.Companies)
.WithOne(x => x.Entity);
}
}
And the context class.
public class AucContext : DbContext
{
public AucContext(string databaseConfiguration)
{
_databaseConfiguration = databaseConfiguration;
}
private readonly string _databaseConfiguration;
public DbSet<Campaign> Campaigns { get; set; }
public DbSet<CampaignProject> CampaignProjects { get; set; }
public DbSet<Company> Companies { get; set; }
public DbSet<Cart> Carts { get; set; }
public DbSet<CartItem> CartItems { get; set; }
public DbSet<Donation> Donations { get; set; }
public DbSet<DonationRecurrencePeriod> DonationRecurrencePeriods { get; set; }
public DbSet<Entity> Entities { get; set; }
public DbSet<Institution> Institutions { get; set; }
public DbSet<PaymentMethod> PaymentMethods { get; set; }
public DbSet<Person> People { get; set; }
public DbSet<Project> Projects { get; set; }
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new CampaignMap());
modelBuilder.ApplyConfiguration(new CampaignProjectMap());
modelBuilder.ApplyConfiguration(new CompanyMap());
modelBuilder.ApplyConfiguration(new CartMap());
modelBuilder.ApplyConfiguration(new CartItemMap());
modelBuilder.ApplyConfiguration(new DonationMap());
modelBuilder.ApplyConfiguration(new DonationRecurrencePeriodMap());
modelBuilder.ApplyConfiguration(new EntityMap());
modelBuilder.ApplyConfiguration(new InstitutionMap());
modelBuilder.ApplyConfiguration(new PaymentMethodMap());
modelBuilder.ApplyConfiguration(new PersonMap());
modelBuilder.ApplyConfiguration(new ProjectMap());
modelBuilder.ApplyConfiguration(new UserMap());
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(_databaseConfiguration);
}
}
And the query was simple
var entity = context.Entities.Find(3)
this simple query is returning nothing, any ideas for what is happening?
Update
I have updated somethings since yesterday, and now i have updated the question unfortunately still don't work
OBS:. The ConnectionString it's ok, other objects just work fine.
First, add Id to your Entity:
public int Id { get; set; }
Then in your DbContext:
1:In your OnModelCreating,add
modelBuilder.ApplyConfiguration(new EntityMap());
2:Add DbSet:
public DbSet<Entity> Entity { get; set; }
Re-migrate and update the database.Your code will work fine.
An interesting problem if some entities work but this one doesn't. There are a couple additional things to check/try:
Ensure you have no duplicate mappings. For example, if your Entity has a HasMany.WithOne relationship with another entity, ensure that the mapping for that other entity does not declare a HasOne.WithMany or other relationship back to Entity. This can cause weird behaviour.
Your HasOne relationships are missing WithMany and FK declarations. Given you are using "Id" as a base inherited PK on your entities you should consider explicitly declaring your FK relationships. The WithMany declaration is optional in EFCore, however it is needed to declare the FK if it doesn't follow convention. (and I'm no fan of convention for just deciding not to work)
builder
.HasOne(entity => entity.EntityStatus)
.WIthMany()
.HasForeignKey(entity => entity.EntityStatusId);
builder
.HasOne(entity => entity.Address);
.WIthMany()
.HasForeignKey(entity => entity.AddressId);
EF should be working out the FK names by convention though. Just keep in mind that EF conventions follow the type name, not property name so for instance something like this:
public User CreatedBy { get; set; }
by convention would be looking for a FK property of UserId rather than CreatedById which can lead to weird behaviour or errors.
On a side note you do not need to declare .Property() for each property in an entity, only for properties that require some special configuration like IdentityColumn, NotMapped (ignore) or specifying a data constraint / length etc. I would also recommend removing the .Property() statement for any FK columns in your entity
This all said, I've tinkered with a test EF Core project setting up duplicate mapping between objects and leaving off WithMany() and FK declarations and I was not able to reproduce your issue. I think there is something very specific to your schema or mapping that is tripping up EF to resolve this "Entity" object. If these changes do not work, take it down to the minimum viable object and remove all related entity mappings, setting them all to NotMapped so-as not to break your code and then try loading your Entity objects. From there re-introduce the relationships one by one until it stops loading them and narrow it down. If you do identify a rogue mapping responsible, do be sure to post an update with details about the culprit because it would probably be useful in case someone else gets tripped up by it.

fluent API on table that has relation with itself

I've created a table that has a relation with itself the table has a one-to-many relationship here is my Entity:
public class Permission
{
[Key]
public int PermissionId { get; set; }
public string PermissionTitle { get; set; }
public int? ParentId { get; set; }
#region Relations
[ForeignKey("ParentId")]
public virtual ICollection<Permission> Permissions { get; set; }
#endregion
}
but when I used migration to create the table in SQL, update-database failed for this error:
Introducing FOREIGN KEY constraint 'FK_Permission_Permission_ParentId' on table 'Permission' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
So I decided to use fluent API to solve this issue but I don't know how to Specify ON DELETE NO ACTION by Fluent API on a table that has a relation with itself. any help?
is there any solution to solve my problem?
For EF Core the Entity should normally have two Navigation Properties, like this:
public class Permission
{
[Key]
public int PermissionId { get; set; }
public string PermissionTitle { get; set; }
public int? ParentPermissionId { get; set; }
#region Relationships
public virtual Permission ParentPermission { get; set; }
public virtual ICollection<Permission> ChildPermissions { get; } = new HashSet<Permission>();
#endregion
}
And configured like this:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Permission>()
.HasMany(p => p.ChildPermissions)
.WithOne(p => p.ParentPermission)
.HasForeignKey(p => p.ParentPermissionId)
.OnDelete(DeleteBehavior.Restrict);
base.OnModelCreating(modelBuilder);
}

The INSERT statement conflicted with the FOREIGN KEY constraint

I'm rather new to Entity Framework (code-first). Here are my two entities-
public class Employee
{
public Employee() { }
public long Id {get; set;}
public string Fullname {get; set;}
public virtual ICollection<Attendance> Attendances { get; set; }
}
public class Attendance
{
public Attendance() { }
public DateTime CheckinDateTime { get; set; }
public DateTime? CheckoutDateTime { get; set; }
public long EmployeeId { get; set; }
[ForeignKey("Id")]
public virtual Employee Employee{ get; set; }
}
Employee has one-to-many relation with Attendance.
I've tried to create a new Attendance data-
var attendance = new Attendance()
{ EmployeeId = 1,
CheckinDateTime = today.CurrentDateTime
};
DbContext.Attendances.Add(attendance);
DbContext.SaveChanges(); //Exception here.
I have an Employee record in database.
Why I'm getting the exception?
Code First enables you to describe a model by using C# or Visual Basic .NET classes. The basic shape of the model is detected by using conventions. Conventions are sets of rules that are used to automatically configure a conceptual model based on class definitions when working with Code First. The conventions are defined in the System.Data.Entity.ModelConfiguration.Conventions namespace.
You can further configure your model by using data annotations or the fluent API. Precedence is given to configuration through the fluent API followed by data annotations and then conventions. For more information see Data Annotations, Fluent API - Relationships, Fluent API - Types & Properties and Fluent API with VB.NET.
Here you find more about Entity Framework Code First Conventions
You set wrong ids name as FK and PK, you need add primary key for Attendance also,follow code first conventions name, change your model like:
public class Employee
{
public Employee()
{
Attendances = new List<Attendance>();
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long EmployeeId { get; set; }
public string Fullname { get; set; }
public virtual ICollection<Attendance> Attendances { get; set; }
}
public class Attendance
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long AttendanceId { get; set; }
public DateTime CheckinDateTime { get; set; }
public DateTime? CheckoutDateTime { get; set; }
[Required]
[ForeignKey("Employee")]
public long EmployeeId { get; set; }
public virtual Employee Employee { get; set; }
}
ForeignKey attribute is applied on Attendance navigation property to specify foreignkey property name for Attendance property.
Without DataAnnotation we can use Fluent API for configuration our relationship. Ofcourse you need use code first convention names
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//one-to-many
modelBuilder.Entity<Attendance>()
.HasRequired<Employee>(e => e.Employee) // Attendance entity requires Employee
.WithMany(a => a.Attendances); // Employee entity includes many Attendances entities
}
If your model not contains convention name, using Fluent API can use .HasForeignKey() and set specific name FK
public class Attendance
{
public long AttendanceId { get; set; }
public DateTime CheckinDateTime { get; set; }
public DateTime? CheckoutDateTime { get; set; }
//Not first code convention name
public long EmpId { get; set; }
public virtual Employee Employee { get; set; }
}
.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//one-to-many
modelBuilder.Entity<Attendance>()
.HasRequired<Employee>(e => e.Employee)
.WithMany(a => a.Attendances)
.HasForeignKey(e => e.EmpId);
}

code first one-to-one enable cascade delete

I have one to one relationship with foreign keys but the Cascade Delete is not enabled for some reason. The sample code is below.
public class AppRegistration
{
public int AppRegistrationId { get; set; }
[Required]
[StringLength(50)]
[Display(Name = "Username")]
public string UserName { get; set; }
[Required]
[StringLength(100)]
public string Password { get; set; }
[StringLength(20)]
public string StudentOrAgent { get; set; }
// navigation properties
public virtual AppStatus AppStatus { get; set; }
public virtual Agreement Agreement { get; set; }
public virtual AnotherTable AnotherTable { get; set; }
}
The dependent table with a foreign key is below.
public class Agreement
{
[Key]
[ForeignKey("AppRegistration")]
public int AppRegistrationId { get; set; }
public DateTime DateAgreed { get; set; }
public virtual AppRegistration AppRegistration { get; set; }
}
When I try to delete an entry from the generated AppRegistrations table I get a Reference constraint conflict.
I tried putting [Required] on the navigation property in the dependent table but it doesn't do anything - the Update-Database command shows the No pending code-based migrations. message. Any ideas? Thanks.
Update:
I'm getting the following error message:
The DELETE statement conflicted with the REFERENCE constraint "FK_dbo.AppStatus_dbo.AppRegistrations_AppRegistrationId". The conflict occurred in database "MVCapp", table "dbo.AppStatus", column 'AppRegistrationId'.
I decided to work out the cascade delete problem in a separate sample project. I found the following blog & MSDN pages very useful.
http://blog.bennymichielsen.be/2011/06/02/entity-framework-4-1-one-to-one-mapping/
http://msdn.microsoft.com/en-us/library/gg671256%28v=VS.103%29.aspx
http://msdn.microsoft.com/en-us/library/gg671273%28v=VS.103%29.aspx
Using the Code First approach create the following Model.
public class Category
{
public int CategoryId { get; set; }
public string CategoryName { get; set; }
public virtual Book Book { get; set; }
}
public class Book
{
public int CategoryId { get; set; }
public string BookTitle { get; set; }
public string BookAuthor { get; set; }
public string BookISBN { get; set; }
public virtual Category Category { get; set; }
}
(I realize the entity names suggest one-to-many relationship, but I am trying to model 1-to-1 relationship, as in my original question at the top.)
So, in the above model each Category can only have one Book.
In your DbContext-derived class add the following.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Book>()
.HasKey(t => t.CategoryId);
modelBuilder.Entity<Category>()
.HasRequired(t => t.Book)
.WithRequiredPrincipal(t => t.Category)
.WillCascadeOnDelete(true);
}
(The following namespaces are required for the above code: System.Data.Entity, System.Data.Entity.ModelConfiguration.Conventions.)
This properly creates the 1-to-1 relationship. You'll have a primary key in each table and also a foreign key in Book table with ON DELETE CASCADE enabled.
In the above code, on the Category entity I used WithRequiredPrincipal() with t => t.Category argument, where the argument is the foreign key column in the dependent table.
If you use WithRequiredPrincipal() without an argument you'll get an extra column in the Book table and you'll have two foreign keys in the Book table pointing to CategoryId in Category table.
I hope this info helps.
UPDATE
Later on I found answer directly here:
http://msdn.microsoft.com/en-us/data/jj591620#RequiredToRequired
A reason why you're not getting cascading delete is because your relationship is optional.
If you want the relationship required i.e. an AppRegistration has to have one Agreement you can use (cascading delete configured automatically):
public class Agreement
{
...
[Required]
public AppRegistration AppRegistration{ get; set; }
}
If you want the relationship to be optional with cascading delete you can configure this using Fluent API:
modelBuilder.Entity<AppRegistration>()
.HasOptional(a => a.Agreement)
.WithOptionalDependent()
.WillCascadeOnDelete(true);

Specifying Foreign Key Entity Framework Code First, Fluent Api

I have a question about defining Foreign Key in EF Code First Fluent API.
I have a scenario like this:
Two class Person and Car. In my scenario Car can have assign Person or not (one or zero relationship).
Code:
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Car
{
public int Id { get; set; }
public string Name { get; set; }
public Person Person { get; set; }
public int? PPPPP { get; set; }
}
class TestContext : DbContext
{
public DbSet<Person> Persons { get; set; }
public DbSet<Car> Cars { get; set; }
public TestContext(string connectionString) : base(connectionString)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Car>()
.HasOptional(x => x.Person)
.WithMany()
.HasForeignKey(x => x.PPPPP)
.WillCascadeOnDelete(true);
base.OnModelCreating(modelBuilder);
}
}
In my sample I want to rename foreign key PersonId to PPPPP. In my mapping I say:
modelBuilder.Entity<Car>()
.HasOptional(x => x.Person)
.WithMany()
.HasForeignKey(x => x.PPPPP)
.WillCascadeOnDelete(true);
But my relationship is one to zero and I'm afraid I do mistake using WithMany method, but EF generate database with proper mappings, and everything works well.
Please say if I'm wrong in my Fluent API code or it's good way to do like now is done.
Thanks for help.
I do not see a problem with the use of fluent API here. If you do not want the collection navigational property(ie: Cars) on the Person class you can use the argument less WithMany method.