Laden...

EntityFramework (6.x) ManyToMany Objekt mehrmals in Auflistung

Erstellt von toxic vor 7 Jahren Letzter Beitrag vor 7 Jahren 2.303 Views
T
toxic Themenstarter:in
64 Beiträge seit 2010
vor 7 Jahren
EntityFramework (6.x) ManyToMany Objekt mehrmals in Auflistung

verwendetes Datenbanksystem: MSSqlLocalDB

Hi,
ich habe ein kleines Problem mit dem EntityFramework (6.x) und einer ManyToMany Relation.
Nehmen wir mal an, ich hab folgendes Model:


    public class Auto
    {
        public int ProduktID { get; set; }
        public string Name { get; set; }

        public ICollection<Sensor> Sensoren { get; private set; }
    }

    public class Sensor
    {
        public int SensorID { get; set; }
        public string Name { get; set; }

        public ICollection<Auto> Autos { get; private set; }
    }

Context:


    public class AutoContext : DbContext
    {
        public DbSet<Auto> Autos { get; set; }
        public DbSet<Sensor> Sensoren { get; set; }

        public AutoContext() : base() { }
        public AutoContext(string connectionstring) : base(connectionstring) { }


        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Auto>()
                .HasMany<Sensor>(X => X.Sensoren)
                .WithMany(x => x.Autos)
                .Map(m =>
               {
                   m.MapLeftKey("AutoID");
                   m.MapRightKey("SensorID");
                   m.ToTable("AutoSensoren");
               });
        }
    }

Jetzt könnte es ja sein, es gibt z.B. einen Sensor mit dem Namen „Digital“, dieser soll im Auto 2x verbaut werden. Wenn ich aber der Liste Sensoren im Auto zweimal den Sensor „Digital“ zuordne, wird er aber beim SaveChanges nur einmal als ManyToMany angelegt.


        public void Main()
        {
            using (AutoContext ctx = new AutoContext())
            {
                var sen = new Sensor() { Name = "Digital" };

                var auto = new Auto();
                auto.Name = "Brum";
                auto.Sensoren.Add(sen);
                auto.Sensoren.Add(sen);

                ctx.Autos.Add(auto);
                ctx.SaveChanges();
            }
        }

Im Debugger wird korrekt angezeigt, dass das Objekt Sensor „Digital“ 2x in der Collection Sensoren ist.
Hab ich einen Denkfehler, oder geht das im EF nicht?
Danke und Gruß

T
toxic Themenstarter:in
64 Beiträge seit 2010
vor 7 Jahren
D
985 Beiträge seit 2014
vor 7 Jahren

Du fügst aber keine zwei Sensoren hinzu, sondern einen Sensor zweimal.

T
toxic Themenstarter:in
64 Beiträge seit 2010
vor 7 Jahren

Ja das ist auch von mir so gedacht.
Also der eine Sensor zweimal. Oder 2 Stück vom gleichem Sensor.

1.040 Beiträge seit 2007
vor 7 Jahren

Bin mir zwar nicht ganz sicher, denke allerdings du hast einen Denkfehler.

Many-To-Many besagt ja grob gesagt folgendes aus:
*Auto1 *Auto2

*Sensor1 *Sensor2

Kombinationen in der Many-To-Many-Zwischentabelle:
Auto1 - Sensor1
Auto1 - Sensor2
Auto2 - Sensor1
Auto2 - Sensor2

Was aber nicht geht:
Ein Pärchen doppelt einzutragen, so wie du es vor hast.

P
1.090 Beiträge seit 2011
vor 7 Jahren

Bei Many To Many wird ja im SQL nur eine Zwischentabelle angelegt. Grob (AutoID|SensorID) wenn du den gleichen Sensor der gleichen Maschine noch einmal zuordnen würdest. Hattest du zwei gleiche Eintrage, bei denen die Spalten nicht zu unterscheiden wären. (1|1) (1|1). Wenn du das machen möchtest brauchst du noch eine Spalte z.B. (ID|AutoID|SensorID) dann funktioniert das.

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

16.806 Beiträge seit 2008
vor 7 Jahren

Eine Entität bildet ein reales Objekt ab, und reale Objekte existieren immer nur ein Mal.
Hast Du zwei Sensoren des gleichen Typs, dann sind das in der Realität trotzdem zwei Entitäten, die dann aber wiederum eine Eigenschaft mit dem Typ haben.

// ...
   var sen1 = new Sensor() { Name = "Digital #1", Type = SensorType.Digital };
   var sen2 = new Sensor() { Name = "Digital #2", Type = SensorType.Digital };

// ...
   auto.Sensoren.Add(sen1);
   auto.Sensoren.Add(sen2);
T
toxic Themenstarter:in
64 Beiträge seit 2010
vor 7 Jahren

