I am developing an application where I am using Entity framework. I have an abstract class called BaseEntity which is inherited by all other classes; it contains properties which are common to all other classes - Id(PK), CreatedDate, ModifiedDate, CreatedBy and ModifiedBy.
public abstract class BaseEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public DateTime CreatedDate
{
get ; set ;
}
public DateTime? ModifiedDate
{
get; set;
}
public string CreatedBy { get; set; }
public string ModifiedBy { get; set; }
[Timestamp]
public byte[] Version { get; set; }
}
I have a class called City which inherits the above class
public class City : BaseEntity
{
public string Name { get; set; }
}
The problem I am having now is Entity framework is rightfully inserting the PK value in the column Id but its creating an extra column City_Id which is null. Since I am using the Id column from the base class as the PK I don't need the City_Id column. How can I get rid of it? I know it's the convention of Entity Framework to use ID or Type_Id as the PK.
Fluent API configuration
public class CityConfig : EntityTypeConfiguration<City>
{
public CityConfig()
{
Property(x => x.Name).IsRequired().HasMaxLength(50);
Property(x => x.CreatedBy).IsRequired().HasMaxLength(50);
Property(x => x.ModifiedBy).IsRequired().HasMaxLength(50);
}
}
And my context is as shown below
public virtual DbSet<City> Cities { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
//Other lines removed for brevity
modelBuilder.Configurations.Add(new CityConfig());
}
Related
im using entity framework core 5.0 and i created my one to many relationship with fluent api.
im getting that error when i try to create a new user in my project.
let me show u to my User class:
public class User : Base
{
[Key]
public int UserID { get; set; }
public string UserName { get; set; }
public string UserSurname { get; set; }
public string UserPassword { get; set; }
public string UserEMail { get; set; }
public int? AgencyID { get; set; }
public virtual Agency Agency { get; set; }
}
public class UserConfiguration : IEntityTypeConfiguration<User>
{
public void Configure(EntityTypeBuilder<User> builder)
{
builder.HasKey(user => user.UserID);
}
}
and here its a Agency class which is related to User class:
public class Agency : Base
{
[Key]
public int AgencyID { get; set; }
public string AgencyName { get; set; }
public string AgencyPhoto { get; set; }
public string AgencyEMail { get; set; }
public string AgencyPhone { get; set; }
public int AgencyExportArea { get; set; }
public virtual ICollection<User> Users { get; set; }
}
public class AgencyConfiguration : IEntityTypeConfiguration<Agency>
{
public void Configure(EntityTypeBuilder<Agency> builder)
{
builder.HasKey(agency => agency.AgencyID);
builder.HasMany(us => us.Users)
.WithOne(us => us.Agency)
.HasForeignKey(au => au.UserID)
.IsRequired(false)
}
}
i know,im getting that error SqlException: The INSERT statement conflicted with the FOREIGN KEY constraint "FK_Users_Agencies_UserID". The conflict occurred in database "anan", table "dbo.Agencies", column 'AgencyID'. because there is a no data in Agency table. The thing which im trying to do is make that AgencyID foreign key optional as a nullable. in User class u can see i defined that AgencyID as a nullable.
do i really need to define that relationship as a one-to-one or zero or is there a another way to do that ?
if i have to define that relationship as a one-to-one or zero,can u show me the way how can i do that.
Since you are using EF core 5 you don't need:
public class UserConfiguration : IEntityTypeConfiguration<User>
and
public class AgencyConfiguration : IEntityTypeConfiguration<Agency>
All this code is reduntant. You have a standart one-to-many relation that EF core recognizes and configures by default. Remove all of this code and everything will be fine.
But if you are a student and need to do everything hard way, you can add this reduntant code:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>(entity =>
{
entity.HasOne(d => d.Agency)
.WithMany(p => p.Users)
.HasForeignKey(d => d.AgencyId)
.OnDelete(DeleteBehavior.ClientSetNull);
});
}
And since you are interested in a configuration, these are another redundant attributes:
public class User : Base
{
[Key]
public int UserID { get; set; }
.....
public int? AgencyID { get; set; }
[ForeignKey(nameof(AgencyId))]
[InverseProperty("Users")]
public virtual Agency Agency { get; set; }
}
public class Agency : Base
{
[Key]
public int AgencyID { get; set; }
.....
[InverseProperty(nameof(User.Agency))]
public virtual ICollection<User> Users { get; set; }
}
I have the a Error "'Class1' cannot be used as a property on entity type 'Class2' because it is configured as a navigation." when using add migration.
I cant add a Unique Contraint at a Navigator property.
Using EF Core 3.1
Class1:
public class Class1
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Class1Id { get; set; }
public DateTime ExtractedDate { get; set; }
[Required]
public virtual ICollection<Class2> Class2Id { get; set; }
}
Class2:
public class Class2
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Class2Id { get; set; }
[MaxLength(256)]
public string LicenceKey { get; set; }
[Required]
public Class1 Class1Id { get; set; }
}
Here is my Dbcontext class:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
//Property Configurations
modelBuilder.Entity<Class1>()
.HasMany(c => c.Class2Id)
.WithOne(e => e.Class1Id);
modelBuilder.Entity<Class1>()
.HasIndex(ir => ir.ExtractedDate)
.IsUnique();
modelBuilder.Entity<Class2>()
.HasIndex(ir => new {ir.Class1Id, ir.LicenceKey})
.IsUnique();
}
Is there a way that i can declare the Contraints with the Navigators? or if there is another way?
I tryed also Using SQL Connection and manually adding the Constrains after the tables was created and it works. But then im not using fully the EF Core.
If I have the following model:
[Table("Person")]
public class PersonDao
{
public Guid Id { get; set; }
public ICollection<Address> { get; set; }
// other properties
}
[Table("Address")]
public class AddressDao
{
public Guid Id { get; set; }
public PersonDao Person { get; set; }
// other properties
}
Entity Framework uses Person and Address correctly for the table names but the foreign key in Address is called PersonDao_Id. I want it to be Person_Id.
Is this a bug or am I supposed to write a custom convention for the property names?
NOTE: I use MySQL with Entity Framework, I don't know if that matters.
EDIT: I know that I can specify the column name manually using the ForeignKey attribute or the fluent API. I need this to work automatically and globally.
Use attributes just like you did to have different names for the table and class:
[Table("Address")]
public class AddressDao
{
public Guid Id { get; set; }
[ForeignKey("Person_Id")]
public PersonDao Person { get; set; }
// other properties
}
If you don't want to use the default convention you could just remove Dao from your class names:
public class Person
{
public Guid Id { get; set; }
public ICollection<Address> { get; set; }
// other properties
}
public class Address
{
public Guid Id { get; set; }
public Person Person { get; set; }
// other properties
}
If you want make your own name of column in database, you can use Fluent API in protected override void OnModelCreating(DbModelBuilder modelBuilder) method in your database context. Add to your DAO class properties with column name.
[Table("Person")]
public class PersonDao
{
public Guid Id { get; set; }
public ICollection<Address> Addresses { get; set; }
// other properties
}
[Table("Address")]
public class AddressDao
{
public Guid Id { get; set; }
public Guid MyPersonDaoColumnName { get; set; }
public PersonDao Person { get; set; }
// other properties
}
and then in Fluent API write:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<AddressDao>().HasRequired(x => x.Person)
.WithMany(x => x.Addresses)
.HasForeignKey(x => x.MyPersonDaoColumnName);
}
but it is ugly to mix Fluent API with Attribute so you need also:
modelBuilder.Entity<AddressDao>.ToTable("Address");
modelBuilder.Entity<PersonDao>.ToTable("Person");
Hi There I have the following Model
Template (Id,Name)
UserBody (Id, name)
EmployeeBody (Id, Name)
I then Have a template mappers where i associate a template with one of many
users and employess.
TemplatesMaps (id, TemplateId, UserId, EmployeeId) userid and employeeId are nullable
I need a TemplatesMaps to consist of 1 templateid mapping to many Userbody.id 's and many EmployeeBody.Id's
Example
Id TemplateId UserBodyId, EmployeeBodyId
1 1 1 Null
2 1 Null Null
3 2 4 Null
4 2 Null 5
MY Code is as follows
public class UserBody
{
[Key]
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
public class EmployeeBody
{
[Key]
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
public class Template
{
[Key]
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
public class TemplatesMaps
{
[Key]
public virtual int Id { get; set; }
public virtual Template Template { get; set; }
public virtual ICollection<EmployeeBody> Employees { get; set; }
public virtual ICollection<UserBody> Users { get; set; }
}
public class MyDbContext : DbContext
{
public virtual IDbSet<EmployeeBody> EmployeeBody { get; set; }
public virtual IDbSet<UserBody> UserBody { get; set; }
public virtual IDbSet<Template> Templates { get; set; }
public virtual IDbSet<TemplatesMaps> TemplatesMaps { get; set; }
public MyDbContext() : base("Default")
{
Database.SetInitializer<TrawlerDbContext>(null);
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<TemplatesMaps>().HasOptional(o => o.Employees).WithMany().Map(m => m.MapKey("EmployeeId"));
modelBuilder.Entity<TemplatesMaps>().HasOptional(o => o.Usersus).WithMany().Map(m => m.MapKey("UserId"));
base.OnModelCreating(modelBuilder);
}
//when i run the following i get the error The declared type of navigation property XYZ is not compatible with the result of the specified navigation.
var test = _templateMapperRepo.GetAll().Where(x => x.Template.Id == input.TemplateId).Include(x => x.Users).Include(xx => xx.Employees);
TemplatesMaps (Id, TemplateId, UserId, EmployeeId) looks more like a junction table, thus requires a different entity model:
public class TemplatesMaps
{
[Key]
public virtual int Id { get; set; }
public virtual Template Template { get; set; }
public virtual EmployeeBody Employee { get; set; }
public virtual UserBody User { get; set; }
}
and setup
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<TemplatesMaps>().HasRequired(o => o.Template).WithMany().Map(m => m.MapKey("TemplateId"));
modelBuilder.Entity<TemplatesMaps>().HasOptional(o => o.Employee).WithMany().Map(m => m.MapKey("EmployeeId"));
modelBuilder.Entity<TemplatesMaps>().HasOptional(o => o.User).WithMany().Map(m => m.MapKey("UserId"));
base.OnModelCreating(modelBuilder);
}
If needed, you can add reverse navigation property
public virtual ICollection<TemplatesMaps> TemplateMaps { get; set; }
to any of the Template, UserBody and EmployeeBody classes. Just make sure to update the corresponding WithMany configuration accordingly, i.e. WithMany() => WithMany(e => e.TemplateMaps).
Your two models don't agree. In your example, each Template record has 0..1 Employee and 0..1 User. You have multiple records with the same TemplateId, but they're still different records. EF doesn't know how to take all of those records and turn them into a single Template object in memory.
Assuming these are 1-to-many relationships (each Employee can only be in one Template, each User can only be in one template) your foreign keys are on the wrong side. Employee and User should both have TemplateIds.
If you have many-to-many relationships, you will need a new table to represent the association. See associative entity.
I am using entity framework 6 and i have a base entity called EntityBase, very simple as so
public abstract class EntityBase : IEntityBase
{
public virtual long Id { get; set; }
}
Each of my entities inherit from this. Now some entities need audit information, so i have first created an interface called IAudit
public interface IAudit
{
Audit Audit { get; set; }
}
And the Audit object like so
public class Audit
{
public bool IsDeleted { get; set; }
public DateTime? DeletedDate { get; set; }
public long? DeletedByUserId { get; set; }
public DateTime CreatedDate { get; set; }
public long CreatedByUserId { get; set; }
public DateTime UpdatedDate { get; set; }
public long UpdatedByUserId { get; set; }
public byte[] RowVersion { get; set; }
}
And if an entity needs audit information, i apply this interface. Here is an example
public class Attachment : EntityBase, IAudit
{
#region IAudit
public Audit Audit { get; set; }
#endregion
public string Name { get; set; }
}
I am using code first, so now in my DbContext, i have this in my OnModelCreating method
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Configurations.Add(new AttachmentConfig());
}
Here is my AttachmentConfig file, which inherits from EntityBaseConfig, both included below
public abstract class EntityBaseConfig<TEntity> : EntityTypeConfiguration<TEntity>
where TEntity : EntityBase
{
public EntityBaseConfig()
{
this.HasKey(e => e.Id);
}
}
class AttachmentConfig : EntityBaseConfig<Attachment>
{
public AttachmentConfig()
: base()
{
this.Property(e => e.Name)
.HasMaxLength(255)
.IsRequired();
}
}
Now when my table is created, the Attachment table has the columns i would expect, but Audit_RowVersion is varbinary(max) instead of timestamp.
I have tried to put this line in each config file but get this error.
this.Property(e => e.Audit.RowVersion).IsRowVersion();
Schema specified is not valid. Errors: (36,14) : error 2039: The
conceptual side property 'RowVersion' has already been mapped to a
storage property with type 'rowversion'. If the conceptual side
property is mapped to multiple properties in the storage model, make
sure that all the properties in the storage model have the same type.
But i do not want to write this line in each file anyway. How can i get the Audit.RowVersion column to generate as a timestamp, and ideally write it once, so that all objects that implement IAudit also get the configured fields?
EDIT:
I have now added a config file for the Audit object, which looks like this
public class AuditConfig : EntityTypeConfiguration<Audit>
{
public AuditConfig() : base()
{
this.Property(e => e.RowVersion)
.IsRowVersion();
}
}
And i call this in the OnModelCreating method, like so, before the other Configuration calls
modelBuilder.Configurations.Add(new AuditConfig());
Now when i run my project, i get the following error
A table can only have one timestamp column. Because table 'Attachment'
already has one, the column 'Audit_RowVersion' cannot be added.
If i look at the database and Attachment table created so far, it has the fields in the Audit object, but they do not have the Audit_ prefix? Maybe this is a clue to someone?
You can use DataAnnotations.
In your model class
public class Audit
{
public bool IsDeleted { get; set; }
public DateTime? DeletedDate { get; set; }
public long? DeletedByUserId { get; set; }
public DateTime CreatedDate { get; set; }
public long CreatedByUserId { get; set; }
public DateTime UpdatedDate { get; set; }
public long UpdatedByUserId { get; set; }
[TimeStamp]
public byte[] RowVersion { get; set; }
}
You can add [TimeStamp] Attribute for your variable.