天道酬勤,学无止境

Nullable Owned types in EF Core

I my case I want to store an address but it has to be optional.

My mapping lookes like this:

map.OwnsOne(x => x.Address, cb => cb.OwnsOne(l => l.Location));

But when comitting my DbContext with Address as null iam getting this error:

InvalidOperationException: The entity of 'Member' is sharing the table 'Members' with 'Member.Address#StreetAddress', but there is no entity of this type with the same key value 'Id:-2147480644' that has been marked as 'Added'.

I then instantiated the Address and Location from the constructors, and now I can save the entity. But when fetching the data again I also gets an instantiated Address, where i really wanted a null value.

Is it not possible to make nullable Owned Types ?

评论

Is it not possible to make nullable Owned Types?

As of EF Core 3, this is now possible 🎉.

all dependents are now optional. (Shipping in preview 4): Source


Sample Code:

static void Main(string[] args)
{
  using (var context = new OwnedEntityContext())
  {
    context.Add(new DetailedOrder
    {
      Status = OrderStatus.Pending,
      OrderDetails = new OrderDetails
      {
        ShippingAddress = new StreetAddress
        {
          City = "London",
          Street = "221 B Baker St"
        }
        //testing 3.0: "Yes, all dependents are now optional"
        //reference: https://github.com/aspnet/EntityFrameworkCore/issues/9005#issuecomment-477741082
        //NULL Owned Type Testing
        //BillingAddress = new StreetAddress
        //{
        //    City = "New York",
        //    Street = "11 Wall Street"
        //}
      }
    });
    context.SaveChanges();
  }
  //read test
  using (var context = new OwnedEntityContext())
  {
    #region DetailedOrderQuery
    var order = context.DetailedOrders.First(o => o.Status == OrderStatus.Pending);
    Console.Write("NULL Owned Type Test, Is Billing Address NULL?");
    //PRINTS FALSE
    Console.WriteLine($"{order.OrderDetails.BillingAddress == null}");
    #endregion
  }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    #region OwnsOneNested
    modelBuilder.Entity<DetailedOrder>().OwnsOne(p => p.OrderDetails, od =>
    {
        od.OwnsOne(c => c.BillingAddress);
        od.OwnsOne(c => c.ShippingAddress);
    });
    #endregion

    #region OwnsOneTable
    modelBuilder.Entity<DetailedOrder>().OwnsOne(p => p.OrderDetails, od =>
    {
        od.OwnsOne(c => c.BillingAddress);
        od.OwnsOne(c => c.ShippingAddress);
        od.ToTable("OrderDetails");
        //Exception message:Microsoft.Data.SqlClient.SqlException:
        //'Cascading foreign key 'FK_OrderDetails_DetailedOrders_OrderId' cannot
        //be created where the referencing column 'OrderDetails.OrderId' is an identity column.
        //Could not create constraint or index. See previous errors.'
        //3.0 bug: https://github.com/aspnet/EntityFrameworkCore/issues/17448#issuecomment-525444101 
        //fixed in 3.1: https://github.com/aspnet/EntityFrameworkCore/pull/17458
        od.Property("OrderId")
            .ValueGeneratedNever();
    });
    #endregion
}

One of the limitations of Owned Types is that no support for optional (i.e. nullable). I recommend you to follow this thread.

In my solution, I use the Empty Object approach and use the IsEmpty method to know if an Address is Empty instead of asking if the address is null. I hope this approach helps you.

public sealed class Address : ValueObject<Address>
{
    public string StreetAddress1 { get; private set; }
    public string StreetAddress2 { get; private set; }
    public string City { get; private set; }
    public string State { get; private set; }
    public string ZipCode { get; private set; }
    public string Country { get; private set; }

    private Address() { }
    public Address(string streetAddress1, string city, string state, string zipcode, string country)
    {
        StreetAddress1 = streetAddress1;
        City = city;
        State = state;
        ZipCode = zipcode;
        Country = country;
    }

    public Address(string streetAddress1, string streetAddress2, string city, string state, string zipcode, string country)
        : this(streetAddress1, city, state, zipcode, country)
    {
        StreetAddress2 = streetAddress2;
    }

