2016-05-10 2 views
0

ich ein EF-Modell mit vielen Einheiten haben, wie Knoten, Attribute, Tags, etc.EF6: Einzelbeziehungstabelle für mehrere verbundene Unternehmen

Es gibt auch ein „Alias“ Einheit, und so ziemlich jede andere Einheit anders kann eine Viele-zu-Viele-Beziehung mit Aliasen haben. Eine der unerwünschten Eigenschaften ist die Anzahl der Tabellen, die zum Verfolgen dieser Beziehungen erstellt werden (z. B. NodeAlias, AttributeAlias ​​usw.).

Gibt es Designalternativen, die einen Alias ​​allen anderen Entitäten in einer einzelnen Tabelle zuordnen könnten? Ich dachte, vielleicht etwas in dieser Richtung, wenn es möglich ist:

+---------+--------+-------------+-----------+ 
| AliasId | NodeId | AttributeId | TagId | 
+---------+--------+-------------+-----------+ 
|  1 |  1 |   2 |   3 | 
+---------+--------+-------------+-----------+ 

Antwort

1

ich meine Lösung aktualisiert, um zu sehen viele-zu-viele Beziehungen zwischen Aliase und jedem anderen Unternehmen zur Verfügung zu stellen.

Ich habe dies absichtlich als eine separate Antwort geschrieben, so dass meine vorherige Antwort auch hier bleiben kann, wenn jemand es brauchen würde.

Schritt # 1: I Erweiterungsmethoden erstellt für das Abrufen und Eigenschaftswerte einstellen Reflexion in bequeme Weise:

public static class ObjectExtensions 
{ 
    public static TResult GetPropertyValue<TResult>(this object entity, string propertyName) 
    { 
     object propertyValue = entity?.GetType().GetProperty(propertyName)?.GetValue(entity); 

     try 
     { 
      return (TResult)propertyValue; 
     } 
     catch 
     { 
      return default(TResult); 
     } 
    } 

    public static void SetPropertyValue(this object entity, string propertyName, object value) 
    { 
     entity?.GetType().GetProperty(propertyName)?.SetValue(entity, value); 
    } 
} 

Schritt # 2: ich die Modelle aktualisiert viele-bieten viele Beziehungen.

public class Node 
{ 
    [Key] 
    public int NodeId { get; set; } 

    public string Name { get; set; } 

    public virtual ICollection<AliasMapping> AliasMappings { get; set; } 
} 

public class Attribute 
{ 
    [Key] 
    public int AttributeId { get; set; } 

    public string Name { get; set; } 

    public virtual ICollection<AliasMapping> AliasMappings { get; set; } 
} 

public class Tag 
{ 
    [Key] 
    public int TagId { get; set; } 

    public string Name { get; set; } 

    public virtual ICollection<AliasMapping> AliasMappings { get; set; } 
} 

public class Alias 
{ 
    [Key] 
    public int AliasId { get; set; } 

    public string Name { get; set; } 

    public virtual ICollection<AliasMapping> AliasMappings { get; set; } 
} 

public class AliasMapping 
{ 
    [Key] 
    public int Id { get; set; } 

    [ForeignKey("Alias")] 
    public int AliasId { get; set; } 

    public Alias Alias { get; set; } 

    [ForeignKey("Node")] 
    public int? NodeId { get; set; } 

    public virtual Node Node { get; set; } 

    [ForeignKey("Attribute")] 
    public int? AttributeId { get; set; } 

    public virtual Attribute Attribute { get; set; } 

    [ForeignKey("Tag")] 
    public int? TagId { get; set; } 

    public virtual Tag Tag { get; set; } 
} 

Schritt # 3: Aufgrund Beziehung ändert sich die MyDbContext als die [ForeignKey] Daten vereinfacht werden könnte Anmerkungen genug sind.

public class MyDbContext : DbContext 
{ 
    public DbSet<Node> Nodes { get; set; } 
    public DbSet<Attribute> Attributes { get; set; } 
    public DbSet<Tag> Tags { get; set; } 
    public DbSet<Alias> Aliases { get; set; } 
    public DbSet<AliasMapping> AliasMappings { get; set; } 
} 

Schritt # 4: ich auch die Erweiterungsmethoden aktualisiert, so dass Sie erstellen und Alias-Zuordnungen entfernen können.

