Skip to content
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

Implement complex types (value objects) #13947

Closed
Tracked by #31238 ...
eveneveneven opened this issue Nov 13, 2018 · 19 comments · Fixed by #31003
Closed
Tracked by #31238 ...

Implement complex types (value objects) #13947

eveneveneven opened this issue Nov 13, 2018 · 19 comments · Fixed by #31003
Labels
area-o/c-mapping closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported punted-for-6.0 punted-for-7.0 Originally planned for the EF Core 7.0 (EF7) release, but moved out due to resource constraints. type-enhancement
Milestone

Comments

@eveneveneven
Copy link

Greetings, this is a question/feature request:

Per the Limitations section of the Value Conversions documentation: "There is currently no way to spread a conversion of one property to multiple columns or vice-versa."

Is something like this planned, or is there an alternative better way to do this?
I'm looking for a way to keep the backing fields out of my entity classes. And to not use strings in the configuration or public backing fields in my entity classes.

Here's a quick example from my current code working with a legacy DB:

From an entity class:

      private DateTime _lastChanged { get; set; }
      private int _lastChangedHour { get; set; }
      private int _lastChangedMinute { get; set; }      
      public DateTime LastChanged
      {
         get
         {
            if (_lastChanged.Hour != 0 || 
                _lastChanged.Minute != 0 || 
                _lastChanged.Second != 0)
               return _lastChanged;

            var returnDateTime = 
               new DateTime(
                  _lastChanged.Year, 
                  _lastChanged.Month, 
                  _lastChanged.Day, 
                  _lastChangedHour, 
                  _lastChangedMinute, 
                  0);
            return returnDateTime;
         }

         set
         {
            _lastChangedHour = value.Hour;
            _lastChangedMinute = value.Minute;
            _lastChanged = value;
         }
      }

From the entity's configuration:

            builder.Ignore(o => o.LastChanged);

            builder
               .Property<DateTime>("_lastChanged")
               .HasColumnName("SIST_ENDRET")
               .HasColumnType("datetime")
               .HasDefaultValue((DateTime) SqlDateTime.MinValue);

            builder
               .Property<int>("_lastChangedHour")
               .HasColumnName("SIST_ENDRET_TIM")
               .HasColumnType("decimal(2,0)")
               .HasConversion<decimal>();

            builder
               .Property<int>("_lastChangedMinute")
               .HasColumnName("SIST_ENDRET_MIN")
               .HasColumnType("decimal(2,0)")
               .HasConversion<decimal>();

Thanks for your time!

@ajcvickers
Copy link
Member

@divega to look for dupe.

@divega
Copy link
Contributor

divega commented Nov 16, 2018

This issue could be considered a duplicate of #9906 "Use C# structs or classes as value objects", based on the triage notes at #9906 (comment), and the fact that #13067 was previously closed in favor of #9906.

But I personally don't think the association is clear, especially given that we don't have a design for #9906. I would prefer to keep this issue open to represent the desire to have value conversions that spread over multiple columns, so that customers can vote for it and discuss it independently of structs and value objects.

@ajcvickers ajcvickers changed the title Value Conversions, spreading to multiple columns Implement value conversions that spread out over multiple columns Nov 16, 2018
@ajcvickers ajcvickers added this to the Backlog milestone Nov 19, 2018
@ajcvickers ajcvickers self-assigned this Nov 19, 2018
@mcd8604
Copy link

mcd8604 commented Dec 29, 2018

Having value conversions spread over multiple columns is exactly what I was hoping I could do. I have a property type (Length) in my data model which is defined as a struct in a third party library. It contains two critical data fields - the value (double) and the unit (enum). I simply want to map the value to a double column and the string-converted-enum to another column.

As a workaround, I ended up going with the same approach as the original poster. It is undesirable since it muddies up the data model:

Entity configuration in DbContext via Fluent API:

EntityTypeBuilder<PhysicalBraid> physicalBraidBuilder = modelBuilder.Entity<PhysicalBraid>();
physicalBraidBuilder.Property(p => p.Notes).HasColumnType("xml").HasConversion(v => XamlWriter.Save(v), v => XamlReader.Parse(v) as FlowDocument);
physicalBraidBuilder.Property<double>("measuredLength_Value");
physicalBraidBuilder.Property<LengthUnit>("measuredLength_Unit").HasConversion(new EnumToStringConverter<LengthUnit>());