    public static Address Empty()
    {
        return new Address("", "", "", "", "");
    }

    public bool IsEmpty()
    {
        if (string.IsNullOrEmpty(StreetAddress1)
         && string.IsNullOrEmpty(City)
         && string.IsNullOrEmpty(State)
         && string.IsNullOrEmpty(ZipCode)
         && string.IsNullOrEmpty(Country))
        {
            return true;
        }
        else
        {
            return false;
        }
    }

}
public class Firm : AggregateRoot<Guid>
{
    public string Name { get; private set; }
    public Address Address { get; private set; }

    private Firm() { }
    public Firm(string name)
    {
        if (String.IsNullOrEmpty(name))
            throw new ArgumentException();

        Id = Guid.NewGuid();
        Name = name;
        Address = Address.Empty();
    }
}

Entity Framework document states that

Reference navigations to owned entity types cannot be null unless they are explicitly mapped to a separate table from the owner

So, in fact, there is a solution to your problem. You need to map your owned entity to a separate table instead of having it inside the same table as the owner.

map.OwnsOne(x => x.Address, cb => cb.OwnsOne(l => l.Location, l=> l.ToTable("Locations")));

By mapping the location entity into a separate table called Locations, the owned entity becomes nullable.

Have a look here: https://msdn.microsoft.com/en-us/magazine/mt846463.aspx and scroll down to "Temporary Work-Around to Allow Null Value Objects".

A shorter example can be found here: https://entityframeworkcore.com/knowledge-base/48063630/nullable-owned-types-in-ef-core

I would be nice to be able to do this without this workaround, but I don't believe that's possible yet.

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。

