Efcore: Owned Types default mapping to owner table not working with RemovePluralizing

Created on 12 Jun 2018  路  9Comments  路  Source: dotnet/efcore

Steps to reproduce

I have DbSets in plural
```C#
public DbSet Persons { get; set; }


but use Db Table names in singular so I have this method that is called in `OnModelCreating`:

```C#
public static class ModelBuilderExtensions
{
    public static void RemovePluralizingTableNameConvention(this ModelBuilder modelBuilder)
    {
        foreach (IMutableEntityType entity in modelBuilder.Model.GetEntityTypes())
        {
            entity.Relational().TableName = entity.ClrType.Name;
        }
    }
}

Next there is
```C#
public class InfoLog
{
public int InfoLogId { get; set; }

public string Message { get; set; }

public Audit Audit { get; set; }

}

public class Audit
{
public string ChangedBy { get; set; }

public DateTime? ChangedTime { get; set; }

}

And in Context:
```C#
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.RemovePluralizingTableNameConvention();
    modelBuilder.Entity<InfoLog>().OwnsOne(a => a.Audit);//.ToTable(nameof(InfoLog));
}

But this creates 2 tables instead of one.
It can be fixed by add ing.ToTable, but is there some way to configure it more generic so not having to do it for every table?

Further technical details

EF Core version: 2.1
Database Provider: Microsoft.EntityFrameworkCore.SqlServer
Operating system: Win 10
IDE: Visual Studio 2017 15.7.3

closed-question customer-reported

Most helpful comment

@borisdj This can be done by not setting the table name on the owned type, but that requires that the owned type be configured before RemovePluralizingTableNameConvention processes the types. So, first move it to the end of OnModelCreating:

```C#
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity().OwnsOne(a => a.Audit);//.ToTable(nameof(InfoLog));
modelBuilder.RemovePluralizingTableNameConvention();
}

Then change RemovePluralizingTableNameConvention to skip owned types:

```C#
public static void RemovePluralizingTableNameConvention(this ModelBuilder modelBuilder)
{
    foreach (IMutableEntityType entity in modelBuilder.Model.GetEntityTypes())
    {
        if (!entity.IsOwned())
        {
            entity.Relational().TableName = entity.ClrType.Name;
        }
    }
}

Now, the owned type doesn't have an explicit table set and so will share the table of it's owner.

All 9 comments

@borisdj This can be done by not setting the table name on the owned type, but that requires that the owned type be configured before RemovePluralizingTableNameConvention processes the types. So, first move it to the end of OnModelCreating:

```C#
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity().OwnsOne(a => a.Audit);//.ToTable(nameof(InfoLog));
modelBuilder.RemovePluralizingTableNameConvention();
}

Then change RemovePluralizingTableNameConvention to skip owned types:

```C#
public static void RemovePluralizingTableNameConvention(this ModelBuilder modelBuilder)
{
    foreach (IMutableEntityType entity in modelBuilder.Model.GetEntityTypes())
    {
        if (!entity.IsOwned())
        {
            entity.Relational().TableName = entity.ClrType.Name;
        }
    }
}

Now, the owned type doesn't have an explicit table set and so will share the table of it's owner.

Just to add info, that if OwnedType is set with Attribute:
```C#
[Owned]
public class Audit { ...

Then call to
```C#
modelBuilder.RemovePluralizingTableNameConvention();

does not have to be at end.
Annotation obviously has precedence over OnModelCreating config.

@borisdj Fluent API has precedence over annotations.

@ajcvickers I know that FluentAPI has higher priority.
When for example Column name is set to custom string with both
_[Column("SomeName1")]_ Attribute, and
_.HasColumnName("SomeName2")_ Fluent API function.
In DbTable it would end up with second name: _SomeName2_

What I meant by 'precedence' is that Annotation are applied first, before Fluent config.
And that's why _RemovePluralizing_ can remain at the beginning.
Maybe the word 'precedence' was not the best term for describing this situation.

@borisdj It's actually a little more involved than that, but yes, you are essentially correct. Sorry for being pedantic. :-)

No problem, precision is always good :)
I know there is a lot of processing happening in the EF background but I wanted to simplify it for this use case.

@ajcvickers could I ask here another Q, it is also related with OwnedTypes
Why is list empty?

var entityType = context.Model.FindEntityType(typeof(InfoLog));
var list = entityType.GetNavigations().Where(a => a.DeclaringEntityType.IsOwned()).ToList();

Only GetNavigations() returns correctly 'Audit' but when Where() is added then it is empty.
Seems like IsOwned() is not working (it's always false).
Is there some other way to check if Nav.Property is Owned?

I am trying to add support for OwnedTypes into EFCore.BulkExtensions(OwnedTypes_support)

@borisdj The DeclaringEntityType is InfoLog, and InfoLog is not owned. You'll need to look at the target of the navigation.

Thx, got it
C# var list = entityType.GetNavigations().Where(a => a.GetTargetType().IsOwned()).ToList();

Was this page helpful?
0 / 5 - 0 ratings