Laden...

EF Core 2.1 - Delete record in relations and creation of relations

Erstellt von Duesmannr vor 4 Jahren Letzter Beitrag vor 4 Jahren 1.474 Views
D
Duesmannr Themenstarter:in
161 Beiträge seit 2017
vor 4 Jahren
EF Core 2.1 - Delete record in relations and creation of relations

Verwendetes System:
Asp.Net Core 2.1 mit EF Core 2.1

1. Frage
Ich habe diese Basis Klasse:

public abstract class Base
{
    public Guid Id { get; set; }

    public Guid? CreatedById { get; set; }

    public Guid? ModifiedById { get; set; }

    public DateTime CreatedOn { get; set; } = DateTime.Now;

    public DateTime ModifiedOn { get; set; } = DateTime.Now;

    public User CreatedBy { get; set; }

    public User ModifiedBy { get; set; }
}

Die habe ich, weil ich die Eigenschaften für fast jede Klasse benötige.
Dann habe ich bspw. diese Klasse noch:

public class Access : Base
{
    public String Name { get; set; }
}

Alle Relations die ich habe, erstelle ich mit Hilfe der Fluent API.
In dem Fall wäre es:

modelBuilder.Entity<Access>()
    .HasOne(u => u.CreatedBy)
    .WithOne()
    .OnDelete(DeleteBehavior.Restrict);

modelBuilder.Entity<Access>()
    .HasOne(u => u.ModifiedBy)
    .WithOne()
    .OnDelete(DeleteBehavior.Restrict);

Das passiert derzeit in ca. 20 Klassen so. Der Hintergrund ist, dass CreatedBy und ModifiedBy nicht geleert werden sollen (die ID soll bei behalten werden), bzw. der ganze Datensatz gelöscht werden soll, wenn der/ein User gelöscht wird.

Es heißt ja, dass der Child Datensatz standardmäßig gelöscht wird, wenn der Parent gelöscht wird. Was in dem Fall nicht passieren sollte, weil es eine 1:1 Relation ist und es keine Child/Parent Abhängigkeit gibt, oder? Also ist der Aufruf von OnDelete überflüssig?

Macht es ein Unterschied, wenn ich von der Klasse Access anfange die Relation zu erstellen? Also in dem Fall gehe ich gerade auf

modelBuilder.Entity<Access>()

und erstelle die 1:1 Relation.
Ich kann die Relation ja auch von der Base Klasse aus erstellen.

2. Frage
Zweiteres wo ich gerade dran hänge ist bei M:N Relations.
Nehmen wir an, wir haben die beiden Klassen:

  1. Students
  2. Courses

Students haben N Courses und Courses haben N Students => M:N Relation => 3. Tabelle um die Relation auf zwei 1:N Relations aufzuteilen => StudentCourse Klasse wird erstellt.

In dem Beispiel was ich vor mir liegen habe, wird so die Relation erstellt:


modelBuilder.Entity<StudentCourse>().HasKey(sc => new { sc.SId, sc.CId });

modelBuilder.Entity<StudentCourse>()
    .HasOne<Student>(sc => sc.Student)
    .WithMany(s => s.StudentCourses)
    .HasForeignKey(sc => sc.SId);


modelBuilder.Entity<StudentCourse>()
    .HasOne<Course>(sc => sc.Course)
    .WithMany(s => s.StudentCourses)
    .HasForeignKey(sc => sc.CId);

Macht es einen Unterschied, wenn ich bei der Entity Course anfange und dann auf die Relation Tabelle zugreife um die Relation zu erstellen? Und als zweiten Step dann bei der Entity Student weitermache? Also das ich anstelle von Entity<StudentCourse> einmal Student und einmal Course enthalten ist. Hoffe Ihr versteht was ich meine, will nicht zu viel Code posten.

Oder wie ist die "vorgeschriebene" Art das zu bewerkstelligen?

Grüße und einen schönen Abend
duesmannr

16.835 Beiträge seit 2008
vor 4 Jahren

Ich weiß nicht auswendig wo, aber prinzipiell ist deine Frage in einem Hinweisabsatz in der Doku erklärt.
Egal wie man Relationen in der Fluent API definiert; es wirkt sich immer von Parent auf Child aus.

Wenn ich mich richtig erinnere ist es in der Cascading-Hilfe beschrieben.