public static class AliasExtensions 
{ 
    public static void CreateMapping(this MyDbContext context, object entity, Alias alias) 
    { 
     if (entity == null || alias == null) 
     { 
      return; 
     } 

     string mappingEntityPropertyName = entity.GetType().Name; 
     string entityKeyPropertyName = String.Concat(mappingEntityPropertyName, "Id"); 
     int entityId = entity.GetPropertyValue<int>(entityKeyPropertyName); 

     AliasMapping[] mappings = 
      context 
       .AliasMappings 
       .Where(mapping => mapping.AliasId == alias.AliasId) 
       .ToArray(); 

     if (mappings.Any(mapping => mapping.GetPropertyValue<int?>(entityKeyPropertyName) == entityId)) 
     { 
      // We already have the mapping between the specified entity and alias. 
      return; 
     } 

     bool usableMappingExists = true; 
     var usableMapping = mappings.FirstOrDefault(mapping => mapping.GetPropertyValue<int?>(entityKeyPropertyName) == null); 

     if (usableMapping == null) 
     { 
      usableMappingExists = false; 
      usableMapping = new AliasMapping() 
      { 
       Alias = alias 
      }; 
     } 

     usableMapping.SetPropertyValue(mappingEntityPropertyName, entity); 
     usableMapping.SetPropertyValue(entityKeyPropertyName, entityId); 

     if (!usableMappingExists) 
     { 
      context.AliasMappings.Add(usableMapping); 
     } 

     // This step is required here, I think due to using reflection. 
     context.SaveChanges(); 
    } 

    public static void RemoveMapping(this MyDbContext context, object entity, Alias alias) 
    { 
     if (entity == null || alias == null) 
     { 
      return; 
     } 

     string mappingEntityPropertyName = entity.GetType().Name; 
     string entityKeyPropertyName = String.Concat(mappingEntityPropertyName, "Id"); 
     int entityId = entity.GetPropertyValue<int>(entityKeyPropertyName); 

     AliasMapping[] mappings = 
      context 
       .AliasMappings 
       .Where(mapping => mapping.AliasId == alias.AliasId) 
       .ToArray(); 

     AliasMapping currentMapping = mappings.FirstOrDefault(mapping => mapping.GetPropertyValue<int?>(entityKeyPropertyName) == entityId); 

     if (currentMapping == null) 
     { 
      // There is no mapping between the specified entity and alias. 
      return; 
     } 

     currentMapping.SetPropertyValue(mappingEntityPropertyName, null); 
     currentMapping.SetPropertyValue(entityKeyPropertyName, null); 

     // This step is required here, I think due to using reflection. 
     context.SaveChanges(); 
    } 
} 

Schritt # 5: die Konsole app Schritte aktualisiert sie mit den Veränderungen anzupassen.

class Program 
{ 
    static void Main(string[] args) 
    { 
     // Consider specify the appropriate database initializer! 
     // I use DropCreateDatabaseAlways<> strategy only for this example. 
     Database.SetInitializer(new DropCreateDatabaseAlways<MyDbContext>()); 

     var aliases = 
      Enumerable 
       .Range(1, 9) 
       .Select(index => new Alias() { Name = String.Format("Alias{0:00}", index) }) 
       .ToList(); 

     var attributes = 
      Enumerable 
       .Range(1, 5) 
       .Select(index => new Attribute() { Name = String.Format("Attribute{0:00}", index) }) 
       .ToList(); 

     var nodes = 
      Enumerable 
       .Range(1, 5) 
       .Select(index => new Node() { Name = String.Format("Node{0:00}", index) }) 
       .ToList(); 

     var tags = 
      Enumerable 
       .Range(1, 5) 
       .Select(index => new Tag() { Name = String.Format("Tag{0:00}", index) }) 
       .ToList(); 

     using (var context = new MyDbContext()) 
     { 
      context.Aliases.AddRange(aliases); 
      context.Nodes.AddRange(nodes); 
      context.Attributes.AddRange(attributes); 
      context.Tags.AddRange(tags); 

      // Always save changes after adding an entity but before trying to create a mapping. 
      context.SaveChanges(); 

      // One Alias To Many Entities 
      context.CreateMapping(nodes[0], aliases[0]); 
      context.CreateMapping(nodes[1], aliases[0]); 
      context.CreateMapping(nodes[2], aliases[0]); 
      context.CreateMapping(nodes[3], aliases[0]); 
      context.CreateMapping(attributes[0], aliases[0]); 
      context.CreateMapping(attributes[1], aliases[0]); 
      context.CreateMapping(attributes[2], aliases[0]); 
      context.CreateMapping(tags[0], aliases[0]); 
      context.CreateMapping(tags[1], aliases[0]); 

      // One Entity To Many Aliases 
      context.CreateMapping(nodes[4], aliases[0]); 
      context.CreateMapping(nodes[4], aliases[1]); 
      context.CreateMapping(nodes[4], aliases[2]); 
      context.CreateMapping(attributes[3], aliases[1]); 
      context.CreateMapping(attributes[3], aliases[3]); 
      context.CreateMapping(tags[2], aliases[2]); 
      context.CreateMapping(tags[2], aliases[3]); 

      // Remove mapping 
      context.RemoveMapping(nodes[4], aliases[0]); 

      // Not really needed here as both 'CreateMapping' and 'RemoveMapping' save the changes 
      context.SaveChanges(); 
     } 

     Console.Write("Press any key to continue . . ."); 
     Console.ReadKey(true); 
    } 
} 

