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

Use C# structs or classes as value objects #9906

Closed
Tracked by #31238 ...
sir-boformer opened this issue Sep 26, 2017 · 56 comments
Closed
Tracked by #31238 ...

Use C# structs or classes as value objects #9906

sir-boformer opened this issue Sep 26, 2017 · 56 comments
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. 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

@sir-boformer
Copy link

I was really looking forward to the new Owned Entities feature until I found out that struct types are not supported.

Common examples of struct types:

struct Vector3 {
    float X { get; set; }
    float Y { get; set; }
    float Z { get; set; }
}

struct TimeRange {
    DateTime StartTime { get; set; }
    DateTime EndTime { get; set; }
}

struct Date {
    private readonly DateTime _dt;

    public UtcDate(int year, int month, int day)
    {
        _dt = new DateTime(year, month, day, 0, 0, 0, DateTimeKind.Utc);
    }
    ...
}

Similar Struct Types in third party libraries: corefxlab NodaTime

I would like to use these struct types in my entities:

class Event {
    string Name { get;set; }
    TimeRange TimeRange { get; set; }
}

class BankHoliday {
    string Name { get;set; }
    Date Date { get; set; }
}

modelBuilder.Entity<Event>().OwnsOne(e => e.TimeRange);
modelBuilder.Entity<BankHoliday>().OwnsOne(e => e.Date);

so that the generated tables look like these:

Event [Name, TimeRange_StartTime, TimeRange_EndTime]
BankHoliday [Name, Date_dt]
@fgheysels
Copy link

Good point.
Value types (like an address type for instance) are most likely to be mapped as an Owned Entity. Value types are mostly represented by structs, so not having the possibility to map a struct as an 'owned entity' could indeed be a dealbreaker.

@sir-boformer
Copy link
Author

It seems like nullable owned entities are also not supported.

@smitpatel
Copy link
Member

@sir-boformer - nullable owned entities are covered by #9005 If you map them to different table then they can work.

@fgheysels
Copy link

Why would you want to map an owned entity to a different table ? In most cases, I want them to be part of the owning table. Nullable owned entities are a valid use-case, especially if the database already exists ...

@smitpatel
Copy link
Member

@fgheysels - That is valid use-case. At present, when they are mapped in same table, they uses same column for PK. In current implementation there is no way to figure out if the owned entity is null. So if you map it to separate table, you can find out owned entity is null when no matching row is found. It is not ideal and users would want to map it to same table hence #9005 is the tracking issue to implement support for it.

@ajcvickers
Copy link
Member

Notes from triage: we think the common cases for structs like these could be handled by being able to map a type to multiple columns. The main missing feature for this is type mapping from a single property to multiple columns.

@ajcvickers ajcvickers added this to the Backlog milestone Oct 4, 2017
@ajcvickers ajcvickers changed the title Struct Owned Entities Use C# structs in entities Oct 4, 2017
@ajcvickers ajcvickers changed the title Use C# structs in entities Use C# structs or classes as value objects Nov 27, 2017
@fgheysels
Copy link

@smitpatel - I think that , when using owned entities as a value type (which will be for me the most obvious usage of owned entities), you should not think in terms of primary keys. A value type is not an entity and has no identifier; it's identity is made up by its value.

@ajcvickers I'm a fond user of NHibernate, and there this is done using 'component-mapping'. Maybe helpfull to take a look at it ? http://nhibernate.info/doc/nhibernate-reference/components.html

@ajcvickers
Copy link
Member

Note: consider the case that the value object contains key properties--see #10682

@huoyaoyuan
Copy link
Member

This prevents me from reusing my models, as I'm using struct for related properties set heavily.

@bugproof
Copy link

bugproof commented Oct 3, 2018

It works for classes but not for structs. If you try to use struct with OwnsOne you get error: error CS0452: The type 'Vector' must be a reference type in order to use it as parameter 'TRelatedEntity' in the generic type or method 'EntityTypeBuilder<Player>.OwnsOne<TRelatedEntity>(Expression<Func<Player, TRelatedEntity>>)

It says clearly that Vector must be a reference type (class) but struct is a value type.

@ajcvickers
Copy link
Member

See also #13947 (comment)

@CesarD
Copy link

CesarD commented May 25, 2023

Would this allow support for ValueObjects implemented with C# Records? Using classes when we could leverage records to implement a ValueObject on our model is kind of cumbersome.

@roji
Copy link
Member

roji commented May 26, 2023

@CesarD probably, yes.

@rofenix2
Copy link

rofenix2 commented Jun 6, 2023

Two questions:

  • Will value objects accept this signature?: public readonly record struct.
  • Will it support value object inheritance?.

@roji
Copy link
Member

roji commented Jun 6, 2023

Will value objects accept this signature?: public readonly record struct.

Probably yes, although performing SaveChanges updates on immutable types (whether value types or otherwise) is tracked by #11457.

Will it support value object inheritance?

What do you mean by that?

@rofenix2
Copy link

rofenix2 commented Jun 6, 2023

Base class with derived types for value objects, but i realise now that this can be done with json right?.

@roji
Copy link
Member

roji commented Jun 6, 2023

It's hard to know at this point exactly what will be supported and what won't - there's no specific plan yet for this feature.

ajcvickers added a commit that referenced this issue Aug 15, 2023
ajcvickers added a commit that referenced this issue Aug 15, 2023
roji pushed a commit to roji/efcore that referenced this issue Aug 16, 2023
@ajcvickers ajcvickers modified the milestones: Backlog, 8.0.0-rc1, 8.0.0-rc2 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
@KennethHoff
Copy link

🎉

@ajcvickers ajcvickers modified the milestones: 8.0.0-rc2, 8.0.0 Nov 14, 2023
@NickStrupat
Copy link

Now that structs can be used as complex types, is there a plan to allow ComplexTypeAttributes on struct?

Currently it's only allowed on classes. So, if you use a struct you have to use the ModelBuilder fluent API.

[AttributeUsage(Class /*| Struct*/, AllowMultiple=false)]
public class ComplexTypeAttribute : Attribute { ... }

@ajcvickers
Copy link
Member

@NickStrupat The team decided not to do this. /cc @AndriySvyryd

@AndriySvyryd
Copy link
Member

@NickStrupat You can also call ComplexProperties<T>() in ConfigureConventions

@dtila
Copy link

dtila commented Mar 31, 2024

Nor micrososft provides any example. Here is what I have tried:

in the ConfigureConventions I have defined the complex property:

builder.ComplexProperties<MyStruct>()

In the entity I define the property:
e.ComplexProperty(e => e.JouePublish);

This gives an exception:

No suitable constructor was found for entity type 'Ebtitiy.MyStruct?'. The following constructors had parameters that could not be bound to properties of the entity type:
Cannot bind 'structProperty1' in 'Entity#MyStruct?(byte param1, int param2)'
Note that only mapped properties can be bound to constructor parameters. Navigations to related entities, including references to owned types, cannot be bound.

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. 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

No branches or pull requests