Auch das zweite ist "egal", wobei die best practise sagt, dass es die Übersicht erleichtert wenn du immer das gleiche vorgehen verwendest.
EF bzw die Migrations warnen, wenn etwas am mapping nichz stimmt oder doppelt ist.

PS: man sollte Zeitstempel in Datenbanken immer mit DateTimeOffset verwenden.

2.207 Beiträge seit 2011
vor 4 Jahren

Hallo Duesmannr,

bitte beachte [Hinweis] Wie poste ich richtig? 1.2 Nur ein Thema pro Thread.

  • Es ist unmöglich einen Titel zu finden, da kein Problem richtig beschrieben werden kann
  • Es ist unmöglich zu moderieren, die Threads laufen zwangsläufig auseinander
  • Hilfesuchende haben es brutal schwer ihre infos herauszufinden. Der Sinn des Forums ist somit dahin.

Der Thread bleibt erstmal auf, da eine Antwort schon da ist. Normalerweise hätte ich ihn gelöscht, dir eine Kopie des Beitrags zugesandt und dich gebeten daraus zwei Threads zu machen. So kann dir besser geholfen werden.

Danke und Gruss

Coffeebean

D
Duesmannr Themenstarter:in
161 Beiträge seit 2017
vor 4 Jahren

Moin @Coffeebean,

ich habe sogar vor dem Posten des Beitrags nochmal in den Post zum richtig Posten geguckt.

Aber da die beiden Fragen auf ein Thema belaufen (EF Core Relations) bin ich davon ausgegangen, dass ein Thread in dem Fall in Ordnung ist.
Ich kann es gerne noch ändern und zwei Threads daraus erstellen.

@Abt
Das es sich immer auf das Child auswirkt, weiß ich. Aber wie erkenne ich, welche Properties die Childs sind?

Das mit dem Zeitstempel, okey..
Ich mache es derzeit so:


IEnumerable<IMutableEntityType> classes = modelBuilder.Model.GetEntityTypes().Where(x => x.ClrType.IsSubclassOf(typeof(Base)));

            foreach(IMutableEntityType item in classes)
            {
                modelBuilder.Entity(item.ClrType, x =>
                {
                    x.Property("CreatedOn").HasDefaultValueSql("getdate()");
                    x.Property("ModifiedOn").HasDefaultValueSql("getdate()");
                    x.Property("Id").HasDefaultValueSql("newid()");
                    x.Property("CreatedById").IsRequired(false);
                    x.Property("ModifiedById").IsRequired(false);
                });
            }

16.835 Beiträge seit 2008
vor 4 Jahren

Passt schon so; beziehen sich ja beide Fragen auf die gleiche Sache.

Du kannst einfach die Konfiguration auslagern; Dein Weg ist unpraktisch - und unnötig kompliziert.

Hier nen Beispiel aus meinem EF Talk; das erfüllt Dein Zweck.

    public abstract class BaseEntity
    {
        public int Id { get; set; }
    }

    public class PersonEntity : BaseEntity
    {
        public string Name { get; set; }
    }

    public abstract class BaseEntityConfig<TEntity> : IEntityTypeConfiguration<TEntity> where TEntity : BaseEntity
    {
        public void Configure(EntityTypeBuilder<TEntity> builder)
        {
            builder.HasKey(p => p.Id);
            builder.Property(p => p.Id).ValueGeneratedOnAdd();
        }
    }

    public  class PersonEntityConfig : BaseEntityConfig<PersonEntity> 
    {
        public new void Configure(EntityTypeBuilder<PersonEntity> builder)
        {
            builder.ToTable("Persons");

        }
    }
  • Abstrakte Basisklasse mit gemeinsamen Eigenschaften

  • Konkrete Klasse, die auf die Basisklasse aufbaut

  • Gemeinsame Konfiguration erfolgt über Basis-Konfiguration

  • Konkrete Konfiguration erbt von gemeinsamer Konfiguration

2.207 Beiträge seit 2011
vor 4 Jahren

ich habe sogar vor dem Posten des Beitrags nochmal in den Post zum richtig Posten geguckt.

Aber da die beiden Fragen auf ein Thema belaufen (EF Core Relations) bin ich davon ausgegangen, dass ein Thread in dem Fall in Ordnung ist.
Ich kann es gerne noch ändern und zwei Threads daraus erstellen.){gray}

Hallo Duesmannr,

alles gut so. Lass es so.

Gruss

Coffeebean