Bitte beachten Sie:RemoveMapping() wird keine AliasMapping auch wenn keine Einheit mit ihm verbunden ist, löschen! Aber CreateMapping() wird später bei Bedarf davon Gebrauch machen. Z.B. Auf dem Screenshot unten und überprüfen AliasMapping wo Id = 5.

Screenshot über das Ausführungsergebnis:

AliasMapping

0

Sie sprachen über viele-zu-viele-Beziehung, aber das Lesen Sie Ihre Post Ich denke, es ist mehr wahrscheinlich eine „besondere one-to-many“ -Beziehung, tatsächlich "kombinierte mehrere Eins-zu-Eins" -Beziehung, wie ich sehe, dass ein Alias kann auf eine einzige Node AND/OR auf eine einzige Attribute AND/OR auf eine einzige Tag abgebildet werden.

Ich denke, ich habe eine Lösung für diesen Fall gefunden.

Wenn es nicht der Fall ist und ein Alias kann Node UND/ODER mehrere Attribute UND/ODER auf mehr Tag dann auf mehr abgebildet werden denke ich, diese Lösung unter nur eine kleine Änderung muss. :)

Schritt # 1 - Das sind meine Beispielmodelle

public class Node 
{ 
    [Key] 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public virtual AliasMapping AliasMapping { get; set; } 
} 

public class Attribute 
{ 
    [Key] 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public virtual AliasMapping AliasMapping { get; set; } 
} 

public class Tag 
{ 
    [Key] 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public virtual AliasMapping AliasMapping { get; set; } 
} 

public class Alias 
{ 
    [Key] 
    public int AliasId { get; set; } 
    public string Name { get; set; } 
    public virtual AliasMapping AliasMapping { get; set; } 
} 

Schritt # 2 - Erstellen der benutzerdefinierten Zuordnungstabelle

public class AliasMapping 
{ 
    [Key] 
    [ForeignKey("Alias")] 
    public int AliasId { get; set; } 

    public Alias Alias { get; set; } 

    [ForeignKey("Node")] 
    public int NodeId { get; set; } 

    public virtual Node Node { get; set; } 

    [ForeignKey("Attribute")] 
    public int AttributeId { get; set; } 

    public virtual Attribute Attribute { get; set; } 

    [ForeignKey("Tag")] 
    public int TagId { get; set; } 

    public virtual Tag Tag { get; set; } 
} 

Schritt # 3 - Erstellen der DbContext

public class MyDbContext : DbContext 
{ 
    public DbSet<Node> Nodes { get; set; } 
    public DbSet<Attribute> Attributes { get; set; } 
    public DbSet<Tag> Tags { get; set; } 
    public DbSet<Alias> Aliases { get; set; } 
    public DbSet<AliasMapping> AliasMappings { get; set; } 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     modelBuilder 
      .Entity<AliasMapping>() 
      .HasOptional(mapping => mapping.Attribute) 
      .WithOptionalPrincipal(attribute => attribute.AliasMapping) 
      .Map(config => config.MapKey("AliasId")); 

     modelBuilder 
      .Entity<AliasMapping>() 
      .HasOptional(mapping => mapping.Node) 
      .WithOptionalPrincipal(node => node.AliasMapping) 
      .Map(config => config.MapKey("AliasId")); 

