Using Model-first and Table-per-heirachy, I can create two classes that inherit from the same base class, and map a column in each of the two derived classes to the same table column so that I can 're-use' columns.
If I try that with Code-first, I get the following error: "Each property name in a type must be unique. Property name 'XXX' was already defined."
I assume that this is a bug in code-first?
Here is some example code:
public class Parent
{
public Guid Id { get; set; }
}
public class ChildA : Parent
{
public Int32 ChildAProperty { get; set; }
}
public class ChildB : Parent
{
public Int32 ChildBProperty { get; set; }
}
public class TestContext : DbContext
{
public DbSet<Parent> Entities { get { return this.Set<Parent>(); } }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var childAConfig = modelBuilder.Entity<ChildA>();
childAConfig.Property(p => p.ChildAProperty).HasColumnName("Property");
var childBConfig = modelBuilder.Entity<ChildB>();
childBConfig.Property(p => p.ChildBProperty).HasColumnName("Property");
}
}
Related
I have a class named Owner that has a BitString property:
public class Owner
{
public BitString Mask { get; set; }
}
where BitString is a class that contains a list of BitStringSegments:
public class BitString
{
public List<BitStringSegment> Segments;
}
public class BitStringSegment
{
public long BitMask { get; set; }
public int MaskIndex { get; set; }
}
I want to map this using EF Core's Fluent API such that the BitString property named Mask is an Owned property of the class named Owner, and the BitMaskSegments of Mask are mapped to their own table, but I am unsure of the syntax to make it work properly.
public class OwnerConfiguration : IEntityTypeConfiguration<Owner>
{
public virtual void Configure(EntityTypeBuilder<Owner> builder)
{
builder.OwnsOne(x => x.Mask, b =>
{
b.WithOwner();
b.OwnsMany(s => s.Segments)
.ToTable("BitMaskSegments")
.WithOwner();
}).Navigation(x => x.Mask).IsRequired();
}
}
I am currently working on a codebase, to which I want to add a number of new entities with corresponding owned entities. Because, in some other part of the codebase I won't touch, UseLazyLoadingProxies is called; I receive the following exception:
System.InvalidOperationException : Navigation property 'Foo' on entity type 'FooOwner' is not virtual. UseLazyLoadingProxies requires all entity types to be public, unsealed, have virtual navigation properties, and have a public or protected constructor.
If I mark the property as virtual, the owned entity goes into a new table; which I do not want either.
According to github issues I encountered, these seem to be the expected behavior.
My question is this: Is there a way to work around this problem, such that, I can somehow mark the owned entity to be stored in the same table as the owner entity, and if possible to be always Included, eagerly loaded.
using System.Diagnostics;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using NUnit.Framework;
namespace StackOverflowObjectContext.Tests
{
public class Foo
{
public long Id { get; set; }
public int Data { get; set; }
}
public class FooOwner
{
public int Id { get; set; }
public Foo Foo { get; set; }
}
public class FooOwnerMap : IEntityTypeConfiguration<FooOwner>
{
public void Configure(EntityTypeBuilder<FooOwner> builder)
{
builder.HasKey(x => x.Id);
builder.HasOne(x => x.Foo);
}
}
public class StackOverflowObjectContext : DbContext
{
public StackOverflowObjectContext(DbContextOptions options) : base(options) { }
DbSet<FooOwner> FooOwners { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new FooOwnerMap());
base.OnModelCreating(modelBuilder);
}
}
[TestFixture]
public class StackOverflowTest
{
StackOverflowObjectContext _objectContext;
[SetUp]
public void SetUp()
{
var builder = new DbContextOptionsBuilder<StackOverflowObjectContext>()
.UseSqlServer(#"Data Source=.\SQLEXPRESS;Initial Catalog=StackOverflow;Integrated Security=True")
.UseLazyLoadingProxies();
_objectContext = new StackOverflowObjectContext(builder.Options);
}
[Test]
public void CanGenerateCreateScript()
{
var script = _objectContext.Database.GenerateCreateScript();
Debug.WriteLine(script);
}
}
}
You should use OwnsOne instead of HasOne
I am using Entity Framework 6 Code First for my project.
Entities have Inheritance so I am following TPH(Table per Hierarchy).
I read following Article and many others.
None of them explain a way in which I can use an existing DB Column mapped to a property in Base Entity as Discriminator.
Based on the sample below I get following Exception
One or more validation errors were detected during model generation:
TaskType: Name: Each property name in a type must be unique. Property name 'TaskType' is already defined.
I think EF's auto generated Discriminator and my Entities Mapping is Conflicting.
Is there a possible way to instruct EF to not auto generate column and use Entity mapped Column.
If not, is there any explanation of this can not be avoided.
Peace.
I have Entities in following format
public enum TaskType
{
Random = 0,
Polished = 1,
Dropping = 2
}
public interface ITask
{
int Id { get; set; }
string Name { get; set; }
TaskType typeofTask { get; set; }
}
public abstract class BaseTask : ITask
{
public BaseTask(string name, TaskType type)
{
this.Name = Name;
this.typeofTask = type;
}
public int Id { get; set; }
public string Name { get; set; }
public TaskType typeofTask { get; set; }
}
public class RandomTask : BaseTask
{
public RandomTask() : base("My Random", TaskType.Random)
{
}
public int Owner { get; set; }
}
public class PolishedTask : BaseTask
{
public PolishedTask() : base("My Polished", TaskType.Polished)
{
}
}
public class DBContextTest : DbContext
{
public DBContextTest(string connection) : base(connection)
{
}
public DbSet<BaseTask> Task { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<BaseTask>().Map<RandomTask>(m => m.Requires("TaskType").HasValue(1))
.Map<PolishedTask>(m => m.Requires("TaskType").HasValue(1));
modelBuilder.Entity<BaseTask>().Property(p => p.typeofTask).HasColumnName("TaskType");
}
}
class Program
{
static void Main(string[] args)
{
try
{
DBContextTest dataContext = new DBContextTest("Server = (localdb)\\mssqllocaldb;DataBase = LOC2;Trusted_Connection = True;");
RandomTask randomtask = new RandomTask();
PolishedTask polishedTask = new PolishedTask();
dataContext.Task.Add(randomtask);
dataContext.Task.Add(polishedTask);
dataContext.SaveChanges();
}
catch (System.Exception ex)
{
}
}
}
Remove TaskType from your entity and let EF manage that as part of the TPH mapping. To differentiate types if you're dealing with a base-class collection, use .OfType<PolishedTask>() rather than .Where(x => x.TaskType == TaskType.Polished) EF should take care of the rest. If you do want it on the entity, create a non-mapped property in your sub-classes.
I.e.
public abstract class BaseTask
{
[NotMapped]
public abstract TaskType TaskType { get; }
}
public class PolishedTask
{
[NotMapped]
public override TaskType TaskType => TaskType.Polished
// or
//public override TaskType TaskType
//{
// get { return TaskType.Polished; }
//}
}
I have a header-child tables with the child having different types but stored in the same table (TPH).
On top of this, user can snapshot a copy of a header and its children records and I would like to store the snapped copy into a different table since these snapshot records would be less frequent to view/modify.
To achieve this, I am mixing TPC to my existing TPH.
The new structure is as follows:
public class Header
{
private IList<Child> _childs = new List<Child>();
private IList<ChildSnapshot> _childSnapshots = new List<ChildSnapshot>();
[Key]
public int Id { get; set; }
public string Name { get; set; }
public IList<Child> Childs { get { return _childs; } }
public IList<ChildSnapshot> ChildSnapshots { get { return _childSnapshots; } }
}
public abstract class ChildBase
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public string Name { get; set; }
[ForeignKey("Header")]
public int HeaderId { get; set; }
[ForeignKey("HeaderId")]
public virtual Header Header { get; set; }
}
public abstract class Child : ChildBase
{
}
public class Child1 : Child
{
}
public class Child2 : Child
{
}
public abstract class ChildSnapshot : ChildBase
{
}
public class ChildSnapshot1 : ChildSnapshot
{
}
public class ChildSnapshot2 : ChildSnapshot
{
}
And the database context:
public class TestContext : DbContext
{
public DbSet<Header> Headers { get; set; }
public DbSet<Child> Childs { get; set; }
public DbSet<ChildSnapshot> ChildSnapshots { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Header>().Map(m => m.ToTable("Header"));
modelBuilder.Entity<Child>()
.Map<Child>(m =>
{
m.MapInheritedProperties();
m.ToTable("Child");
})
.Map<Child1>(m =>
{
m.Requires("Discriminator").HasValue("Child1");
})
.Map<Child2>(m =>
{
m.Requires("Discriminator").HasValue("Child2");
});
modelBuilder.Entity<ChildSnapshot>()
.Map<ChildSnapshot>(m =>
{
m.MapInheritedProperties();
m.ToTable("ChildSnapshot");
})
.Map<ChildSnapshot1>(m =>
{
m.Requires("Discriminator").HasValue("Child1");
})
.Map<ChildSnapshot2>(m =>
{
m.Requires("Discriminator").HasValue("Child2");
});
}
}
It works perfectly after many trials and errors. However, I've got to create 2 list properties in Header class. Is it possible to have only 1 list property of ChildBase type? I got the following error when I do so.
The type 'Child' cannot be mapped as defined because it maps inherited
properties from types that use entity splitting or another form of
inheritance. Either choose a different inheritance mapping strategy so
as to not map inherited properties, or change all types in the
hierarchy to map inherited properties and to not use splitting.
Why is the behaviour determined by the type of the container list? Can't EF infer from the type of the object in the list instead?
FYI I am using EF 4.3.
I am using Entity Framework Code First method to create my database table. The following code creates a DATETIME column in the database, but I want to create a DATE column.
//[Column(TypeName = "Date")]
public DateTime endDate { get; set; }
public DateTime startDate { get; set; }
The Column Attribute is not working.How can I create a column of type DATE, during table creation?
Thanks!
You can map your entities to your tables in the DbContext in the OnModelCreating method. Personally I prefer to create a mapping class per entity and reference it within this method. Example:
EDIT - Updated Code. This is fully functional code.
public sealed class MyDb : DbContext
{
static MyDb()
{
System.Data.Entity.Database.SetInitializer<MyDb>(new DropCreateDatabaseAlways<MyDb>());
}
public MyDb(string nameOrConnectionString) : base(nameOrConnectionString)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new MyMapper());
base.OnModelCreating(modelBuilder);
}
public DbSet<TesTme> Items { get; set; }
}
public sealed class MyMapper : EntityTypeConfiguration<TesTme>
{
public MyMapper()
{
// other mapping info
this.ToTable("TestTable");
HasKey(x => x.MyId);
this.Property(x => x.MyId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(x => x.dt).HasColumnType("date");
}
}
public sealed class TesTme
{
public int MyId { get; set; }
public DateTime dt { get; set; }
}
public sealed class SomeCallerOfTheDbContext
{
public void TestMe()
{
// this is the entry point
using (var context = new MyDb("connectionStringName"))
{
var items = context.Items.ToList();
}
}
}