Entity Framework Core does not respect Identity columns - c#

Entity Framework is not respecting my Identity columns. It insists on trying to insert a value into an Identity (auto-increment) column in my MS SQL DB, which is obviously an error since the DB is supposed to supply the value.
System.Data.SqlClient.SqlException: 'Cannot insert explicit value for identity column in table 'Assignee' when IDENTITY_INSERT is set to OFF.'
Why is it trying to do that? I've paired it down to a schema involving one table and one column:
CREATE TABLE [dbo].[Assignee](
[AssigneeID] INT IDENTITY(-1, 1) NOT NULL
CONSTRAINT [Assignee$PrimaryKey] PRIMARY KEY CLUSTERED
( [AssigneeID] ASC ))
After publishing this schema to my local DB I use Scaffold-DbContext to generate entity and context classes. The generated Assignee class contains just this public property.
public int AssigneeId { get; set; }
The context only refers to Assignee here:
modelBuilder.Entity<Assignee>(entity =>
{
entity.Property(e => e.AssigneeId).HasColumnName("AssigneeID");
});
Searching around I see people claiming that for E.F. to respect Identity columns, the context should configure the property with ValueGeneratedOnAdd(). In other words, the line in the context class should read:
entity.Property(e => e.AssigneeId).HasColumnName("AssigneeID")
.ValueGeneratedOnAdd();
I have two problems with this:
I'm starting with an existing DB and generating entity classes. If I need ValueGeneratedOnAdd() then why isn't Scaffold-DbContext generating it?
Even if I manually edit the generated context class and add ValueGeneratedOnAdd() it still doesn't work with the same error.
Elsewhere I see suggestions to use UseSqlServerIdentityColumn(). That also doesn't work for me. Points 1 and 2 still apply.
Any help would be greatly appreciate. Please don't suggest that I use IDENTITY_INSERT as that defeats the entire point of using auto-increment columns.
(I am using Entity Framework Core 2.2.3 and Microsoft SQL Server 14)

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Todo>(entity =>
{
entity.Property(x => x.Id)
.HasColumnName("id")
.HasColumnType("int")
.ValueGeneratedOnAdd()
**.UseIdentityColumn();**
}
Try do this.
Ef Core Dependency : Microsoft.EntityFrameworkCore.SqlServer

This works for me:
modelBuilder.Entity<Assignee>().Property(e => e.AssigneeId).UseIdentityColumn();
So UseIdentityColumn() is the key.
I'm using Microsoft.EntityFrameworkCore.SqlServer v3.1.8.

Short version
We are getting and experiencing different results here one can reproduce the issue, others can not. My experience it depends on if the Id property's value is 0 or not.
Detailed version
My experience, that the default behavior (based on name convention) is definitely working, so in case you are naming your db entity's attribute (C# property) to Id or EntityNameId it should work. No C# entity class attributes neither OnModelCreating config is necessary.
The same time if the issue is there neither No C# entity class attributes neither OnModelCreating config will fix it.
...because if the Id property's value is not 0, the generated SQL will contain the explicit field name and value, so we got the error.
This is clearly and issue in EF core, but workaround is easy..

For DB first try adding [key] as a data annotation
With Data annotation
[Key]
public int AssigneeId { get; set; }
fluent API
modelBuilder.Entity<Assignee>()
.HasKey(o => o.AssigneeId);
See here or here if you want to use fluent API

I've tried to reproduce this issue based on your example but it appears to work just fine. I did not use Scaffold though, just coded class and I tried the model creating code you had and it hasn't had an issue. I suspect there has to be more to this though because with just the "Assignee" class, EF convention is expecting an "Assignees" table, so I suspect there is more mapping being set up.
Tested with EF Core 2.0.3 and 2.2.4
DB: used the OP's script.
Entity:
[Table("Assignee")]
public class Assignee
{
public int AssigneeId { get; set; }
}
I had to use the Table attribute to map to the table name.
Context:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Assignee>(entity =>
{
entity.Property(e => e.AssigneeId).HasColumnName("AssigneeID");
});
}
as-per OP comment.
Test:
[Test]
public void TestIncrement()
{
using (var context = new TestDbContext())
{
var newItem = new Assignee();
context.Assignees.Add(newItem);
context.SaveChanges();
}
}
Works as expected.
However, what I'd normally have for the entity:
[Table("Assignee")]
public class Assignee
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity), Column("AssigneeID")]
public int AssigneeId { get; set; }
}
And then nothing for this column needed in the context OnModelCreating override.
I suspect that there is some additional configuration lurking somewhere given there is no mention of the table name issue, either manually added or via scaffold that is goofing up EF. I was full-on expecting EF to fail without the Key/DbGenerated attributes, but it seemed to work just fine.
Edit: Also tried this with scafolding running Scaffold-DbContext across the existing schema. Again, worked without an issue.
For comparison against your tests:
Generated DbContext: (Unaltered save removing the warning and connection string details.)
public partial class AssigneeContext : DbContext
{
public AssigneeContext()
{
}
public AssigneeContext(DbContextOptions<AssigneeContext> options)
: base(options)
{
}
public virtual DbSet<Assignee> Assignee { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer("Data Source=machine\\DEV;Initial Catalog=Spikes;uid=user;pwd=password;MultipleActiveResultSets=True");
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasAnnotation("ProductVersion", "2.2.4-servicing-10062");
modelBuilder.Entity<Assignee>(entity =>
{
entity.Property(e => e.AssigneeId).HasColumnName("AssigneeID");
});
}
}
Generated Entity: (Unaltered)
public partial class Assignee
{
public int AssigneeId { get; set; }
}
I did figure out why my table annotation was needed. EF Core (Not sure if applies to EF6 as well) was basing the convention for the table name on the DbSet variable name in the DbContext. I couldn't see any config difference with the scaffold generated context and my own, except the DbSet name. I renamed my original DbContext's DbSet name to "Assignee" and it worked without the Table attribute.
That said, based on the information present your code should work. Something is lurking in the details because this example does work so you will need to provide more detail about an example that definitely doesn't work in your case.

