-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ToView for entity types with owned types throws NRE #18298
Comments
@TAGC A keyless entity type cannot own another entity type because this implies that the owned type borrows the key of the owner...which doesn't have one. |
We should throw a better exception until #14497 is implemented. |
The thing is that I've got a custom DbContext that's similar to this and it works fine for actually performing queries against the database. It's just the actual generation of the migration (to make it easy to recreate the database views if needed) that's problematic. I've encountered exceptions about keyless entity types not being able to own other entity types in the past, but those were very clear exceptions, and they occurred when trying to perform the actual queries, not generating migrations. This is different. I could try playing around with that SSCCE to see if I can run queries without it throwing exceptions. As an aside, in my production app I've also realised that I've modelled the domain logic incorrectly - specifically, that my assumption that there's a one-to-one mapping between two entities was wrong. This means that I no longer model any entity as owning another entity. I've just tried generating a migration using the EF Core command-line tool and it succeeds, but the migration logic is empty: using Microsoft.EntityFrameworkCore.Migrations;
namespace MyCompany.MyProduct.Infrastructure.Migrations
{
public partial class InitialCreate : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
}
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
} I assume that this is expected behaviour since I guess EF Core won't know what exact SQL is necessary to generate the database views. If so, that's not an issue and I'll be able to manually write that logic. |
Just a quick update on what I've found through playing around with this toy project. I've found that explicitly configuring the primary key for the entities allows migrations to be generated. I've updated the gist to reflect this approach, but this is the relevant part: private static void MapToViewWithKeys(EntityTypeBuilder<Order> builder)
{
builder.ToView("vw_OrderStatus_Orders");
builder.HasKey(o => o.OrderId);
builder.OwnsOne(o => o.Vehicle, vb => vb.HasKey(v => v.Id));
} However, the migration that gets generated attempts to create a table rather than a view. I'm not sure if this is intended behaviour: public partial class InitialCreate : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "vw_OrderStatus_Orders1",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
OrderId = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_vw_OrderStatus_Orders1", x => x.Id);
table.ForeignKey(
name: "FK_vw_OrderStatus_Orders1_vw_OrderStatus_Orders_OrderId",
column: x => x.OrderId,
principalTable: "vw_OrderStatus_Orders",
principalColumn: "OrderId",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_vw_OrderStatus_Orders1_OrderId",
table: "vw_OrderStatus_Orders1",
column: "OrderId",
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "vw_OrderStatus_Orders1");
}
} I've also noticed that the publicly exposed DbSets influences the migration that gets generated. For example, uncommenting the public partial class InitialCreate : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Vehicles",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
OrderId = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Vehicles", x => x.Id);
table.ForeignKey(
name: "FK_Vehicles_vw_OrderStatus_Orders_OrderId",
column: x => x.OrderId,
principalTable: "vw_OrderStatus_Orders",
principalColumn: "OrderId",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Vehicles_OrderId",
table: "Vehicles",
column: "OrderId",
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Vehicles");
}
} |
@AndriySvyryd @bricelam - I tested this and there are weird issues.
public class Blog
{
public int Count { get; set; }
public Address Address { get; set; }
}
public class Address
{
public string City { get; set; }
}
public DbSet<Blog> Blogs { get; set; }
// OnModelCreating
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Configure model
modelBuilder.Entity<Blog>().ToView("A");
modelBuilder.Entity<Blog>().HasKey(e => e.Count);
modelBuilder.Entity<Blog>().OwnsOne(e => e.Address, ow => ow.HasKey(e => e.City));
} Migration. public partial class Init : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "A1",
columns: table => new
{
City = table.Column<string>(nullable: false),
BlogCount = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_A1", x => x.City);
table.ForeignKey(
name: "FK_A1_A_BlogCount",
column: x => x.BlogCount,
principalTable: "A",
principalColumn: "Count",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_A1_BlogCount",
table: "A1",
column: "BlogCount",
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "A1");
}
} Model snapshot. Empty. [DbContext(typeof(MyContext))]
partial class MyContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "3.1.0-preview2.19501.6")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
#pragma warning restore 612, 618
}
} |
Filed #18458 for the migrations part of this. |
Given a domain consisting of two entities
X
andY
whereX
owns an instance ofY
and both are mapped to a view, any attempt made to create an initial database migration usingAdd-Migration
ordotnet ef migration add
fails with aNullReferenceException
.Steps to reproduce
Experiment
).Microsoft.EntityFrameworkCore.Tools
inExperiment
.Experiment.Lib
).Microsoft.EntityFrameworkCore.SqlServer
inExperiment.Lib
.Experiment.Lib
.Experiment.Lib
fromExperiment
.Experiment.Lib
, then runAdd-Migration InitialCreate
.Actual Result
The following exception is generated:
Comment/uncomment the other lines in
BranchContext:OnModelCreating()
to confirm that this does not occur with some minor changes to the entity configuration logic.Expected Result
A migration should be generated to create a view on the database that will contain columns that correspond to the properties of
Order
andVehicle
.Further technical details
EF Core version: EF Core 3.0
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET Core 3
Operating system: Windows 10 64-bit
IDE: Visual Studio 2019 16.3
The text was updated successfully, but these errors were encountered: