EF Core中可为空的所有类型

时间:2018-01-02 15:31:44

标签: c# entity-framework-core

我的情况我想存储一个地址,但必须是可选的。

我的映射看起来像这样:

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

但是当我将DbContext与Address as为空时,我得到了这个错误:

  

InvalidOperationException:'会员'的实体正在分享这张桌子   '成员'使用' Member.Address#StreetAddress',但没有实体   此类型具有相同的键值' Id:-2147480644'那已经   标记为'已添加'。

然后我从构造函数中实例化了Address和Location,现在我可以保存实体了。但是当再次获取数据时,我还获得了一个实例化的地址,其中我真的想要一个空值。

是否无法制作可归类的拥有类型?

4 个答案:

答案 0 :(得分:5)

  

是否无法创建可为空的拥有类型?

从EF Core 3开始,?现在可以了。

  

所有依赖项现在都是可选的。 (在预览版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
}

答案 1 :(得分:3)

拥有类型的一个限制是不支持可选(即可为空)。 我建议你按照这个帖子。 https://github.com/aspnet/EntityFramework.Docs/issues/466

在我的解决方案中,我使用空对象方法并使用IsEmpty方法来了解地址是否为空而不是询问地址是否为空。我希望这种方法可以帮助你。

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();
    }

}

答案 2 :(得分:2)

实体框架document指出

  

除非拥有实体类型的引用导航,否则它们不能为空   明确地映射到所有者以外的单独表

因此,实际上,您的问题有解决方案。您需要将自己的实体映射到单独的表,而不是将其与所有者放在同一表中。

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

通过将位置实体映射到名为 Locations 的单独表中,拥有的实体将变为可空。

答案 3 :(得分:1)

在此处查看:https://msdn.microsoft.com/en-us/magazine/mt846463.aspx,然后向下滚动到“临时工作区以允许空值对象”。

可以在此处找到一个简短的示例:https://entityframeworkcore.com/knowledge-base/48063630/nullable-owned-types-in-ef-core

如果没有这种解决方法,我很高兴能够做到这一点,但是我不认为这是可能的。