Related

C# Pomelo mySQL trigger onUpdate Timestamp

I'm trying to create a log table in Pomelo.MySQL which has an onUpdate Timestamp, but I can't seem to trigger it with Entity Framework.
This is my model for the table
public class OrganisationLog
{
[Key]
public int Id { get; set; }
[Column(TypeName = "VARCHAR(1024)")]
[Required(AllowEmptyStrings = false)]
public string MachineName { get; set; }
[DefaultValue("CURRENT_TIMESTAMP")]
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime LastContact { get; set; }
public int OrganisationId { get; set; }
[ForeignKey("OrganisationId")]
public Organisation Organisation { get; set; }
}
And below is the function that should work.
private void UpdateOrganisationLog(Organisation organisation, string machineName)
{
try
{
OrganisationLog organisationLog = _context.OrganisationLogs
.Where(x => x.OrganisationId == organisation.Id && x.MachineName == machineName)
.FirstOrDefault();
if (organisationLog == null)
{
organisationLog = new OrganisationLog()
{
MachineName = machineName,
OrganisationId = organisation.Id,
LastContact = DateTime.Now
};
_context.OrganisationLogs.Add(organisationLog);
}
else
{
_context.Update(organisationLog);
}
_context.SaveChanges();
}
catch (Exception e)
{
Console.WriteLine("Error " + e.Message);
}
}
I ended up making it work with a manual SQL statement, but I want to figure it out through Entity Framework.
_context.Database.ExecuteSqlCommand($"UPDATE organisationlogs SET LastContact = CURRENT_TIMESTAMP(6) WHERE Id = {organisationLog.Id}");
Could it have something to do with CURRENT_TIMESTAMP(6) rather than CURRENT_TIMESTAMP()? Not sure why Entity Framework has made it as (6).
According to the EF Core docs on Default Values, data annotations are not supported:
You can not set a default value using Data Annotations.
If it would have been supported by EF Core, than using it for CURRENT_TIMESTAMP would probably still not have worked, because it is not a System.DateTime value, but technically a SQL fragment.
In your case, a FluentAPI configuration like the following, that uses .HasDefaultValueSql() to specify the SQL fragment, should work for Pomelo 3.0.1+:
class MyContext : DbContext
{
// ...
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<OrganisationLog>(entity =>
{
entity.Property(e => e.LastContact)
.HasDefaultValueSql("CURRENT_TIMESTAMP");
});
}
}
The DatabaseGenerated(DatabaseGeneratedOption.Computed) attribute should not be necessary.
If you want to have the value not just generated on creation, but also updated automatically when changing the table row, use the following model definition instead:
class MyContext : DbContext
{
// ...
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<OrganisationLog>(entity =>
{
entity.Property(e => e.LastContact)
.ValueGeneratedOnAddOrUpdate();
});
}
}
In case you only want the value to be updated when changing the table row, but not when creating it, you can use ValueGeneratedOnUpdate() with EF Core 3.1.0.
There is a bug in EF Core < 3.1.0, where ValueGeneratedOnUpdate() will not generate correct C# code. This should not be an issue for most people, because lifetime support for EF Core 3.0.0 is very limited anyway (and as mentioned above, the feature is only supported by Pomelo since 3.0.1). If you need a workaround for 3.0.1 >= Pomelo < 3.1.0 anyway, then using ValueGeneratedOnAddOrUpdate() instead will work for most use cases.
See #959 on our GitHub repo for the fix that implemented support for datetime columns in conjunction with CURRENT_TIMESTAMP and for further details.
I'm using Pomelo.EntityFramework.MySql version 2.1.4. I'm hosting this with Elastic Beanstalk so I need to use an older version of dotnet
Everything above is not going to work correctly for Pomelo 2.1.4 (using a timestamp or timestamp(6) column might work, but you would need to manually change the DEFAULT statement to remove the single quotes, in case you scaffold the database). But you can always just change the table definition as a workaround.
If you are using migrations, the following line (or something similar) can be added to an Up() method for example:
migrationBuilder.Sql("ALTER TABLE `OrganisationLog` CHANGE COLUMN `LastContact` datetime(6) CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;");
Not ideal, but it should do its job for older Pomelo versions.

