I have a struct that wraps an int as such:
public struct LongId
{
public long Value { get; set; }
}
// Model
public class TestEntity
{
public LongId Id { get; set; }
public string Name { get; set; }
}
Then in my context's OnCreating method:
protected override void OnModelCreating(ModelBuilder mb)
{
mb.Entity<TestEntity>()
.Property(o => o.Id)
.HasConversion(o => o.Value, l => new LongId{Value = l})
.UseHiLo();
}
However, when trying to run migrations, I get the following error:
SQL Server sequences cannot be used to generate values for the property 'Id' on entity type 'TestEntity' because the property type is 'LongId'. Sequences can only be used with integer properties.
How can I make this work?
Related
I have a model class:
public class something
{
public int id { get; set; }
public string name { get; set; }
public string desc { get; set; }
}
And this is my ContextModelSnapshot:
partial class ContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
modelBuilder.Entity("My.Data.Models.Something", b =>
{
b.Property<int>("id")
.HasColumnType("int");
b.Property<string>("name")
.HasColumnType("nvarchar(max)");
b.Property<string>("desc")
.HasColumnType("nvarchar(max)");
b.Property<int>("id1")
.HasColumnType("int");
b.ToTable("something", t => t.ExcludeFromMigrations());
});
Note: from the ContextModelSnapshot.cs there is an extra property called id1, this is NOT in my model and it is causing errors when I attempt to use my model. I would like to get rid of this, but I do not know how.
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 am trying to map an existing database in EF with code first. The provider (jetEntityFrameworkProvider) does not support DB first.
I am trying to map the Table "Component" (1) to the Table "ComponentText" (Many)
This is what I have
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Component>().Map(m =>
{
m.Properties(p => new { p.ComponentText });
m.ToTable("ComponentText");
});
modelBuilder.Entity<ComponentText>().HasKey(t => t.ComponentCounter);
}
When I run it I get the follow error
System.InvalidOperationException: 'The property 'ComponentText' on type 'Component' cannot be mapped because it has been explicitly excluded from the model or it is of a type not supported by the DbModelBuilderVersion being used.'
These are my models with only the relevant properties
Component
[Table("Component")]
public class Component
{
[Key]
[Column("Counter")]
public int Id { get; set; }
[Column("Name")]
public virtual ICollection<ComponentText> ComponentText { get; set; }
}
ComponentText
[Table("ComponentText")]
public class ComponentText
{
[Key]
[Column("Counter")]
public int Id { get; set; }
public int TextId { get; set; }
public string Text { get; set; }
//** Foreign Key
public int ComponentCounter { get; set; }
}
ETA:
I've changed my code per Backs answer. However, it is still not working. I have tried several variations. .HasRequired(), .HasOptional().
Note I removed m.ToTable("ComponentText"); As Component is already mapped in the class to the "Component" Table.
I am getting 0 results and receiving this error in Results View
Error = The function evaluation requires all threads to run.
If I uncomment the section line in the comment modelBuilder.Entity<ComponentText>().HasKey(t => t.ComponentCounter);
I get this error
Component_ComponentText_Target: : Multiplicity is not valid in Role 'Component_ComponentText_Target' in relationship 'Component_ComponentText'. Because the Dependent Role refers to the key properties, the upper bound of the multiplicity of the Dependent Role must be '1'.
public class ProjectContext : DbContext
{
private DbConnection con = new JetConnection();
public ProjectContext() : base(new JetConnection(#"Provider=Microsoft.Jet.OLEDB.4.0; Data Source = 'C:\Users\Ben-Laptop\Desktop\Test-Project.sep'; User Id = Admin; Jet OLEDB:Database Password = SEEME;"), true)
{
Database.SetInitializer<ProjectContext>(null);
}
public DbSet<Component> Components { get; set; }
public DbSet<Content> Contents { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Component>().HasMany(p => p.ComponentText).WithOptional().HasForeignKey(p => p.ComponentCounter);
//modelBuilder.Entity<ComponentText>().HasKey(t => t.ComponentCounter);
}
}
Remove m.Properties(p => new { p.ComponentText }); because it only maps property ComponentText
Add mapping for collection
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Component>().Map(m =>
{
m.ToTable("ComponentText");
})
.HasMany(p => p.ComponentText)
.WithRequired()
.HasForeignKey(p => p.ComponentCounter);
}
Entity Framework Fluent API - Relationships
I am relatively new to the Code First approach to Entity Framework. I have used the Database First approach for a while now, but the Code First seems to be a better fit for the application I am currently developing. I am working with an existing MS SQL database, and I am not allowed to make any changes whatsoever to the database. The reason why I am using Code First is because the Fluent API allows me to dynamically assign a table name to a class.
That said, I have a predicament where I need to assign a relationship between 2 tables. One table, ArCodes, has a composite key made up of the CodeType and the Code (both are strings). The CodeType column determins the type of code and the Code column is the identifier unique to the code type.
public class ArCode {
[Column("cod_typ", Order = 0), Key]
public string CodeType { get; set; }
[Column("ar_cod", Order = 1), Key]
public string Code { get; set; }
[Column("desc")]
public string Description { get; set; }
}
The other table, Invoices, needs to have a relationship to the ArCodes table for both a "ship via" code and a "terms" code.
public class Invoice {
[Column("pi_hist_hdr_invc_no"), Key]
public int InvoiceNumber { get; set; }
[Column("shp_via_cod")]
public string ShipViaCode { get; set; }
public ArCode ShipVia { get; set; }
[Column("terms_cod")]
public string TermsCode { get; set; }
public ArCode Terms { get; set; }
}
I would like to setup the relationship for both the "ShipVia" property and the "Terms" property. However, I am not sure how to do so in regards to the CodeType portion of the composite key. For "ship via" codes the Code Type should be "S", and code "terms" codes, the code type should be "T".
I have tried the following in by DB Context, but it did not work:
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
// setup the table names
modelBuilder.Entity<ArCode>().ToTable("ARCODS" + CompanyCode);
modelBuilder.Entity<Invoice>().ToTable("IHSHDR" + CompanyCode);
//
// setup the relationships
//
// 1 Invoice <--> 0-1 Ship Via AR Codes
modelBuilder.Entity<Invoice>()
.HasOptional(invoice => invoice.ShipVia)
.WithMany()
.HasForeignKey(invoice => new { TheType = "S", invoice.ShipViaCode })
;
base.OnModelCreating(modelBuilder);
}
Any help would be appreciated.
Update #1
Ok, I reduced my code to its simplest form, and I followed the solution as provided by #GertArnold.
public abstract class ArCode {
[Column("cod_typ")]
public string CodeType { get; set; }
[Column("ar_cod")]
public string Code { get; set; }
[Column("terms_desc")]
public string TermsDescription { get; set; }
[Column("terms_typ")]
public string TermsType { get; set; }
[Column("shp_via_desc")]
public string ShipViaDescription { get; set; }
[Column("tax_desc")]
public string TaxDescription { get; set; }
}
public class TermsCode : ArCode { }
public class ShipViaCode : ArCode { }
public class Invoice {
[Column("pi_hist_hdr_invc_no"), Key]
public int InvoiceNumber { get; set; }
[Column("hdr_invc_dat")]
public DateTime InvoiceDate { get; set; }
[Column("shp_via_cod")]
public string ShipViaCode { get; set; }
public ShipViaCode ShipVia { get; set; }
[Column("terms_cod")]
public string TermsCode { get; set; }
public TermsCode Terms { get; set; }
public Invoice() {
}
}
public class PbsContext : DbContext {
public DbSet<Invoice> Invoices { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Entity<Invoice>().ToTable("IHSHDR");
modelBuilder.Entity<ArCode>().HasKey(r => r.Code).ToTable("ARCODS");
modelBuilder.Entity<TermsCode>().Map(m => m.Requires("CodeType")
.HasValue("T").HasColumnType("varchar").HasMaxLength(1).IsRequired())
.ToTable("ARCODS");
modelBuilder.Entity<ShipViaCode>().Map(m => m.Requires("CodeType")
.HasValue("S").HasColumnType("varchar").HasMaxLength(1).IsRequired())
.ToTable("ARCODS");
base.OnModelCreating(modelBuilder);
}
public PbsContext()
: base("name=PbsDatabase") {
}
}
However, the following code returns an error:
PbsContext context = new PbsContext();
var invoice = context.Invoices.OrderByDescending(r => r.InvoiceDate).FirstOrDefault();
error 3032: Problem in mapping fragments starting at line 28:Condition member 'ArCode.cod_typ' with a condition other than 'IsNull=False' is mapped. Either remove the condition on ArCode.cod_typ or remove it from the mapping.
If I remove the "CodeType" column from the ArCode class and change all "CodeType" references to the database column name of "cod_typ" within the OnModelCreating event, then the statement above executes without error. However, invoice.ShipVia and invoice.Terms will both be null event though there is a matching record in the database.
Update #2
public abstract class ArCode {
[Column("ar_cod")]
public string Code { get; set; }
[Column("terms_desc")]
public string TermsDescription { get; set; }
[Column("terms_typ")]
public string TermsType { get; set; }
[Column("shp_via_desc")]
public string ShipViaDescription { get; set; }
[Column("tax_desc")]
public string TaxDescription { get; set; }
}
public class TermsCode : ArCode { }
public class ShipViaCode : ArCode { }
public class Invoice {
[Column("pi_hist_hdr_invc_no"), Key]
public int InvoiceNumber { get; set; }
[Column("hdr_invc_dat")]
public DateTime InvoiceDate { get; set; }
[Column("shp_via_cod")]
public ShipViaCode ShipVia { get; set; }
[Column("terms_cod")]
public TermsCode Terms { get; set; }
public Invoice() {
}
}
public class PbsContext : DbContext {
public DbSet<Invoice> Invoices { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Entity<Invoice>().ToTable("IHSHDR");
modelBuilder.Entity<ArCode>().HasKey(r => r.Code).ToTable("ARCODS");
modelBuilder.Entity<TermsCode>().Map(m => m.Requires("CodeType")
.HasValue("T").HasColumnType("varchar").HasMaxLength(1).IsRequired())
.ToTable("ARCODS");
modelBuilder.Entity<ShipViaCode>().Map(m => m.Requires("CodeType")
.HasValue("S").HasColumnType("varchar").HasMaxLength(1).IsRequired())
.ToTable("ARCODS");
base.OnModelCreating(modelBuilder);
}
public PbsContext()
: base("name=PbsDatabase") {
}
}
Now, the following code returns an error:
PbsContext context = new PbsContext();
var invoice = context.Invoices.OrderByDescending(r => r.InvoiceDate).FirstOrDefault();
EntityCommandExecutionException - Invalid column name 'ShipVia_Code'. Invalid column name 'Terms_Code'.
What you want is impossible for EF. ArCode has a composite key, so any association to it will have to use two Properties. That means that in Invoice you'd need four properties (two pairs) to refer to the two ArCode objects. But two of these properties (those for CodeType) are not backed up by columns in the database, so EF can not map them.
But... there is a way that may help you out. You could create two derived classes from ArCode and let Invoice refer to those by single-property associations. But then you have to divert from the model as such and fool EF a bit by defining a single key:
public abstract class ArCode { ... } // abstract!
public class TermsCode : ArCode { }
public class ShipViaCode : ArCode { }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Invoice>().ToTable("IHSHDR");
modelBuilder.Entity<Invoice>().HasOptional(i => i.Terms).WithOptionalDependent().Map(m => m.MapKey("terms_cod"));
modelBuilder.Entity<Invoice>().HasOptional(i => i.ShipVia).WithOptionalDependent().Map(m => m.MapKey("shp_via_cod"));
modelBuilder.Entity<ArCode>().HasKey(a => a.Code).ToTable("ARCODS");
modelBuilder.Entity<TermsCode>().Map(m => m.Requires("CodeType")
.HasValue("T").HasColumnType("varchar").HasMaxLength(1).IsRequired())
.ToTable("ARCODS");
modelBuilder.Entity<ShipViaCode>().Map(m => m.Requires("CodeType")
.HasValue("S").HasColumnType("varchar").HasMaxLength(1).IsRequired())
.ToTable("ARCODS");
base.OnModelCreating(modelBuilder);
}
public class Invoice
{
[Column("pi_hist_hdr_invc_no"), Key]
public int InvoiceNumber { get; set; }
public ShipViaCode ShipVia { get; set; }
public TermsCode Terms { get; set; }
}
This may work for you if you don't have to insert ARCODS records through EF. It won't allow you to insert records with identical Codes, although the database would allow it. But I expect the content of ARCODS to be pretty stable and maybe it is enough to fill it with a script.
I successfully mapped my complex type like this:
modelBuilder
.ComplexType<Name>()
.Property(name => name.First)
.HasColumnName("firstNameColumn");
modelBuilder
.ComplexType<Name>()
.Property(name => name.Last)
.HasColumnName("lastNameColumn");
So far so good. But notice that we do not specify any entity type. What if we want to map the same complext type also for a table with columns "firstN" and "lastN"? I tried EntityTypeConfiguration<> but you are not allowed to specify complex types there. Finally it looks like that complexTypes are defined globally.
You can also customize the complex type columns names at the entity level, like the following:
public class User
{
public int UserId { get; set; }
public Name NameInfo { get; set; }
}
public class Customer
{
public int CustomerId { get; set; }
public Name NameInfo { get; set; }
}
[ComplexType]
public class Name
{
public string First { get; set; }
public string Last { get; set; }
}
public class Context : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Customer> Customers { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.ComplexType<Name>()
.Property(name => name.First)
.HasColumnName("firstNameColumn");
modelBuilder.ComplexType<Name>()
.Property(name => name.Last)
.HasColumnName("lastNameColumn");
// Here is how can customize the column names at the entity level:
modelBuilder.Entity<Customer>().Property(u => u.NameInfo.First)
.HasColumnName("firstN");
modelBuilder.Entity<Customer>().Property(u => u.NameInfo.Last)
.HasColumnName("lastN");
}
}
And the resultant schema will be:
Here you can find another example.