Domanda

Il problema

Sto cercando di condividere un grande tavolo (200 + campi) intorno a ~ 7 entità utilizzando la tabella divisa da tabella come da La mia domanda precedente .

EF6 richiede proprietà di navigazione non solo dal modello primario ai modelli per bambini, ma tra tutti i modelli figlio (che succhiano).

Soluzione manuale

Questo può essere fatto manualmente:

public class Franchise
{
    [Key]
    public int Id { get; set; }
    public virtual FranchiseEntity Entity { get; set; }
    public virtual FranchiseMiscellaneous Miscellaneous { get; set; }
}

[Table("Franchise")]
public class FranchiseEntity
{
    [Key]
    public int Id { get; set; }
    public virtual FranchiseEntity Entity { get; set; } // Ignored, but relevant when inheritance involved, below...
    public virtual FranchiseMiscellaneous Miscellaneous { get; set; }
}

[Table("Franchise")]
public class FranchiseMiscellaneous
{
    [Key]
    public int Id { get; set; }
    public virtual FranchiseEntity Entity { get; set;
    public virtual FranchiseMiscellaneous Miscellaneous { get; set; }  // Ignored, but relevant when inheritance involved, below...
}
.

con mappature fluenti:

public class FranchiseMapping : EntityTypeConfiguration<Franchise>
{
    public FranchiseMapping()
    {
        HasRequired(x => x.Entity).WithRequiredPrincipal();
        HasRequired(x => x.Miscellaneous).WithRequiredPrincipal();
    }
}

public class FranchiseEntityMapping : EntityTypeConfiguration<FranchiseEntity>
{
    public FranchiseEntityMapping()
    {
        Ignore(x => x.Entity);
        HasRequired(x => x.Miscellaneous).WithRequiredPrincipal(x => x.Entity);
    }
}

public class FranchiseMiscellaneousMapping : EntityTypeConfiguration<FranchiseMiscellaneous>
{
    public FranchiseMiscellaneousMapping()
    {
        Ignore(x => x.Miscellaneous);
    }
}
.

funziona. Ma non sarà questo non si ridurrà bene con 7+ modelli.

Tentativo di migliorare # 1

Mi piacerebbe migliorare attraverso l'ereditarietà + principio secco:

public abstract class SharedFranchiseIdBase
{
    [Key]
    public int Id { get; set; }
    public virtual FranchiseEntity Entity { get; set; }
    public virtual FranchiseMiscellaneous Miscellaneous { get; set; }
}

public class Franchise : SharedFranchiseIdBase { ... }

public class FranchiseEntity : SharedFranchiseIdBase { ... }

public class FranchiseMiscellaneous : SharedFranchiseIdBase { ... }

// Maybe generalize the mapping code too...
.

Ma questo fallisce sulla prima richiesta con "Sequenza contiene più di un elemento corrispondente":

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.InvalidOperationException: Sequence contains more than one matching element
Result StackTrace:  
at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source, Func`2 predicate)
   at System.Data.Entity.ModelConfiguration.Configuration.Properties.Navigation.NavigationPropertyConfiguration.ConfigureDependentBehavior(AssociationType associationType, EdmModel model, EntityTypeConfiguration entityTypeConfiguration)
   at System.Data.Entity.ModelConfiguration.Configuration.Properties.Navigation.NavigationPropertyConfiguration.Configure(NavigationProperty navigationProperty, EdmModel model, EntityTypeConfiguration entityTypeConfiguration)
   at System.Data.Entity.ModelConfiguration.Configuration.Types.EntityTypeConfiguration.ConfigureAssociations(EntityType entityType, EdmModel model)
   at System.Data.Entity.ModelConfiguration.Configuration.Types.EntityTypeConfiguration.Configure(EntityType entityType, EdmModel model)
   at System.Data.Entity.ModelConfiguration.Configuration.ModelConfiguration.ConfigureEntities(EdmModel model)
   at System.Data.Entity.DbModelBuilder.Build(DbProviderManifest providerManifest, DbProviderInfo providerInfo)
   at System.Data.Entity.DbModelBuilder.Build(DbConnection providerConnection)
   at System.Data.Entity.Internal.LazyInternalContext.CreateModel(LazyInternalContext internalContext)
   at System.Data.Entity.Internal.RetryLazy`2.GetValue(TInput input)
   at System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
   at System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType)
   at System.Data.Entity.Internal.Linq.InternalSet`1.Initialize()
   at System.Data.Entity.Internal.Linq.InternalSet`1.get_InternalContext()
   at System.Data.Entity.Infrastructure.DbQuery`1.System.Linq.IQueryable.get_Provider()
   at System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source)
   ... // my test query function
.

Tentativo di migliorare # 2

Pensavo che potessi dichiararli astratti, quindi almeno i programmatori sono costretti ad implementare i membri corretti (succhia ancora per ri-dichiarare su ciascuna classe derivata):

public abstract class SharedFranchiseIdBase
{
    [Key]
    public int Id { get; set; }
    public abstract FranchiseEntity Entity { get; set; }
    public abstract FranchiseMiscellaneous Miscellaneous { get; set; }
}

public class Franchise : SharedFranchiseIdBase
{
    [Key]
    public int Id { get; set; }
    public override FranchiseEntity Entity { get; set; }
    public override FranchiseMiscellaneous Miscellaneous { get; set; }
}
//etc for other classes
.

Ma questo fallisce quando lo stesso errore. Eh ?? Le definizioni di classe sono identiche come la copia di lavoro tranne che vengono dichiarate "override" anziché "virtual". È come se E / F stia indicizzando su PropertyInFos o qualcosa di cui non riguarda il PropertyInfo.refleckStype

Tentativo di migliorare # 3

Potrei imputare il modello utilizzando un'interfaccia, ma questo è meno preferibile poiché l'interfaccia deve essere dichiarata su ciascuna classe che sta iniziando a sembrare piuttosto strana:

public class Franchise : SharedFranchiseIdBase, ISharedFranchiseId { ... }

public class FranchiseEntity : SharedFranchiseIdBase, ISharedFranchiseId { ... }

public class FranchiseMiscellaneous : SharedFranchiseIdBase, ISharedFranchiseId { ... }
.

Huh?

È un bug in E / F, che lotta per trattare le proprietà sulla classe base identica a quelle delle classi derivate?

Scuse per la spiegazione a lungo vento, è il riassunto dell'intera indagine di questa mattina.

È stato utile?

Soluzione

Alla fine ho deciso di adottare la soluzione manuale poiché non potevo ottenere nessuno dei tentativi di miglioramento di lavorare.

Il codice e i modelli non sono eleganti, ma alla fine della giornata esegue OK.Ho implementato il modello in 3 aree e si sta eseguendo come richiesto, nel dominio e nello strato SQL.

Per alleviare il dolore e fornire agli sviluppatori un modo coerente per lavorare con questo modello, ho creato questa interfaccia per far rispettare tutte le relazioni:

public interface ISharedFranchiseId
{
    FranchiseBilling Billing { get; set; }
    FranchiseCompliance Compliance { get; set; }
    FranchiseLeadAllocation LeadAllocation { get; set; }
    FranchiseMessaging Messaging { get; set; }
    FranchiseMiscellaneous Miscellaneous { get; set; }
    FranchiseSignup Signup { get; set; }
}
.

Quindi ciascuno dei modelli che condivide la chiave primaria ha queste proprietà (il bit fastidioso):

public class FranchiseBilling/Compliance/etc : ISharedFranchiseId
{
    // Properties implemented on this model
    #region Navigations to other entities sharing primary key
    public virtual FranchiseBilling Billing { get; set; }
    public virtual FranchiseCompliance Compliance { get; set; }
    public virtual FranchiseLeadAllocation LeadAllocation { get; set; }
    public virtual FranchiseMessaging Messaging { get; set; }
    public virtual FranchiseMiscellaneous Miscellaneous { get; set; }
    public virtual FranchiseSignup Signup { get; set; }
    #endregion
}
.

e configurare via API fluente come segue (il bit doloroso):

// Franchise = the "primary/parent" model
public class FranchiseMapping : EntityTypeConfiguration<Franchise>
{
    public FranchiseMapping()
    {
        HasRequired(x => x.Billing).WithRequiredPrincipal();
        HasRequired(x => x.Compliance).WithRequiredPrincipal();
        HasRequired(x => x.LeadAllocation).WithRequiredPrincipal();
        HasRequired(x => x.Miscellaneous).WithRequiredPrincipal();
        HasRequired(x => x.Messaging).WithRequiredPrincipal();
        HasRequired(x => x.Signup).WithRequiredPrincipal();
    }
}

// Now each "child" model gets link to all the others. We only need links going one way,
// So each model links to the ones listed below.
// This makes it easy to implement an extra child model down the track as we just
// insert the configuration it here and copy from the next one.
public class FranchiseBillingMapping : EntityTypeConfiguration<FranchiseBilling>
{
    public FranchiseBillingMapping()
    {
        Ignore(x => x.Billing);
        HasRequired(x => x.Compliance).WithRequiredDependent(x => x.Billing);
        HasRequired(x => x.LeadAllocation).WithRequiredPrincipal(x => x.Billing);
        HasRequired(x => x.Miscellaneous).WithRequiredPrincipal(x => x.Billing);
        HasRequired(x => x.Messaging).WithRequiredPrincipal(x => x.Billing);
        HasRequired(x => x.Signup).WithRequiredPrincipal(x => x.Billing);
    }
}

public class FranchiseComplianceMapping : EntityTypeConfiguration<FranchiseCompliance>
{
    public FranchiseComplianceMapping()
    {
        Ignore(x => x.Compliance);
        HasRequired(x => x.LeadAllocation).WithRequiredPrincipal(x => x.Compliance);
        HasRequired(x => x.Miscellaneous).WithRequiredPrincipal(x => x.Compliance);
        HasRequired(x => x.Messaging).WithRequiredPrincipal(x => x.Compliance);
        HasRequired(x => x.Signup).WithRequiredPrincipal(x => x.Compliance);
    }
}

public class FranchiseLeadAllocationMapping : EntityTypeConfiguration<FranchiseLeadAllocation>
{
    public FranchiseLeadAllocationMapping()
    {
        Ignore(x => x.LeadAllocation);
        HasRequired(x => x.Miscellaneous).WithRequiredPrincipal(x => x.LeadAllocation);
        HasRequired(x => x.Messaging).WithRequiredPrincipal(x => x.LeadAllocation);
        HasRequired(x => x.Signup).WithRequiredPrincipal(x => x.LeadAllocation);
    }
}

public class FranchiseeMiscellaneousMapping : EntityTypeConfiguration<FranchiseeMiscellaneous>
{
    public FranchiseeMiscellaneousMapping()
    {
        Ignore(x => x.Miscellaneous);
        HasRequired(x => x.Messaging).WithRequiredPrincipal(x => x.Miscellaneous);
        HasRequired(x => x.Signup).WithRequiredPrincipal(x => x.Miscellaneous);
    }
}

public class FranchiseMessagingMapping : EntityTypeConfiguration<FranchiseMessaging>
{
    public FranchiseMessagingMapping()
    {
        Ignore(x => x.Messaging);
        HasRequired(x => x.Signup).WithRequiredPrincipal(x => x.Messaging);
    }
}

public class FranchiseSignupMapping : EntityTypeConfiguration<FranchiseSignup>
{
    public FranchiseSignupMapping()
    {
        Ignore(x => x.Signup);
    }
}
.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top