Could anybody tell me what's wrong with this code below, because I'm having problems getting an one-to-one relation working with EF 4.3 code first.
// Problem with EF 4.3 code first, one-to-one relation
// context
// ------------
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<SecondModel>().HasRequired(r => r.FirstModel).WithOptional(r => r.SecondModel).WillCascadeOnDelete(false);
}
// models
// --------
public abstract class MyBaseEntity : // some interfaces
{
// ...
[EdmScalarPropertyAttribute(EntityKeyProperty = true, IsNullable = false)]
public virtual int Id
{
get { return GetPropertyValue<int>("Id"); }
set { SetPropertyValue<int>("Id", value); }
}
// ...
}
public class FirstModel : MyBaseEntity
{
// ...
public int SecondModelID { get; set; }
public virtual SecondModel SecondModel { get; set; }
// ...
}
public class SecondModel : MyBaseEntity
{
// ...
public int FirstModelID
{
get { return GetPropertyValue<int>("FirstModelID"); }
set { SetPropertyValue<int>("FirstModelID", value); }
}
public virtual FirstModel FirstModel { get; set; }
// ...
}
// this code above doesn't seem to work :s
// when removing FirstModelID and removing SecondModelID i'm unable to create the the database
Have been trying all kinds of things, adding foreignkey attributes, (un)commenting some id's, following samples.
Results were always: IDs in database are not correctly or it doesn't create the database.
Thanks in advance.
I dont have EF 4.3 but it is surprising if it is different from EF 4.1 in this regard. I've done it by configuring the model in the fluent API, overriding the ModelBuilder like this inside the DbContext class:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<FirstModel>()
.HasRequired(e => e.SecondModel)
.WithRequiredPrincipal()
.WillCascadeOnDelete(true);
base.OnModelCreating(modelBuilder);
}
And the classes:
public class FirstModel : MyBaseEntity
{
// ...
public virtual SecondModel SecondModel { get; set; }
// ...
}
public class SecondModel : MyBaseEntity
{
// ...
public int Id
{
get { return GetPropertyValue<int>("FirstModelID"); }
set { SetPropertyValue<int>("FirstModelID", value); }
}
// ...
}
Related
I've problem with seeding data to database. Eariler I tried way from this tut: Seed Data in EF 6 Code-First
and then the seed method is never called
DBSchool.cs
namespace SchoolTest.DAL
{
public class DBSchool : DbContext
{
public DBSchool() : base("DBSchool")
{
Database.SetInitializer(new Seeder());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
public DbSet<Guest> Guests { get; set; }
}
}
Seeder.cs
public class Seeder : DropCreateDatabaseAlways<DBSchool>
{
protected override void Seed(DBSchool context)
{
IList<Guest> GuestList = new List<Guest>();
GuestList.Add(new Guest()
{
Name = "Dexter",
Surname = "Dexter",
Email = "test#test.com"
});
context.Guests.AddRange(GuestList);
context.SaveChanges();
base.Seed(context);
}
}
Guest.cs
public class Guest
{
public string Name { get; set; }
public string Surname { get; set; }
public string Email { get; set; }
[Key]
public int GuestId { get; set; }
}
App.config
<appSettings>
<add key="DatabaseInitializerForType SchoolTest.DAL.DBSchool, SchoolTest"
value="SchoolTest.Data.Seeder, SchoolTest" />
</appSettings>
Is there any way to call the Seed() method or just through the Configuration.cs?
Try changing your code like this.
public class DBSchool : DbContext
{
public DBSchool() : base("name=<database-name>")
{
Database.SetInitializer<DBSchool>(new Seeder());
}
// Rest of your implementation
}
Replace <database-name> with the name of your database.
If that didn't work, you can give a Generic Type Parameter to the context class and change your code as follows.
Seeder.cs -> public class Seeder<T> : DropCreateDatabaseAlways<DBSchool>
DBSchool.cs -> Database.SetInitializer<DBSchool>(new Seeder<DBSchool>());
Read more on that here.
If that didn't work either, you can use migrations and seed data using custom sql using Sql().
I'm using EF Core and SQLite in unit tests. Given the following 1-to-1 entities:
public class Entity1 : FullAuditedEntity<int>
{
public Entity2 Entity2 { get; set; }
public string Name { get; set; }
}
public class Entity2 : FullAuditedEntity<int>
{
public string Name { get; set; }
[ForeignKey("Entity1Id")]
public Entity1 Entity1 { get; set; }
public int Entity1Id { get; set; }
}
DbContext class has the following code:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
...
modelBuilder.Entity<Entity1>().HasOne(t => t.Entity2)
.WithOne(t1 => t1.Entity1)
.OnDelete(DeleteBehavior.Cascade);
...
}
I have a test method:
[Fact]
public async Task Should_Create_And_Then_Delete_Single_Entity1()
{
var entity1Service = Resolve<Entity1Service>();
var entity1Repo = Resolve<IRepository<Entity1>>();
var entity2Repo = Resolve<IRepository<Entity2>>();
var entity1 = new entity1 { Name = "ent1" };
entity2Repo.Count().ShouldBe(0);
// entity2 created also, see assert below
var created = await entity1Service.CreateEntity1Async(entity1).ConfigureAwait(false);
created.Id.ShouldBe(1);
created.Name.ShouldBe("net1");
entity1Repo.Count().ShouldBe(1);
entity2Repo.Count().ShouldBe(1);
var ent = await entity1Service.GetEntity1Async(created.Id).ConfigureAwait(false);
ent.ShouldNotBeNull();
ent.Entity2.ShouldNotBeNull();
await entity1Service.DeleteEntity1Async(ent.Id).ConfigureAwait(false);
entity1Repo.Count().ShouldBe(0);
entity2Repo.Count().ShouldBe(0);
}
The problem is that the last line of code, "entity2Repo.Count().ShouldBe(0);" assertion is broken, it is actually 1 instead of 0, IsDeleted (soft delete) is false, but I expect it to be true.
What am I doing wrong?
Thanks in advance.
You need to cascade soft deletes yourself.
See the rationale in the closed PR aspnetboilerplate/aspnetboilerplate#3559.
You can do that by defining an event handler:
public class Entity1DeletingCascader : IEventHandler<EntityDeletingEventData<Entity1>>, ITransientDependency
{
private readonly IRepository<Entity2> _entity2Repository;
public Entity1DeletingCascader(IRepository<Entity2> entity2Repository)
{
_entity2Repository = entity2Repository;
}
[UnitOfWork]
public virtual void HandleEvent(EntityDeletingEventData<Entity1> eventData)
{
var entity1 = eventData.Entity;
_entity2Repository.Delete(e2 => e2.Entity1Id == entity1.Id);
}
}
Im learning Postgresql with EF Core 2.0
im keep getting this error. I searched on google but but I am not able to find the right answer.
Here is some detail of my code ;
Startup.CS;
public void ConfigureServices(IServiceCollection services)
{
var connectionString = #"User ID=postgres;Password=postgres;Host=localhost;Port=5432;Database=Test;";
services.AddEntityFrameworkNpgsql()
.AddDbContext<DataContext>(options => options.UseNpgsql(connectionString));
services.AddMvc();
}
and Data Context ;
public class DataContext : DbContext
{
public DataContext(DbContextOptions<DataContext> options) : base(options)
{
}
public DbSet<Questions> Questions { get; set; }
}
and my model ;
public class Questions
{
public int Id { get; set; }
public int studentId { get; set; }
public string Title { get; set; }
public int Votes { get; set; }
}
any help would be appreciated.
Stumbled across this question and, although late to the party, I've also experienced case sensitivity with Oracle (using Oracle.EntityFrameworkCore, where I needed uppercase) and postgres (using Npgsql.EntityFrameworkCore, where I needed lowercase) for table/column names.
I'll add my resolution here so that it helps anyone else in future. I resolved by creating an extension method as follows:
public static void LowercaseRelationalTableAndPropertyNames(this ModelBuilder modelBuilder)
{
foreach (var entity in modelBuilder.Model.GetEntityTypes())
{
entity.Relational().TableName = entity.Relational().TableName.ToLowerInvariant();
foreach (var property in entity.GetProperties())
{
property.Relational().ColumnName = property.Relational().ColumnName.ToLowerInvariant();
}
}
}
and using as follows:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.LowercaseRelationalTableAndPropertyNames();
}
which allows models to be built with mixed case and converted at runtime as follows:
public class Questions
{
public int Id { get; set; }
public int StudentId { get; set; }
}
Note, for EFCore 3.0, the extension method changes as follows:
public static void LowercaseRelationalTableAndPropertyNames(this ModelBuilder modelBuilder)
{
foreach (var entity in modelBuilder.Model.GetEntityTypes())
{
entity.SetTableName(entity.GetTableName().ToLowerInvariant());
foreach (var property in entity.GetProperties())
{
property.SetColumnName(property.GetColumnName().ToLowerInvariant());
}
}
}
Alright fixed.
i think postgresql is case sensetive.
when i changed model equal to database problem is solved.
here is my new model.
I have following code first model:
public class Model1 : DbContext
{
public Model1()
: base("name=Model1")
{
}
public virtual DbSet<Master> Masters { get; set; }
public virtual DbSet<Slave> Slaves { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Master>().Property(e => e.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<Slave>().Property(e => e.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<Master>().Property(e => e.Name).IsRequired();
modelBuilder.Entity<Slave>().Property(e => e.Name).IsRequired();
}
}
public interface IEntity
{
int Id { get; }
}
public class Master : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Slave> Slaves { get; set; }
public Master()
{
Slaves = new EntityHashSet<Slave>();
}
public Master(string name)
: this()
{
Id = name.GetHashCode();
Name = name;
}
public void Update(IEnumerable<Slave> slaves, Model1 model)
{
Slaves = new EntityHashSet<Slave>(slaves.Select(s => model.Slaves.CreateOrFind(s)));
}
public void Update(IEnumerable<string> slaves, Model1 model)
{
Update(slaves.Select(s => new Slave(s)), model);
}
}
public class Slave : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Master> Masters { get; set; }
public Slave()
{
Masters = new EntityHashSet<Master>();
}
public Slave(string name)
: this()
{
Id = name.GetHashCode();
Name = name;
}
}
I'm using the following utility classes:
public class EntityHashSet<TEntity> : HashSet<TEntity> where TEntity : IEntity
{
public EntityHashSet()
: base(new EntityEqualityComparer<TEntity>())
{ }
public EntityHashSet(IEnumerable<TEntity> collection)
: base(collection, new EntityEqualityComparer<TEntity>())
{ }
}
public class EntityEqualityComparer<TEntity> : IEqualityComparer<TEntity> where TEntity : IEntity
{
public bool Equals(TEntity x, TEntity y)
{
return x.Id.Equals(y.Id);
}
public int GetHashCode(TEntity obj)
{
return obj.Id.GetHashCode();
}
}
public static class ExtensionMethods
{
public static TEntity CreateOrFind<TEntity>(this DbSet<TEntity> dbSet, TEntity entity) where TEntity : class, IEntity
{
return dbSet.Find(entity.Id) ?? dbSet.Add(entity);
}
}
When I first add master entity to the database with the following code no error is thrown:
using (var model = new Model1())
{
var m = new Master("master1");
m.Update(new[] {"slave1", "slave2", "slave3"}, model);
model.Masters.Add(m);
model.SaveChanges();
}
When I try to use the update method for existing one, DbUpdateException is thrown:
var m = model.Masters.CreateOrFind(new Master("master1"));
m.Update(new[] {"slave1", "slave2", "slave3", "slave4"}, model);
model.SaveChanges();
Additional information: An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details.
Relevant inner exception:
Violation of PRIMARY KEY constraint 'PK_dbo.SlaveMasters'. Cannot insert duplicate key in object 'dbo.SlaveMasters'. The duplicate key value is (1928309069, -2136434452).
The statement has been terminated.
Why is this? I'm checking whether entities are already in the database or need to be created via CreateOrFind.
EDIT: To clarify, the line that produces the error is:
Slaves = new EntityHashSet<Slave>(slaves.Select(s => model.Slaves.CreateOrFind(s)));
Error is thrown when calling SaveChanges().
I postpone you must be use the previous ef config file ,so it will always try to insert the same value but update .
you could update or check your ef config file before update.
Found a dirty way of getting around this. Before I create new EntityHashSet I call raw SQL command deleting entries from SlaveMasters table that contains current master Id.
model.ExecuteSqlCommand("DELETE FROM SlaveMasters WHERE Master_Id = " + Id);
Slaves = new EntityHashSet<Slave>(slaves.Select(s => model.Slaves.CreateOrFind(s)));
I have a DbContext like this,
public class EPDContext : TrackerContext
{
public EPDContext()
: base("name=DevelopmentApplicationServices")
{
Database.SetInitializer<EPDContext>(new EPDDBInitializer());
this.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
}
public DbSet<TaskRevision> TaskRevisions { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<TaskRevision>().HasMany(x => x.Models).WithMany().Map(x => x.MapLeftKey("TaskRevisionID").MapRightKey("ModelId").ToTable("TaskRevision2Models"));
}
}
public class EPDDBInitializer : CreateDatabaseIfNotExists<EPDContext>
{
protected override void Seed(EPDContext context)
{
//// My Seeding data goes here
base.Seed(context);
}
}
And my Entity:
[TrackChanges]
public class TaskRevision
{
#region properties
[Key]
public Guid TaskRevisionID { get; set; }
public virtual List<Model> Models { get; set; }
}
and my migration configuration class looks like:
internal sealed class Configuration : DbMigrationsConfiguration<PW.EPD.Data.EPDContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
}
protected override void Seed(PW.EPD.Data.EPDContext context)
{
}
}
I got this error "There is already an object named 'TaskRevisions' in the database entity framework." when I execute my application. DB has created successfully and there is no seeding data.
At the same time when I execute the same code after removing the onModelCreating() override method, db has created with seed data.
What I did wrong here, kindly correct me.
Thanks in advance