Search code examples
c#sql-serverentity-framework-coreowned-types

EF Core Owned types don't work for loading data but they do for saving


I'm working in my project with entity that use value objects, I have read in the doc that if you want work with value objects you must use owned types. When I save my entity with its repository this work perfectly, but when I try to load data this doesn't work, it only load my ID value object but not the others

this is my context

public class WikiContext : DbContext
    {
        private string _sConnection;
        
        #region Constructs

        public WikiContext()
        {
        }
        public WikiContext(string sConnection)
        {
            _sConnection = sConnection;
        }

        #endregion

        #region Entities

        public DbSet<Customer> Customers{ get; set; }

        #endregion

        #region Mapping

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(_sConnection);
            base.OnConfiguring(optionsBuilder);
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.ApplyConfiguration(new CustomerConfiguration());
           
            base.OnModelCreating(modelBuilder);
        }

        #endregion
    }

this is my CustomerConfiguration:

public class CustomerConfiguration : IEntityTypeConfiguration<Customer>
    {
        public void Configure(EntityTypeBuilder<Customer> builder)
        {
            // https://stackoverflow.com/questions/49607375/ef-core-dbcontext-map-custom-type-as-primary-key
            // http://thedatafarm.com/data-access/entity-framework-private-constructors-and-private-setters/
            builder.ToTable("Customers");
            builder.HasKey(customer=> new { customer.Id });
            builder
                .Property<CustomerId>("Id")
                .HasConversion(id => id.Value, guid => new CustomerId(guid));



            // https://www.edgesidesolutions.com/ddd-value-objects-with-entity-framework-core/
            builder.OwnsOne(c => c.Name, a =>
            {
                a.Property(p => p.Value).HasColumnName("Name");
                a.WithOwner();
            });

            builder.OwnsOne(c => c.StudioId, a =>
            {
                a.Property(p => p.Value).HasColumnName("StudioId").HasColumnType("uniqueidentifier");
                a.WithOwner();
            });

            
        }
    }

and this is my customer repository method

        public Customer[] Search()
        {
            var customers = Context.Customers.Select(customer=> customer);
            
            return customers.ToArray();
        }

I can do a workaround with the next model configuration, but I still have doubt about this way

 protected override void OnModelCreating(ModelBuilder modelBuilder)
        {

            modelBuilder.Entity<Customer>(builder =>
            {
                builder.ToTable("Customers");
                builder.HasKey(customer => new { customer.Id });
                builder
                    .Property<CustomerId>("Id")
                    .HasConversion(id => id.Value, guid => new CustomerId(guid));
            
                builder
                    .Property<CustomerName>("Name")
                    .HasColumnName("Name")
                    .HasConversion(name => name.Value, value => new CustomerName(value));
            
                builder
                    .Property<StudioId>("StudioId")
                    .HasColumnName("StudioId")
                    .HasConversion(p => p.Value, value => new StudioId(value));
            });
            
            base.OnModelCreating(modelBuilder);
        }

        #endregion

this is the Customer Entity

    public class Customer: IAggregateRoot
    {
        #region Constructors

        private Customer()
        {
        }
        
        public Customer(CustomerId oId, CustomerName oCustomerName, StudioId oStudioId)
        {
            Id = oId;
            Name = oCustomerName;
            StudioId = oStudioId;
        }

        #endregion

        #region Public Methods

        public static Customer Create(CustomerId oId, CustomerName  oName, StudioId oStudioId)
        {
            var oCustomer = new Customer(oId, oName, oStudioId);

            //TODO: implement event sourcing here

            return oCustomer;
        }

        #endregion

        #region Property

        public CustomerId Id { get; set; }

        public CustomerName Name { get; set; }

        public StudioId StudioId { get; set; }

        #endregion
    }

    public class CustomerName
    {
        
        public string Value { get; set; }
        
        #region Constructors

        private CustomerName()
        {
        }

        public CustomerName(string sName)
        {
            Value = sName;
        }

        #endregion
    }

I can't figure out where the problem is, I tryed several model configuration but still I can understand why this doesn't works.

I'm using EntityFrameworkCore 5.0.1 with SQL Server and .Net Core 3.1


Solution

  • This was answered in the official EFCore's github repository, here a link to the answer answer