Given a database with a table A. I want to create a table B and add a one-to-may relation between A and B with a required foreign key.
For example suppose that have an existing table 'Users' and then we want to add 'Roles' to the existing users.
The code first definition of those entities is as follows:
public class User
{
public Id UserId { get; set; }
public string Email { get; set; }
public string UserName => Email;
public Roles Role { get; set; }
public int? RoleId { get; set; }
}
public class Roles
{
public string RoleName { get; set; }
public int RoleId { get; set; };
public ICollection<User> GetUsers { get; set; }
}
The Configure Method for the Users using Fluent API is as follows:
public void Configure(EntityTypeBuilder<User> builder)
{
builder.ToTable("User");
builder.HasKey(t => t.UserId );
builder.Property(t => t.UserId ).ValueGeneratedOnAdd();
builder.HasOne(dt => dt.Role)
.WithMany(d => d.GetUsers)
.HasForeignKey(dt => dt.RoleId)
.HasConstraintName("ForeignKey_UserRole")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
}
Trying to run the migration I got this error message:
'The ALTER TABLE statement conflicted with the FOREIGN KEY constraint'
By splitting this migration in two migrations and seeding the data between the them allowed me to build the new database:
The first one remove the constrain IsRequired on the Fluent APi definition of the User entity and allowing null value for the foreign key RoleId.
Then Seed the database
Add the second and last migration to enable the constrain of the required foreign key RoleID in the User entity and removing the allows null on the foreign key.
My question is related to if there is an strategy that allows to add a new relation using code first approach with a required foreign key using only one migration?
Thank you
Francisco
Related
I need to add a new party entity (table). This entity follows party design pattern where user, organization unit and role entity Id is primary key and also a foreign key that links to the party entity primary key. I was able to achieve this with user entity and organization entity but not role entity because the role Id is int.
EF core complaints the role table's primary key type mismatched with the party table primary key.
Below are the code samples:
[Serializable]
[Table("MdParties")]
public class Party : FullAuditedEntity<long>, IMayHaveTenant
{
public int? TenantId { get; set; }
}
public partial class User
{
[Required, ForeignKey(nameof(Party))]
public override long Id { get; set; } // PK and FK pointing to Party
public virtual Party Party { get; set; }
}
public class OrganizationUnitExt : OrganizationUnit
{
[Required, ForeignKey(nameof(Party))]
public override long Id { get; set; } // PK and FK pointing to Party
public virtual Party Party { get; set; }
}
public partial class Role : AbpRole<User>
{
[Required, ForeignKey(nameof(Party))]
public override int Id { get; set; } // PK and FK pointing to Party
public virtual Party Party { get; set; }
}
modelBuilder.Entity<User>(b =>
{
b.HasIndex(e => new { e.UserName });
b.HasOne(d => d.Party)
.WithMany()
.HasForeignKey(d => d.Id)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("FK_AbpUsers_PartyId");
});
modelBuilder.Entity<OrganizationUnitExt>(entity =>
{
entity.HasOne(d => d.Party)
.WithMany()
.HasForeignKey(d => d.Id)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("FK_AbpOrganizationUnits_PartyId");
});
You cannot change RoleId type without rebuilding standard Abp package. This is how it was design unfortunetly. However you can walkaround this and add additional table with RoleId and in modelbuilder make it table as Unique Tenant Wise. This will be not trully SQL strict implementation cause you will get one-to-many relation but behaviour of it will be as you expect.
I have two classes
public class Product
{
public Guid Id { get; set; }
public string ProductDetails { get; set; }
}
public class SpecialProductDetails
{
public Guid Product_Id { get; set; } // PK and FK to Product class
public string SpecialName { get; set; }
public string SpecialDescription { get; set; }
public virtual Product Product { get; set; }
}
SpecialProductDetails is mapped 1-1 with Product class and is optional. It shares the same PrimaryKey and ForeignKey.
In Fluent API i am mapping this relationship like this (inside SpecialProductDetails)
public SpecialProductDetails()
{
HasKey(p => p.Product_Id);
HasRequired(p => p.Product).WithMany().HasForeignKey(p => p.Product_Id).WillCascadeDelete(true);
}
This gives me this error when trying to generate the Database
\tSystem.Data.Entity.Edm.EdmAssociationEnd: : Multiplicity is not valid in Role 'SpecialProductDetails_Product_Source' in relationship 'SpecialProductDetails_Product_Source'. Because the Dependent Role refers to the key properties, the upper bound of the multiplicity of the Dependent Role must be '1'.
How can i have a column set as PK and FK on EF Code First?
I'm quite sure you have already solved that, but I hit the same problem and the solution I found was:
public SpecialProductDetails()
{
HasKey(p => p.Product_Id);
HasRequired(p => p.Product).WithOptional();
}
"it worth noting that when we are mapping a one-to-one association with fluent API, we don't need to specify the foreign key as we would do when mapping a one-to-many association with HasForeignKey method. Since EF only supports one-to-one associations on primary keys, it will automatically create the relationship in the database on the primary keys."
after http://weblogs.asp.net/manavi/associations-in-ef-4-1-code-first-part-3-shared-primary-key-associations
I have a question.
I have these two tables:
The principal table is User with Customer dependence.
The reverse engineer code first generated classes as follows:
public class User
{
public User()
{
this.Customers = new List<Customer>();
}
...
public virtual ICollection<Customer> Customers { get; set; }
}
public class Customer
{
public Customer()
{
}
...
public int UserID { get; set; }
public virtual User User { get; set; }
}
I made the following modification in the user class:
public class User
{
public User()
{
}
public int CustomerID { get; set; }
public virtual Customer Customer { get; set; }
}
Because the relationship is One-to–Zero-or-One.
The original mapping is this:
// Relationships
this.HasRequired(t => t.User)
.WithMany(t => t.Customers)
.HasForeignKey(d => d.UserID);
And the modified mapping is this :
this.HasRequired(t => t.User)
.WithOptional(t => t.Customer)
.Map(m => m.MapKey("UserID"));
Is That correct?
If not, how would this mapping?
Thanks.
No, it's not correct.
The best thing you can do - supposed you can change the database schema - is removing the UserID foreign key from the Customer table and then create the relationship in the database between the two primary keys so that Customer.CustomerID is the foreign key in the association.
Reverse Engineering should then automatically create the expected one-to-one relationship, like so:
public class Customer
{
public int CustomerID { get; set; }
public virtual User User { get; set; }
//...
}
public class User
{
public int UserID { get; set; }
public virtual Customer Customer { get; set; }
//...
}
//...
this.HasRequired(t => t.User)
.WithOptional(t => t.Customer);
If you can't change the database schema, your best bet is to only remove the collection ICollection<Customer> Customers from the User class and keep the relationship as one-to-many.
The reason for all this is that EF only supports shared primary key one-to-one associations, but not foreign key one-to-one associations. (The latter one you can only "fake" by removing the collection, but it's still one-to-many from EF viewpoint.)
You can read more about one-to-one associations with EF and its limitations here:
One-to-one Shared Primary Key Associations
One-to-one Foreign Key Associations
I have 2 entities Role and Permission with association one-to-many accordingly.
public class Role
{
public int Id { get; set; }
public bool IsAdmin { get; set; }
public virtual ICollection<Permission> Permissions { get; set; }
}
public class Permission
{
public int Id { get; set; }
public string Code { get; set; }
public string GroupName { get; set; }
public virtual Role Role { get; set; }
}
And created mapping classes for them inherited from EntityTypeConfiguration class.
When I run my application EF created database for me and foreign key for these entities above was Role_Id.
How can I change existing or add new convention to get ride of the underscore in foreign key?
So I want to have RoleId as a foreign key for my entities.
I don't want use data annotation attributes and don't want to add extra property to Permission class (public int RoleId { get; set; }) in order to use it in mapping like this:
HasRequired(x => x.Role).WithMany(y => y.Permissions).HasForeignKey(o => o.RoleId);
Thanks,
Alexey
Entity framework currently doesn't support custom global conventions but you can overwrite the name of the key in fluen API:
modelBuilder.Entity<Permission>()
.HasRequired(x => x.Role)
.WithMany(y => y.Permissions)
.Map(m => m.MapKey("RoleId"));
I'm trying to do this relationship
public class Company {
public int Id { get; set; }
public Configuration Configuration { get; set; }
}
public class Configuration {
public int Id { get; set; }
// public int CompanyId { get; set; } -> can't do this
public Company Company { get; set; }
}
public class ConfigurationMapping : EntityTypeConfiguration<Configuration> {
public ConfigurationMapping {
HasRequired(configuration => configuration.Company)
.WithOptional(company => company.Configuration)
// .HasForeignKey(configuration => configuration.CompanyId) -> this doesn't exist
.Map(f => f.MapKey("CompanyId")); // that's why I can't use the property above
}
}
I can't understand how I can add a Configuration and set the IdCompany.
There's another approach?
How can I do this?
db.Configurations.Add(new Configuration {
IdCompany = idCompany
});
db.SaveChanges();
You cannot. One-to-one relation is currently supported only if dependent entity has FK to principal entity as its primary key. It means that Configuration.Id is PK of Configuration but also FK to Company.Id.
The reason why you cannot use CompanyId is that database would require it to use unique index (otherwise it would not be one-to-one relation but one-to-many) and EF currently doesn't support unique indexes.
Edit:
Sorry. Now I better understand your question. If you know the Id of the company and you want to add it a new configuration you can try to do something like this:
var company = new Company { Id = companyId };
context.Companies.Attach(company); // Existing company, EF know thinks it was loaded from DB
var configuration = new Configuration { Company = company }; // Create relation with existing company. It must not be related to other configuration!
context.Configurations.Add(configuration);