Rebuilding Sql Server indexes name invalidate the hardcoded value in EF

In EF Context file,i have hard coded key name/index name in OnModelCreating. DBA rebuilt those indexes/keys with different names. Do i have to update those reference in code again ? or is there any other approach ?
protected override void OnModelCreating(ModelBuilder modelBuilder){
modelBuilder.Entity<Customert>(entity =>
{
entity.HasKey(e => e.custId)
.HasName("PK__cust__4E739DAA");
}
}
As an option you can use Key attribute directly on entity property, for instance
using System.ComponentModel.DataAnnotations;
public class Customer
{
[Key]
public int CustomerId {get;set;}
}
documentation

How to add the same column to all entities in EF Core?

Imagine that I want to add an IsDeleted colum or some auditing columns to all of my entities. I could create a base class from which all of my entities will inherit and this will solve my problem, however I cannot specify the order in which the column will be created so I will end up with all the auditing fields before the fields of my entity, which I do not want. I want them to be at the end of the table.
In the standard version of entity framework we can do this by using annotations that specify the order of the columns. However, such a thing does not exist for EF core at the moment.
I could do it with the fluent api on the OnModelCreating() method, the problem is that I only know how to do it individually for each of my entities, which means I would have to write the same code for every entity I have.
Is there any way I can do it generically for all of my entities? Some sort of for loop that iterates through all the entities registered in the DbSets on my dbcontext?
Your question title is about adding the same properties to multiple entities. However, you actually know how to achieve this (use a base type) and your actual question is how to ensure that these properties come last in the generated tables' columns.
Although column order shouldn't really matter nowadays, I'll show an alternative that you may like better than a base type and also positions the common properties at the end of the table. It makes use of shadow properties:
Shadow properties are properties that are not defined in your .NET entity class but are defined for that entity type in the EF Core model.
Most of the times, auditing properties don't need much visibility in the application, so I think shadow properties is exactly what you need. Here's an example:
I have two classes:
public class Planet
{
public Planet()
{
Moons = new HashSet<Moon>();
}
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Moon> Moons { get; set; }
}
public class Moon
{
public int ID { get; set; }
public int PlanetID { get; set; }
public string Name { get; set; }
public Planet Planet { get; set; }
}
As you see: they don't have auditing properties, they're nicely mean and lean POCOs. (By the way, for convenience I lump IsDeleted together with "audit properties", although it isn't one and it may require another approach).
And maybe that's the main message here: the class model isn't bothered with auditing concerns (single responsibility), it's all EF's business.
The audit properties are added as shadow properties. Since we want to do that for each entity we define a base IEntityTypeConfiguration:
public abstract class BaseEntityTypeConfiguration<T> : IEntityTypeConfiguration<T>
where T : class
{
public virtual void Configure(EntityTypeBuilder<T> builder)
{
builder.Property<bool>("IsDeleted")
.IsRequired()
.HasDefaultValue(false);
builder.Property<DateTime>("InsertDateTime")
.IsRequired()
.HasDefaultValueSql("SYSDATETIME()")
.ValueGeneratedOnAdd();
builder.Property<DateTime>("UpdateDateTime")
.IsRequired()
.HasDefaultValueSql("SYSDATETIME()")
.ValueGeneratedOnAdd();
}
}
The concrete configurations are derived from this base class:
public class PlanetConfig : BaseEntityTypeConfiguration<Planet>
{
public override void Configure(EntityTypeBuilder<Planet> builder)
{
builder.Property(p => p.ID).ValueGeneratedOnAdd();
// Follows the default convention but added to make a difference :)
builder.HasMany(p => p.Moons)
.WithOne(m => m.Planet)
.IsRequired()
.HasForeignKey(m => m.PlanetID);
base.Configure(builder);
}
}
public class MoonConfig : BaseEntityTypeConfiguration<Moon>
{
public override void Configure(EntityTypeBuilder<Moon> builder)
{
builder.Property(p => p.ID).ValueGeneratedOnAdd();
base.Configure(builder);
}
}
These should be added to the context's model in OnModelCreating:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new PlanetConfig());
modelBuilder.ApplyConfiguration(new MoonConfig());
}
This will generate database tables having columns InsertDateTime, IsDeleted and UpdateDateTime at the end (independent of when base.Configure(builder) is called, BTW), albeit in that order (alphabetical). I guess that's close enough.
To make the picture complete, here's how to set the values fully automatically in a SaveChanges override:
public override int SaveChanges()
{
foreach(var entry in this.ChangeTracker.Entries()
.Where(e => e.Properties.Any(p => p.Metadata.Name == "UpdateDateTime")
&& e.State != Microsoft.EntityFrameworkCore.EntityState.Added))
{
entry.Property("UpdateDateTime").CurrentValue = DateTime.Now;
}
return base.SaveChanges();
}
Small detail: I make sure that when an entity is inserted the database defaults set both fields (see above: ValueGeneratedOnAdd(), and hence the exclusion of added entities) so there won't be confusing differences caused by client clocks being slightly off. I assume that updating will always be well later.
And to set IsDeleted you could add this method to the context:
public void MarkForDelete<T>(T entity)
where T : class
{
var entry = this.Entry(entity);
// TODO: check entry.State
if(entry.Properties.Any(p => p.Metadata.Name == "IsDeleted"))
{
entry.Property("IsDeleted").CurrentValue = true;
}
else
{
entry.State = Microsoft.EntityFrameworkCore.EntityState.Deleted;
}
}
...or turn to one of the proposed mechanisms out there to convert EntityState.Deleted to IsDeleted = true.
You can always generate an initial migration for the model and manually rearrange the column order in the Migration.
Here is the open issue tracking support for explicit column ordering in EF Core: https://github.com/aspnet/EntityFrameworkCore/issues/10059
Also see this question and answer on using Shadow Properties and Query Filters for soft deletes. EF Core: Soft delete with shadow properties and query filters

Entity Framework auto generate GUID

I am new to EF so here goes.I have a class which contains the following
public class EmailTemplate
{
public Guid Id { get; set; }
[MaxLength(2000)]
public string Html { get; set; }
}
Here is my mapping class
class EmailMapper : EntityTypeConfiguration<EmailTemplate>
{
public EmailMapper()
{
ToTable("EmailTemplate");
HasKey(c => c.Id);
Property(c => c.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(c => c.Id).IsRequired();
}
}
I am trying to call DbContext.SaveChanges(), but I get the following error :
Exception Details: System.Data.SqlClient.SqlException: Cannot insert the value NULL into column 'Id', table 'AutoSendConnection.dbo.EmailTemplates'; column does not allow nulls. INSERT fails.
What am i doing wrong? Why won't EF auto create a unique GUID?
Just decorate the Id field on your EmailTemplate class as below and SQL Server will automatically generate the value on insert.
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid Id { get; set; }
You can also remove your Mapper class as it's no longer needed.
If using .Net core then this should work for you ...
Use fluent API
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Node>().Property(x => x.ID).HasDefaultValueSql("NEWID()");
}
or
modelBuilder.Entity<Student>().Property(p => p.StudentID)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Here is a more comprehensive Cheat Sheet for entity framework
Addressing other answers here
None of these other options seem to work and I've questioned this time and time again with the EF team over on github ...
https://github.com/aspnet/EntityFramework6/issues/762
... for some reason the EF dev team seem to think that this is "working by design" and repeatedly close tickets questioning this "bug".
The EF team explanation
For some reason they seem to think that "generating Guids in SQL is considered not best practice and that to ensure the keys are available immediately we should be generating the keys in the app code".
The issue here of course is that highly populated tables run the risk of you taking further business actions consuming an invalid key.
In my case this could break some extremely complex multi server DTC transactions so I don't believe the advice from MS to be correct, that said EF Core doesn't currently support distributed transactions at all so in a focused context they may have a point.
My answer (which actually works)
In short, I solved this by "manually hacking" the generated migration after generating it ...
EF code first migrations, DB generated guid keys
To quote the other question the answer is as follows:
Generate the migration script as you normally would putting both attributes on the key property like this ...
public class Foo
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
}
... declaratively speaking the entity is now correct.
The migration it will generate will look something like:
CreateTable(
"dbo.Foos",
c => new
{
Id = c.Guid(nullable: false),
...
})
.PrimaryKey(t => t.Id)
...;
... I have not been able to pin down why, but in some situations this will work and in others it won't (run the migration, perform an insert to test).
If it fails, roll the migration back then modify it to read something like ...
CreateTable(
"dbo.Foos",
c => new
{
Id = c.Guid(nullable: false, defaultValueSql: "newid()"),
...
})
.PrimaryKey(t => t.Id)
...;
... the extra code here tells SQL to generate the key as we would expect.
As a rule of thumb I would apply this change all the time for consistency reasons and it means that at a glance your migrations will show you exactly what keys are db generated and of course which ones don't.
After a long investigation, I found out that in EF Core 3.1 you need to use
builder.Property(e => e.Id).ValueGeneratedOnAdd();
Set the default sql value of the field to 'newsequentialid()' in the mapping configuration.
You can also set Default Value of ID as NewID() in Sql Server itself and pass the GUID as null
I used to do it in SSMS.
I prefer to leave the database to generate the id automatically for example the following schema:
CREATE TABLE [dbo].[MyTable](
[MyId] [uniqueidentifier] NOT NULL CONSTRAINT [DF_Booking_BookingId] DEFAULT (newsequentialid())
)
Then in the code first mapping I specify the following to tell Entity Framework that the database will take care of generating the value on insert.
Property(a => a.MyId).IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
I encountered this issue in .NET 6. Some background is that we use int for primary keys but a new requirement ment that parts of the system needed to be in sync with other systems and we could no longer depend on our int keys. We decided to use a hybrid approach with auto-increment primary key integer id column and a GUID column as described here:
https://dba.stackexchange.com/a/96990/80960
Basically the model looked like this:
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Guid { get; set; }
Migration looked like this:
migrationBuilder.AddColumn<Guid>(
name: "Guid",
table: "Products",
type: "uniqueidentifier",
nullable: false,
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"));
ApplicationDbContextModelSnapshot.cs:
b.Property<Guid>("Guid")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
I thought this meant that every old value would get 00000000-0000-0000-0000-000000000000 and new values would receive ValueGeneratedOnAdd. However when I tested to add a new value I still got value 00000000-0000-0000-0000-000000000000. See image below:
I then tried to use defaultValue: Guid.NewGuid()); instead of defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); in migration.
This meant that every old and every new value I added got the same Guid but not 00000000-0000-0000-0000-000000000000.
I then switched over to manually add defaultValueSql: "NEWID()" instead of defaultValue: new Guid("00000000-0000-0000-0000-000000000000") in migration.
Now everything started working as expected both with old values and new values:
Now I wanted this functionality to be default for every new Guid added. I therefore modified ApplicationDbContext.cs method protected override void OnModelCreating(ModelBuilder modelBuilder) like this:
foreach (var property in modelBuilder.Model.GetEntityTypes()
.SelectMany(t => t.GetProperties())
.Where(p => p.ClrType == typeof(Guid)))
{
property.SetDefaultValueSql("NEWID()");
}
Model ended up looking like this since [DatabaseGenerated(DatabaseGeneratedOption.Identity)] did not work in this case:
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public Guid Guid { get; set; }
Entity Framework Core Update:
There is no need to use [DatabaseGenerated(DatabaseGeneratedOption.Identity)].
There is no need to use fluent API
EF Core automatically take care of it and generates Id for primary key
Example:
public class DummyEntity
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Seeding
_context.DummyEntities.Add(new DummyEntity
{
FirstName = "Abc",
LastName = "Def",
Postion = "User",
});
_context.SaveChanges();

