Having the following model
public class User
{
public User(Email email)
{
Id = Guid.NewGuid();
Email = email;
}
public Guid Id { get; private set; }
public Email Email { get; private set; }
}
public class Email
{
private readonly string _value;
private Email(string value)
{
_value = value;
}
public static Email Create(string value)
{
// some validation
return new Email(value);
}
public static implicit operator string(Email email) => email._value;
}
and the configuration
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>(builder =>
{
builder.HasKey(x => x.Id);
builder.Property(x => x.Email).HasConversion(
new ValueConverter<Email, string>(email => email, value => Email.Create(value)));
});
}
it will throw:
System.InvalidOperationException
HResult=0x80131509
Message=Cannot call Property for the property 'Email' on entity type 'Customer' because it is configured as a navigation property. Property can only be used to configure scalar properties.
Source=Microsoft.EntityFrameworkCore
StackTrace:
at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalEntityTypeBuilder.Property(Property existingProperty, String propertyName, Type propertyType, MemberInfo clrProperty, Nullable`1 configurationSource, Nullable`1 typeConfigurationSource) in ..EFCoreRepo\src\EFCore\Metadata\Internal\InternalEntityTypeBuilder.cs:line 421
at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalEntityTypeBuilder.Property(String propertyName, Type propertyType, MemberInfo memberInfo, Nullable`1 configurationSource, Nullable`1 typeConfigurationSource) in ..EFCoreRepo\src\EFCore\Metadata\Internal\InternalEntityTypeBuilder.cs:line 385
at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalEntityTypeBuilder.Property(MemberInfo clrProperty, ConfigurationSource configurationSource) in ..EFCoreRepo\src\EFCore\Metadata\Internal\InternalEntityTypeBuilder.cs:line 341
at Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder`1.Property[TProperty](Expression`1 propertyExpression) in ..EFCoreRepo\src\EFCore\Metadata\Builders\EntityTypeBuilder`.cs:line 121
at EFCore.StoreContext.<>c.<OnModelCreating>b__1_0(EntityTypeBuilder`1 builder) in ..\EFCore\Tests.cs:line 45
at Microsoft.EntityFrameworkCore.ModelBuilder.Entity[TEntity](Action`1 buildAction) in ..EFCoreRepo\src\EFCore\ModelBuilder.cs:line 134
at EFCore.StoreContext.OnModelCreating(ModelBuilder modelBuilder) in ..\EFCore\Tests.cs:line 42
at Microsoft.EntityFrameworkCore.Infrastructure.ModelCustomizer.Customize(ModelBuilder modelBuilder, DbContext context) in ..EFCoreRepo\src\EFCore\Infrastructure\ModelCustomizer.cs:line 52
at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelCustomizer.Customize(ModelBuilder modelBuilder, DbContext context) in ..EFCoreRepo\src\EFCore.Relational\Infrastructure\RelationalModelCustomizer.cs:line 61
at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder, IModelValidator validator) in ..EFCoreRepo\src\EFCore\Infrastructure\ModelSource.cs:line 79
at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.<>c__DisplayClass5_0.<GetModel>b__0(Object k) in ..EFCoreRepo\src\EFCore\Infrastructure\ModelSource.cs:line 56
at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.GetModel(DbContext context, IConventionSetBuilder conventionSetBuilder, IModelValidator validator) in ..EFCoreRepo\src\EFCore\Infrastructure\ModelSource.cs:line 54
at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel() in ..EFCoreRepo\src\EFCore\Internal\DbContextServices.cs:line 72
at Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model() in ..EFCoreRepo\src\EFCore\Internal\DbContextServices.cs:line 94
at Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkServicesBuilder.<>c.<TryAddCoreServices>b__7_1(IServiceProvider p) in ..EFCoreRepo\src\EFCore\Infrastructure\EntityFrameworkServicesBuilder.cs:line 252
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies() in ..EFCoreRepo\src\EFCore\DbContext.cs:line 316
at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider() in ..EFCoreRepo\src\EFCore\DbContext.cs:line 304
at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies() in ..EFCoreRepo\src\EFCore\DbContext.cs:line 316
at Microsoft.EntityFrameworkCore.DbContext.SetEntityStates(IEnumerable`1 entities, EntityState entityState) in ..EFCoreRepo\src\EFCore\DbContext.cs:line 1126
at Microsoft.EntityFrameworkCore.DbContext.AddRange(IEnumerable`1 entities) in ..EFCoreRepo\src\EFCore\DbContext.cs:line 1144
at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.AddRange(IEnumerable`1 entities) in ..EFCoreRepo\src\EFCore\Internal\InternalDbSet.cs:line 217
at EFCore.Tests.Main() in ..\EFCore\Tests.cs:line 22
Please feel free to change the name if it's not appropriate.
EF Core version: (2.1.0-preview1 local build with last commit bf03e185f40d05442d031acd5d62bf4f27bb94bb)
Database Provider: (Microsoft.EntityFrameworkCore.SqlServer 2.1.0-preview1 local build)
using 2.1.0-preview1-final;
I found I had problems creating my context if I also Owned another property that was listed after the Conversion, i.e. order of the builder code is relevant
e.g.
The following fails:
builder.Property(x => x.Email).HasConversion(
new ValueConverter<Email, string>(email => email, value => Email.Create(value)));
builder.OwnsOne(p => p.OtherEmailType).Property(p => p.Value).HasColumnName("OtherEmailType");
But the following succeeds:
builder.OwnsOne(p => p.OtherEmailType).Property(p => p.Value).HasColumnName("OtherEmailType");
builder.Property(x => x.Email).HasConversion(
new ValueConverter<Email, string>(email => email, value => Email.Create(value)));
Ta, G.
@Devy-Devly Can you please file a new issue for this, including full details of how to reproduce the problem and version, etc. as requested in the issue template.
Following the example of @glucaci , I am trying to convert a custom type Email to string with ValueConverter (Microsoft.EntityFrameworkCore 2.1.1):
```
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext()
{
this.Database.EnsureDeleted();
this.Database.EnsureCreated();
}
public DbSet<Email> Emails { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder builder)
{
builder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=TestValueConverter; Integrated Security = SSPI;MultipleActiveResultSets=true;Connect Timeout=10;");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>(builder =>
{
builder.HasKey(x => x.Id);
builder.Property(x => x.Email).HasConversion(
new ValueConverter<Email, string>(email => email, value => Email.Create(value)));
});
}
}
public class User
{
public Guid Id { get; set; }
public Email Email { get; set; }
}
public class Email
{
private readonly string value;
private Email() { }
private Email(string value)
{
this.value = value;
}
public static implicit operator string(Email email) => email.value;
public static Email Create(string value)
{
return new Email(value);
}
}
```
it throw:
System.InvalidOperationException - The entity type 'Email' requires a primary key to be defined.
This issue is closed because #242 but... this is not working for me.
Resolved, if I write:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Ignore<Email>();
modelBuilder.Entity<User>(builder =>
{
builder.HasKey(x => x.Id);
builder.Property(x => x.Email).HasConversion(
new ValueConverter<Email, string>(email => email, value => Email.Create(value)));
});
}
modelBuilder.Ignore
@manuelcaub what you actually need here is
C#
builder.OwnsOne(x => x.Email).HasConversion(
new ValueConverter<Email, string>(email => email, value => Email.Create(value)));
The exception is because by convention EF Core recognizes every type you mention in OnModelCreating as an entity type and those require a primary key to be defined.
You should use OwnsOne API to let EF Core know, that Email property is actually a value object and its value should be treated as a part of User entity.
@manuelcaub Your code defines as DbSet for Emails:
C#
public DbSet<Email> Emails { get; set; }
This tells EF that Email is an entity type, which is incorrect since it is in this case just a scalar property of another entity type.