     modelBuilder 
      .Entity<AliasMapping>() 
      .HasOptional(mapping => mapping.Tag) 
      .WithOptionalPrincipal(tag => tag.AliasMapping) 
      .Map(config => config.MapKey("AliasId")); 
    } 
} 

Schritt # 4 - Erstellen einer Erweiterungsmethode, sodass das Erstellen einer Beziehung einfach ist

public static class AliasExtensions 
{ 
    public static void CreateMapping<TEntity>(this MyDbContext context, TEntity entity, Alias alias) 
    { 
     string mappingEntityPropertyName = typeof(TEntity).Name; 
     string entityKeyPropertyName = String.Concat(mappingEntityPropertyName, "Id"); 

     bool entityExists = true; 
     var mapping = context.AliasMappings.Find(alias.AliasId); 

     if (mapping == null) 
     { 
      entityExists = false; 
      mapping = new AliasMapping() 
      { 
       Alias = alias 
      }; 
     } 

     typeof(AliasMapping) 
      .GetProperty(mappingEntityPropertyName) 
      .SetValue(mapping, entity); 

     typeof(AliasMapping) 
      .GetProperty(entityKeyPropertyName) 
      .SetValue(mapping, typeof(TEntity).GetProperty("Id").GetValue(entity)); 

     if (!entityExists) 
     { 
      context.AliasMappings.Add(mapping); 
     } 
    } 
} 

Schritt # 5 - eine Konsole App erstellt diese Arbeit

class Program 
{ 
    static readonly Random rnd = new Random(DateTime.Now.TimeOfDay.Milliseconds); 

    static void Main(string[] args) 
    { 
     Database.SetInitializer(new DropCreateDatabaseAlways<MyDbContext>()); 

     var aliases = 
      Enumerable 
       .Range(1, 9) 
       .Select(index => new Alias() { Name = String.Format("Alias{0:00}", index) }) 
       .ToList(); 

     var attributes = 
      Enumerable 
       .Range(1, 5) 
       .Select(index => new Attribute() { Name = String.Format("Attribute{0:00}", index) }) 
       .ToList(); 

     var nodes = 
      Enumerable 
       .Range(1, 5) 
       .Select(index => new Node() { Name = String.Format("Node{0:00}", index) }) 
       .ToList(); 

     var tags = 
      Enumerable 
       .Range(1, 5) 
       .Select(index => new Tag() { Name = String.Format("Tag{0:00}", index) }) 
       .ToList(); 

     using (var context = new MyDbContext()) 
     { 
      context.Aliases.AddRange(aliases); 
      context.Nodes.AddRange(nodes); 
      context.Attributes.AddRange(attributes); 
      context.Tags.AddRange(tags); 

      context.SaveChanges(); 

      // Associate aliases to attributes 
      attributes.ForEach(attribute => 
      { 
       var usableAliases = aliases.Where(alias => alias.AliasMapping?.Attribute == null).ToList(); 
       var selectedAlias = usableAliases[rnd.Next(usableAliases.Count)]; 
       context.CreateMapping(attribute, selectedAlias); 
      }); 

      // Associate aliases to nodes 
      nodes.ForEach(node => 
      { 
       var usableAliases = aliases.Where(alias => alias.AliasMapping?.Node == null).ToList(); 
       var selectedAlias = usableAliases[rnd.Next(usableAliases.Count)]; 
       context.CreateMapping(node, selectedAlias); 
      }); 

      // Associate aliases to tags 
      tags.ForEach(tag => 
      { 
       var usableAliases = aliases.Where(alias => alias.AliasMapping?.Tag == null).ToList(); 
       var selectedAlias = usableAliases[rnd.Next(usableAliases.Count)]; 
       context.CreateMapping(tag, selectedAlias); 
      }); 

      context.SaveChanges(); 
     } 

     Console.Write("Press any key to continue . . ."); 
     Console.ReadKey(true); 
    } 
} 
+0

Wow, vielen Dank dafür! Ich habe eine Frage zu der Lösung: Was ist ein Knoten benötigt eine Sammlung von Aliasen, anstatt nur 0 oder 1? Wäre es eine glatte Art, das zu tun, selbst wenn es eine separate Aufzeichnung in der Tabelle Aliase bedeuten würde? – blgrnboy

+0

@blgrnboy Ich habe eine neue Antwort hinzugefügt, die Ihnen die Viele-zu-Viele-Beziehung zwischen Aliasen und allen anderen Entitäten bietet. Hör zu! ;) – Gabor