How can you add a navigation property on a view in EF 6.1 CodeFirst

Let's make a case to explain my problem.
MyTable1
+id
+myTable2Id
MyTable2
+id
MyView1
+id
+myTable2Id
MyView1 exists in the case, from data from the MyTable1. Now i want to create a Navigation property from my EF6.1 Code first approach in my View to MyTable2.
I know that it was possible from the database first approach, but is it also possible from the code-first approach and how?
EDIT:
I search some on internet, but due many meanings of the word View, it's very hard to find information on it.
Also with the approaches in codes that i tried, i always get an error that the migration can't be completed. Because the Migration tries to add an foreign key to the view, which isn't possible.
EDIT2:
To elaborate a bit more on my explanation. I want to be able to approach it in code the following way:
Guid table2Id = context.MyView1.FirstOrDefault().MyTable2.id;
EDIT3:
I will eleborate a bit more, to see if i can get my problem better explained.
When i added the following to my view Entity:
public virtual MyTable2 Table2 { get; set;}
EF will automaticly generate the following migration:
public override void Up() {
CreateIndex("MyView1", "MyTable2Id");
AddForeignKey("MyView1", "MyTable2Id", "MyTable2", "id")
}
Which on running update-database gives the following error :
"Cannot create index on view 'MyView1' because the view is not schema bound"
EDIT4:
With help of the comment that the migration aren't of stone.. and are changeable i made it.
I used the following fluentAPI:
// Map one-to-zero or one relationship
modelBuilder.Entity<MyTable2>()
.HasRequired(t => t.MyTable1)
.WithOptional(t => t.MyTable2);
modelBuilder.Entity<MyTable1>()
.HasOptional(t => t.MyTable2);
And changing my tables to this: (The FK to the MyTable2 and removed from the view)
MyTable1
+id
MyTable2
+id
+myTable1
MyView1
+id
Which in the end is better because this way i have less Null values in my model.
In EF you can use a database views and map it to an entity and reference it just as you do with tables.
For code first process you have to create the View in Up and drop it in Down methods from migration class:
public partial class AddView : DbMigration
{
public override void Up()
{
this.Sql(#"CREATE VIEW MyView1 AS ....");
}
public override void Down()
{
this.Sql(#"DROP VIEW MyView1....");
}
}
EDIT:
public long myTable2Id { get; set; }
[ForeignKey( "myTable2Id" )]
public virtual MyTable2 Table2 {get;set;}

Categories