Bei Many To Many wird ja im SQL nur eine Zwischentabelle angelegt. Grob (AutoID|SensorID) wenn du den gleichen Sensor der gleichen Maschine noch einmal zuordnen würdest. Hattest du zwei gleiche Eintrage, bei denen die Spalten nicht zu unterscheiden wären. (1|1) (1|1). Wenn du das machen möchtest brauchst du noch eine Spalte z.B. (ID|AutoID|SensorID) dann funktioniert das.

Ja genau so hab ich mir das gedacht. Mir ist die Unterscheidung zwischen (1|1) und (1|1) eigentlich egal. Hab grad in die DB geschaut. Stimmt, die beiden ID´s sind als Primärschlüssel angelegt - sprich kann also garnicht gehen 😉

Kann ich das irgendwie doch erreichen? Brauch ich unbedingt eine dritte Spalte ID?
Wenn das nicht geht, muss ich mir dann selber eine Klasse/Entity bauen?

16.806 Beiträge seit 2008
vor 7 Jahren

Ein Sensor kann immer nur in einem Auto sein, daher sprechen wir bezogen auf reale Objekte nicht von m:n, sondern von 1:n.

Damit würdest Du in Deiner Sensor-Tabelle einfach die ID des Autos mitführen; nicht umgekehrt.

Ist es eine M:N, zB weil es nicht um den Sensor an sich geht, sondern nur um den Sensortyp, dann ist zwingend ein dritte Tabelle notwendig.

D
985 Beiträge seit 2014
vor 7 Jahren

Überlegen wir uns doch mal, wie das in der Realität funktioniert, dann sieht man meistens Klara (neben Ruth und Edith, eine der wichtigstens Mädels eines Programmierers):

Wir haben da so ein Auto und dort wollen wir nun 2 Sensoren vom Typ 0815 hineinpacken.

Also greifen wir in die Kiste wo diese Sensoren vom Typ 0815 aufbewahrt werden und nehmen dort 2 hinaus und legen diese in das Auto.

Beide sind von der Art gleich und trotzdem ist jeder für sich einzeln.

In C#-EF-Code gesprochen müsste es so in der Art aussehen


namespace ConsoleApp1
{
    class Program
    {
        static void Main( string[] args )
        {
            SensorTyp sensorTyp0815 = new SensorTyp { Name = "0815", };

            dbContext.SensorTypen.Add( sensorTyp0815 );

            Auto auto = new Auto
            {
                Sensoren = new List<Sensor> {
                    new Sensor { SensorTyp = sensorTyp0815, SerienNummer="123456788" },
                    new Sensor { SensorTyp = sensorTyp0815, SerienNummer="123456789" },
                },
            };

            dbContext.Autos.Add( auto );
            dbContext.SaveChanges( );

        }
    }

    class Auto
    {
        [Key]
        public int Id { get; set; }
        [InverseProperty( nameof( Sensor.Auto ) )]
        public virtual ICollection<Sensor> Sensoren { get; set; }
    }

    class Sensor
    {
        [Key]
        public int Id { get; set; }
        [Required]
        public int SensorTypId { get; set; }
        [ForeignKey(nameof(SensorTypId))]
        public virtual SensorTyp SensorTyp { get; set; }
        [Required]
        [MaxLength( 100 )]
        public string SerienNummer { get; set; }
        public int AutoId { get; set; }
        [ForeignKey( nameof( AutoId ) )]
        public virtual Auto Auto { get; set; }
    }

    class SensorTyp
    {
        [Key]
        public int Id { get; set; }
        [Required]
        [MaxLength( 100 )]
        public string Name { get; set; }
    }
}

T
toxic Themenstarter:in
64 Beiträge seit 2010
vor 7 Jahren

Okay, erstmal vielen Dank für eure Hilfe.

Ich seh schon, ich komm um eine dritte Tabelle nicht rum.
Wünschenswert vom EF wäre für mich, so in etwa, folgendes gewesen (.MutipleAdd(true);):


        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Auto>()
                .HasMany<Sensor>(X => X.Sensoren)
                .WithMany(x => x.Autos)
                .Map(m =>
               {
                   m.MapLeftKey("AutoID");
                   m.MapRightKey("SensorID");
                   m.ToTable("AutoSensoren");
               })

               --> .MutipleAdd(true);

        }

Das EF vielleicht in der Zwischentabelle noch selbst eine ID einfügt, so wie Palin das beschrieben hat.

So muss ich ja jetzt leider immer selbst die Zuweisungen über eine Zwischentablle machen.

@Abt, dass was du geschrieben hast, wäre ja eher nur über eine Enum möglich. ("Type = SensorType.Digital") Das wollte ich aber vermeiden, weil wenn es einen neuen Sensortyp gibt, müsste ich ja die Anwendung neu erstellen.

16.806 Beiträge seit 2008
vor 7 Jahren

Davon abgesehen, dass nen String viel schlimmer als nen Enum ist, könntest Du das genauso über eine weitere Entität pflegen. Dann hast die Klassen Sensor und SensorType.
Muss keine Enum sein.

Was ich an Deinem Konzept bemängle ist einfach die nicht-beachtung von realen Objekten bzw. dass Du diese falsch behandelst.