Entity definition in Data Model:

public class PhysicalBraid : INotifyPropertyChanged {
	public int Id { get; set; }
	[Required]
	public virtual BraidDesign Design { get; set; }
	[Required]
	public virtual TurntableConfiguration StartConfiguration { get; set; }
	public virtual ObservableCollection<PhysicalBraidState> States { get; set; } = new ObservableCollection<PhysicalBraidState>();
	[NotMapped]
	public Length MeasuredLength {
		get => new Length(measuredLength_Value, measuredLength_Unit);
		set { measuredLength_Value = value.Value; measuredLength_Unit = value.Unit; }
	}
	private double measuredLength_Value { get; set; }
	private LengthUnit measuredLength_Unit { get; set; }
	public FlowDocument Notes { get; set; }

	public event PropertyChangedEventHandler PropertyChanged;
}

@ajcvickers
Copy link
Member

Notes from planning: we decided to close this in favor of #9906 (Use C# structs or classes as value objects) or vice versa, but reading again the comment from @divega above, I think it does make sense to keep this as separate issues. However, we'll likely work on them together.

@NichUK
Copy link

NichUK commented Mar 19, 2019

+1 please...
I have a custom value type for some finance work, which I'd like to be able to store in a readable fashion, but right now my only option seems to be storing as a byte array. It works, but it's not what I'd like. Flattening my type into 3 columns in the main table would be perfect.

@1234Georg
Copy link

An additional note: For me it would be very interesting to map a combined key (2 columns) with a conversion in a single class or struct. That also means, that I would need to query this combined key, and the restriction should be evaluated in the database and not on the client. Otherwise it would load way to much data. An example is in the issue #15962.

@AndriySvyryd AndriySvyryd changed the title Implement value conversions that spread out over multiple columns Implement value conversions that spread out over multiple columns (value objects) Mar 15, 2020
@spaasis
Copy link

spaasis commented Mar 31, 2020

This would enable really painless audit trails for EF-Core, as what is currently standing in the way is that there are issues in getting the changed values of Value Objects. Related SO: https://stackoverflow.com/questions/60946496/ef-core-how-to-audit-trail-with-value-objects

AndriySvyryd added a commit that referenced this issue Jul 12, 2023
Add model validation rules for complex types
Discover public fields on complex types
Add support for ComplexTypeAttribute

Part of #13947
AndriySvyryd added a commit that referenced this issue Aug 2, 2023
…del configuration or annotation

Throw for complex properties of value types
Throw for optional complex properties

Part of #13947
Fixes #31344
AndriySvyryd added a commit that referenced this issue Aug 2, 2023
…del configuration or annotation

Throw for complex properties of value types
Throw for optional complex properties

Part of #13947
Fixes #31344
AndriySvyryd added a commit that referenced this issue Aug 5, 2023
AndriySvyryd added a commit that referenced this issue Aug 6, 2023
AndriySvyryd added a commit that referenced this issue Aug 6, 2023
AndriySvyryd added a commit that referenced this issue Aug 6, 2023
…del configuration or annotation

Throw for complex properties of value types
Throw for optional complex properties

Part of #13947
Fixes #31344
AndriySvyryd added a commit that referenced this issue Aug 7, 2023
…del configuration or annotation

Throw for complex properties of value types.
Throw for optional complex properties.

Part of #13947
Fixes #31344
AndriySvyryd added a commit that referenced this issue Aug 7, 2023
@ajcvickers ajcvickers modified the milestones: Backlog, 8.0.0-rc1 Sep 6, 2023
@ajcvickers ajcvickers added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Sep 6, 2023
@AndriySvyryd AndriySvyryd removed their assignment Oct 9, 2023
@ajcvickers ajcvickers modified the milestones: 8.0.0-rc1, 8.0.0 Nov 14, 2023
@DumboJet
Copy link

DumboJet commented Mar 5, 2024

So, is this implemented now?
I don't see the docs here updated.
How is this done?

@ajcvickers
Copy link
Member

@DumboJet Docs are behind. See Value objects using Complex Types.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-o/c-mapping closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported punted-for-6.0 punted-for-7.0 Originally planned for the EF Core 7.0 (EF7) release, but moved out due to resource constraints. type-enhancement
Projects
None yet
Development

Successfully merging a pull request may close this issue.