相关推荐
  • 在创建之前由 foreach 访问的 ShadowProperties(ShadowProperties being reached by foreach before being created)
    问题 我是实体框架的新手,我正在通过 Julie Lerman 的 Pluralsight 课程学习 atm。 我正在观看第二门课程“实体框架核心 2:映射”,但我使用的是 EF Core 2.1。 编辑:所以我决定将所有内容都注释掉并再次遵循课程,它现在正在运行,但是生成的迁移生成了不应该存在的 2 列: protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.AddColumn<DateTime>( name: "BetterName_Created", table: "Samurais", nullable: false, defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); migrationBuilder.AddColumn<string>( name: "GivenName", table: "Samurais", nullable: true); migrationBuilder.AddColumn<DateTime>( name: "BetterName_LastModified", table: "Samurais", nullable: false
  • How can I create a Required Owned Type with Entity Framework Core 3.0
    I'm struggling creating a non-nullable/required Owned Type with Entity Framework Core. I'm using EF Core 3.0 against PostgreSQL database. My value object: public class PersonName { public PersonName(string name) { this.Name = name; } public string Name { get; set; } } My entity: public class Person { public int Id { get; set; } public virtual PersonName FullName { get; set; } } My entity configuration: public void Configure(EntityTypeBuilder<Person> builder) { builder.ToTable(nameof(Person)); builder.HasKey(person => person.Id); builder.OwnsOne(person => person.FullName, personName => {
  • 如何处理实体框架中的值对象?(How to deal with value objects in Entity Framework?)
    问题 如何在不污染域模型的情况下将值对象持久保存在Entity Framework中? EF(一般来说,关系数据库)要求我定义一个键-例如,我的值对象没有现成的键 public class Tag : ValueObject<Tag> { private readonly string name; public Tag(string name) { this.name = name; } public string Name { get { return this.name; }} } 另一方面,我不应该在模型中解决持久性问题。 我真的应该创建另一个类,该类包含值对象的所有字段以及键属性,然后将它们相互映射吗? 我宁愿不。 是否有更优雅的解决方案? 回答1 沃恩·弗农(Vaughn Vernon)在其出色的著作《实现域驱动的设计》中谈到了持久化价值对象(第248页)。 ORM和单值对象 基本思想是将Value的每个属性存储在存储其父Entity的行的单独列中。 换句话说,将单个值对象反规范化为其父实体的行。 对列命名采用约定以清楚地标识和标准化序列化对象的命名方式是有优势的。 ORM和数据库实体支持的许多值 使用ORM和关系数据库持久保存Value实例集合的一种非常简单的方法是将Value类型视为数据模型中的实体。 (...)要做到这一点,我们可以使用一个图层超类型。
  • Entity Framework Core 3.0 和 Entity Framework 6.3 正式发布
    Entity Framework Core 3.0 和 Entity Framework 6.3 通用版发布了,接下来我们看看它的新内容: Entity Framework Core 3.0 EF Core 3.0 包括主要特性、小部分增强和错误修复,以下是一些重要内容: LINQ overhaul 重构了 LINQ Provider,以便能够将更多的查询模式转换为 SQL,在更多情况下生成高效的查询,并防止低效率的查询无法被检测到。新的 LINQ Provider 是能在未来版本中提供新的查询功能和性能改进的基础,而不会破坏现有的应用程序和数据提供程序。 Cosmos DB 支持 EF Core 的 Cosmos DB Provider 让熟悉 EF 编程模型的开发人员能够轻松地将 Azure Cosmos DB 作为应用程序数据库。其目标是使 Cosmos DB 的一些优点,如全球分布(Global distribution)、“always on”可用性、弹性可伸缩性和低延迟,被 .NET 开发人员更容易访问。Cosmos DB Provider 针对 Cosmos DB 中的 SQL API 启用了大多数 EF Core 功能,如自动更改跟踪、LINQ 和值转换。 C# 8.0 支持 EF Core 3.0 利用了 C# 8.0 中的一些新特性: 异步流
  • 比较 EF Core 和 EF6
    比较 EF Core 和 EF6 EF Core Entity Framework Core (EF Core) 是适用于 .NET 的新式对象数据库映射器。 它支持 LINQ 查询、更改跟踪、更新和架构迁移。 EF Core 通过数据库提供程序插件模型与 SQL Server/SQL Azure、SQLite、Azure Cosmos DB、MySQL、PostgreSQL 和更多数据库配合使用。 EF6 Entity Framework 6 (EF6) 是专为 .NET Framework 设计的对象关系映射器,但支持 .NET Core。 EF6 是一款受支持的稳定产品,但我们不再对其进行积极开发。 功能比较 EF Core 提供了不会在 EF6 中实现的新功能。 但是,并非所有 EF6 功能都已在 EF Core 中实现。 下表比较了 EF Core 和 EF6 中可用的功能。 这只是大致比较,没有列出全部功能,也未解释不同 EF 版本中相同功能之间的差异。 EF Core 列指出了功能首次出现的产品版本。 创建模型 表 1 功能EF6.4EF Core基本类映射是1.0带有参数的构造函数 2.1属性值转换 2.1没有键的映射类型 2.1约定是1.0自定义约定是1.0(部分;#214)数据注释是1.0Fluent API是1.0继承:每个层次结构一个表 (TPH)是1
  • EF Core 2.2, owned entities generated as another table when multiple in hierarchy
    I have a model with a class Address marked [Owned] and a hierarchy of people (person, customer or employee, then even more subtypes etc). There are addresses at different stages of this hierarchy and all of it ends up in one table as EF Core is limited to table per hierarchy. I expected all the attributes from address to appear multiple times in that person table (once per mention in any of the subtypes) however it doesn't appear at all! Instead i see FK for each of them and a separate Address table. Does EF Core not support multiple owned members of the same type? If not is there anything i
  • 如何阻止 EF Core 索引所有外键(How to stop EF Core from indexing all foreign keys)
    问题 正如实体框架索引所有外键列等问题中所述,EF Core 似乎自动为每个外键生成一个索引。 这对我来说是一个合理的默认值(让我们不要在这里进行意见战......),但在某些情况下,这只是浪费空间并减慢插入和更新的速度。 我如何根据具体情况预防它? 我不想完全关闭它,因为它利大于弊; 我不希望有手动进行配置,所有这些指标我确实想。 我只是想在特定的FK 上阻止它。 相关的问题:EF 文档中的任何地方都提到了自动创建这些索引的事实吗? 我在任何地方都找不到它,这可能是我找不到如何禁用它的原因? 有人肯定会质疑我为什么要这样做......所以为了节省时间,链接问题的操作员在评论中给出了一个很好的例子: 例如,我们有一个People表和一个Addresses表。 People.AddressID FK 是由 EF 索引的,但我只从People行开始并搜索Addresses记录; 我从来没有找到Addresses行,然后在People.AddressID列中搜索匹配的记录。 回答1 如果真的有必要避免使用某些外键索引 - 据我所知(目前) - 在 .Net Core 中,有必要删除将在生成的迁移代码文件中设置索引的代码。 另一种方法是结合属性或扩展方法来实现自定义迁移生成器,以避免创建索引。 您可以在 EF6 的这个答案中找到更多信息:EF6 防止不在外键上创建索引。
  • Generate a composite unique constraint/index with owned entity, in EF Core
    I have an entity that owns another entity public class Entity1 { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public virtual int ID { get; set; } public string Property { get; set; } public Entity2 Description { get; set; } } public class Entity2 { public string Test { get; set; } } and I need to create an index on Entity1.Property and Entity2.Test. The configuration is like this builder.OwnsOne(pt => pt.Description); builder.HasIndex(p => new { p.Property, p.Description.Test }).IsUnique(); //builder.HasIndex("Property", "Description_Test").IsUnique(); I tried both of the above
  • EF Core 总是在添加迁移时创建 .Annotation("SqlServer:Identity", "1, 1")(EF Core always create .Annotation("SqlServer:Identity", "1, 1") on add-migration)
    问题 我在迁移 EF Core 时遇到了问题。 为了使场景清晰,我将它分配给一个现有数据库,该数据库旨在从现在开始使用迁移进行数据库控制。 但我有困难。 到目前为止我所做的。 我从现有数据库生成了脚手架。 我添加了迁移,因此它为数据库创建生成了所有“Up”。 在干净的数据库中,运行 update-database。 到目前为止完美,一切都按预期工作。 但是从这一步开始,每次我生成一个新的迁移时,他(迁移)都坚持为所有表创建一个 Alter Column 语句。 例如: migrationBuilder.AlterColumn<int>( name: "rac_n_codigo", table: "tb_rac_raca", nullable: false, oldClrType: typeof(int), oldType: "int") .Annotation("SqlServer:Identity", "1, 1"); 创建表 migrationBuilder.CreateTable( name: "tb_rac_raca", columns: table => new { rac_n_codigo = table.Column<int>(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), rac_c_nome
  • “拥有的实体类型需要通过导航从另一个实体类型引用”("The owned entity type requires to be referenced from another entity type via a navigation")
    问题 我的应用程序中有一个名为 Person 的实体。 有两种类型的用户,学生和教授,它们继承自 Person。 每个人都有一个设置属性: public abstract class Person { public Guid UserId { get; set; } public string Name { get; set; } public PersonSettings Settings { get; set; } } public class Student : Person { } public class Professor : Person { } 我的 PersonSettings 类只是几个属性。 它不是要存储在数据库中的实体,因此我将其标记为拥有: [Owned] public class PersonSettings { public bool NotificationsEnabled { get; set; } public int GymPassId { get; set; } } 这些作为 json 存储在数据库中,我在我的 Person 实体配置中使用 EF Core 转换值来对其进行序列化和反序列化: builder.Property(p => p.Settings).HasConversion( s => JsonConvert
  • EF 6:映射复杂类型集合?(EF 6: mapping complex type collection?)
    问题 EF 6(代码优先)是否支持复杂类型集合(值对象集合)映射? 我知道它支持复杂类型,但还没有找到我们拥有复杂类型集合的示例。 例如,假设您有一个名为 Student 的实体,其中包含一组联系人。 对于 NH,我可以简单地说 Student 有一组接触,并且接触是一个组件(相当于 ef 中的复杂类型)。 这可以通过 EF 完成而不改变与实体的联系吗? 回答1 显然 NHibernate 在这方面更灵活,因为在撰写本文时(EF6.2 和 EF Core 2.1),EF6 和 EF Core 都不支持复杂(或更普遍的原始或值对象)类型集合映射。 EF Core 更糟糕,因为自有实体类型,据称是 EF 复杂类型的替代品,确实具有更多类似实体的行为(从更改跟踪的角度来看),并且不能用作 DDD 不可变的多属性值对象。 我知道的唯一解决方法是以某种序列化格式((例如 XML、JSON、二进制等)将值对象/集合表示映射到单个数据库字符串/二进制列。虽然这适用于读取/存储数据,但它缺乏查询功能,因此 IMO 不是一个严肃的选择。 谈到 EF6,我认为它永远不会得到这种支持。 EF6 基本上处于维护模式,未来不会进行重大改进。 EF Core 在这方面看起来更有希望,因为对拥有实体集合的支持计划在下一个 EF 2.2 版本中发布。 然而,他们将如何实现它们(最初)是未知的
  • 实体框架核心:无法使用嵌套值对象更新实体(Entity Framework Core: Fail to update Entity with nested value objects)
    问题 我有一个实体,它有一个值对象,这个值对象有另一个值对象。 我的问题是,当更新实体和值对象时,具有父值对象的实体会更新,但子值对象没有更新。 注意,我使用了最新版本的 Entity Framework Core 2.1.0-rc1-final 这是父实体 Employee public class Employee : Entity { public string FirstName { get; private set; } public string LastName { get; private set; } public string Email { get; private set; } public Address Address { get; private set; } } 这是父值对象地址 public class Address : ValueObject<Address> { private Address() { } public Address(string street, string city, string state, string country, string zipcode, GeoLocation geoLocation) { Street = street; City = city; State = state; Country =
  • How to configure a foreign key on an owned EF entity's property?
    EF Core 3.0 I have the following (simplified) entities in my domain: class ApplicationUser { public int Id { get; set; } public string UserName { get; set; } // other properties } [Owned] class Stamp { public string Username { get; set; } public ApplicationUser User { get; set; } DateTime DateTime { get; set; } } class Activity { public Stamp Created { get; set; } public Stamp Modified { get; set; } // other properties } It's not particularly relevant, but it's worth mentioning that ApplicationUser.UserName is a non-primary, unique key. (ApplicationUser actually inherits from ASP.NET
  • 值对象在 asp.net core 2.1 中作为无效对象(Value Object as Invalid Object in asp.net core 2.1)
    问题 我一直在 asp.net core 2.0 项目中使用值对象,该项目在该项目上正常运行。 我将项目更新到 2.1,它给了我一个错误 Invalid object name 'EntityAdress'. 实体: public class Company : AuditableEntity<long> { public int SalesRepId { get; set; } public string Name { get; set; } public int StatusId { get; set; } public EntityAdress Addresses { get; set; } public string BillingAddress { get; set; } } public class EntityAdress : ValueObject { private EntityAdress() { } public string Address { get; set; } public string City { get; set; } public string State { get; set; } public int Zip { get; set; } protected override IEnumerable<object>
  • 在Entity Framework Core中使用[ComplexType](Using [ComplexType] in Entity Framework Core)
    问题 我在EF Core数据模型中使用了自己的类的属性。 public class Currency { public string Code { get; set; } public string Symbol { get; set; } public string Format { get; set; } } [ComplexType] public class Money { public int? CurrencyID { get; set; } public virtual Currency Currency { get; set; } public double? Amount { get; set; } } public class Rate { public int ID { get; set; } public Money Price = new Money(); } 我的问题是,当我尝试创建迁移时,EF Core报告错误。 Microsoft.Data.Entity.Metadata.ModelItemNotFoundException: The entity type 'RentABike.Models.Money' requires a key to be defined. 如果声明密钥,则会为“ Money”创建一个单独的表,这不是我想要的。
  • EF Core 表拆分 - 一张表到多个类(EF Core Table Splitting - One table to multiple classes)
    问题 我的表有四列,我想在多个类之间拆分它。 table1 key col1 col2 col3 col4 Class ClassA key col1 col2 class ClassB key col3 col4 modelBuilder.Entity().ToTable("table1"); modelBuilder.Entity().ToTable("table1"); 目前它给了我 System.InvalidOperationException: '不能将表 'table1' 用于实体类型 'ClassB',因为它正用于实体类型 'ClassA' 在 EF Core 中可以吗? 谢谢 回答1 您可能需要基于此 MS 文档定义如下关系: modelBuilder.Entity<ClassA>() .HasOne(e => e.ClassB).WithOne(e => e.ClassA) .HasForeignKey<ClassB>(e => e.Key); modelBuilder.Entity<ClassA>().ToTable("Products"); modelBuilder.Entity<ClassB>().ToTable("Products"); 回答2 您可以为ClassA和ClassB定义基类: abstract class ClassBase {
  • Entity Framework Code First associations/FK issues and assumptions/defaults
    I am very confused by the way Entity Framework is able to pick up on relationships between entities. I have a few questions on this. In a simple test application, I have a table of people, and a table of notes, and a table of picture assets. There are many pictures, each is owned by a person (a person can own more than one). There are many notes, each is owned by a person (a person can own more than one). and finally a person has a logo which is a picture. . public class Person { public int ID { get; set; } public string name { get; set; } public Picture logo { get; set; } } public class Note
  • EF Core Fluent API 配置防止 TPC 继承(EF Core Fluent API Configuration Prevents TPC Inheritance)
    问题 我有相互继承的模型,但我正在努力让流畅的 api 配置按照我想要的方式运行。 假设我有一个定义一些核心属性的基类 public class Entity { public int Id { get; set; } public string Title { get; set }; } 和 Book 的子类 public class Book : Entity { public int Edition { get; set; } } 这样我就可以拥有书籍、杂志、小册子、漫画、演讲等,所有这些都继承自我的实体,而不必定义每个类的关系。 现在我像这样将我的 DbSet 添加到 DbContext public class ApplicationDbContext : DbContext { public virtual DbSet<Book> Books { get; set; } public virtual DbSet<Magazine> Magazines { get; set; } public virtual DbSet<Comic> Comics { get; set; } } 最后我添加迁移初始。 我的迁移现在为每种类型创建单独的表 (TPC)。 完美的。 当我尝试使用 fluent API 配置我的基类时,问题就出现了。 我为实体添加了一个配置 class
  • 实体框架在插入到查询中不包括具有默认值的列(Entity Framework not including columns with default value in insert into query)
    问题 我有一个模型,其中一些列定义了默认值,例如 table.Column<bool>(nullable: false, defaultValueSql: "1") 当我使用context.SaveChanges()在数据库中保存一个新实体时,我注意到具有默认值的列不包含在 Entity Framework 生成的插入查询中,因此在数据库中生成的值是默认值而不是我在模型中传递的那些。 我是否必须在上下文中设置一些属性才能通过代码设置这些属性? 我正在使用 EF Core,但我不知道这是否是所有 EF 版本的一般行为。 更新:代码非常简单。 这是我所拥有的伪代码。 模型有一些用约束定义的属性,例如我在上面描述的table.Column<bool>(nullable: false, defaultValueSql: "1") 我将使用列 MyBooleanProperty 作为示例。 我有一个服务: var newModel = new GEAddress(); newModel = someEntity.MyBooleanProperty; //it is false,but ends up as 1 in the database 我正在使用工作单元和存储库,所以我有 _unitOfWork.MyModelRepository.Add(newModel); _unitOfWork
  • EF Core 创建多个外键列(EF Core creates multiple foreign key columns)
    问题 我在 .Net Core 3.1 中使用 EF Core 我有一个简单的客户端事件关系示例: public class BaseEntity { [Key] [Required] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } public DateTime CreatedOn { get; set; } public DateTime? ModifiedOn { get; set; } } public class Client : BaseEntity { public string FirstName { get; set; } public string LastName { get; set; } public string Email { get; set; } public string Phone { get; set; } } public class Event : BaseEntity { public DateTime Start { get; set; } public DateTime End { get; set; } public Client Client { get; set; } } 在我的上下文中