Questi modelli possono essere rappresentati in EF7?

c# entity-framework-core

Domanda

Sto cercando di utilizzare alcune classi da un altro assembly nel mio progetto come entità che posso persistere usando EF7, piuttosto che scrivere una serie di classi molto simili che sono più adatte al database.

Le versioni semplificate hanno questo aspetto:

interface IMediaFile
{
    string Uri { get; }
    string Title { get; set; }
}
class CMediaFile : IMediaFile
{
    public CMediaFile() { }
    public string Uri { get; set; }
    public string Title { get; set; }
}

//The following types are in my project and have full control over.
interface IPlaylistEntry
{
    IMediaFile MediaFile { get; }
}
class CPlaylistEntry<T> : IPlaylistEntry where T : IMediaFile
{
    public CPlaylistEntry() { }
    public T MediaFile { get; set; }
}

Ci sono molteplici implementazioni di IMediaFile, ne sto mostrando solo una. My PlaylistEntry class accetta un argomento generico per abilitare diversi tratti per le varie implementazioni e io lavoro solo con IPlaylistEntry.

Quindi ho iniziato a modellarlo in questo modo:

var mediaFile = _modelBuilder.Entity<CMediaFile>();
mediaFile.Key(e => e.Uri);
mediaFile.Index(e => e.Uri);
mediaFile.Property(e => e.Title).MaxLength(256).Required();

var mediaFilePlaylistEntry = _modelBuilder.Entity<CPlaylistEntry<CMediaFile>>();
mediaFilePlaylistEntry.Key(e => e.MediaFile);
mediaFilePlaylistEntry.Reference(e => e.MediaFile).InverseReference();

Come semplice test, ignoro il CPlaylistEntry <> e faccio semplicemente:

dbContext.Set<CMediaFile>().Add(new CMediaFile() { Uri = "irrelevant", Title = "" });
dbContext.SaveChanges()

Questo genera:

NotSupportedException: "MediaFile" sul tipo di entità "CPlaylistEntry" non ha un valore impostato e nessun generatore di valori è disponibile per le proprietà di tipo "CMediaFile". Impostare un valore per la proprietà prima di aggiungere l'entità o configurare un generatore di valori per le proprietà di tipo "CMediaFile"

Non capisco nemmeno questa eccezione e non vedo perché CPlaylistEntry venga visualizzato quando sto provando a memorizzare un'entità CMediaFile. Sto indovinando che questo è legato alla mia definizione del modello - in particolare, la definizione della chiave primaria di CPlaylistEntry non è un tipo semplice, ma un tipo complesso - un'altra entità. Tuttavia, mi aspetto che EF sia abbastanza intelligente da capire che tutto si riduce a una stringa Uri, perché quel tipo complesso ha già la sua chiave primaria dichiarata e ho dichiarato la proprietà come una chiave esterna per quel tipo.

È possibile modellare queste classi in EF senza ridisegnarle radicalmente per guardare più vicino a quali tabelle di database corrispondenti potrebbero essere? Ho lavorato con il database EF6 - prima nel passato, quindi questo è il mio primo tentativo in un pattern code-first, e spero davvero che io possa isolare il disordine che potrebbe sembrare un database solo per la definizione del mio modello, e mantenere classi "pulite" con le quali interagisco in .NET.

Se è necessaria più spiegazione di questi tipi e della loro relazione, basta chiedere: sto tentando di mantenere questo breve.

Risposta accettata

Dubito che questo sia attualmente supportato (incerto se alla fine lo sarà o meno). | Ho provato a ricreare il tuo modello con lievi modifiche e quando provo a creare il database ottengo:

System.NotSupportedException: la proprietà 'PlaylistEntry`1MediaFile' non può essere mappata perché è di tipo 'MediaFile' che al momento non è supportata.

Aggiornamento 1

Penso che il fatto che tu stia inserendo MediaFile come chiave sta creando problemi. Ho apportato alcune modifiche al modello. Spero che questo non rompere nulla di negativo sulla tua fine:

public interface IPlaylistEntry<T>
    where T : IMediaFile
{
    T MediaFile { get; set; }
}

public class PlaylistEntry<T> : IPlaylistEntry<T>
    where T : IMediaFile
{
    public int Id { get; set; }
    public string PlaylistInfo { get; set; } //added for testing purposes
    public T MediaFile { get; set; }
}

Mapping:

protected override void OnModelCreating(ModelBuilder builder)
  {
     builder.ForSqlServer().UseIdentity();

     builder.Entity<MediaFile>().ForRelational().Table("MediaFiles");
     builder.Entity<MediaFile>().Key(e => e.Uri);
     builder.Entity<MediaFile>().Index(e => e.Uri);
     builder.Entity<MediaFile>().Property(e => e.Title).MaxLength(256).Required();

     builder.Entity<PlaylistEntry<MediaFile>>().ForRelational().Table("MediaFileEntries");
     builder.Entity<PlaylistEntry<MediaFile>>().Key(e => e.Id);
     builder.Entity<PlaylistEntry<MediaFile>>().Reference(e => e.MediaFile).InverseReference();
 }

Uso:

  var mediaFile = new MediaFile() {Uri = "irrelevant", Title = ""};
  context.Set<MediaFile>().Add(mediaFile);
  context.SaveChanges();

  context.Set<PlaylistEntry<MediaFile>>().Add(new PlaylistEntry<MediaFile>
  {
      MediaFile = mediaFile,
      PlaylistInfo = "test"
  });

  context.SaveChanges();

Funziona e salva i dati corretti nel database.

Puoi recuperare i dati usando:

  var playlistEntryFromDb = context.Set<PlaylistEntry<MediaFile>>()
         .Include(plemf => plemf.MediaFile).ToList();

Aggiornamento 2

Poiché non vuoi avere un'identità come chiave, puoi aggiungere una proprietà Uri alla classe playlistentry che verrà utilizzata per la relazione tra PlaylistEntry e MediaFile.

public class PlaylistEntry<T> : IPlaylistEntry<T>
    where T : IMediaFile
{
    public string Uri { get; set; }
    public string PlaylistInfo { get; set; }
    public T MediaFile { get; set; }
}

Ecco come apparirà la mappatura in questo caso:

    protected override void OnModelCreating(ModelBuilder builder)
    {
        builder.Entity<MediaFile>().ForRelational().Table("MediaFiles");
        builder.Entity<MediaFile>().Key(e => e.Uri);
        builder.Entity<MediaFile>().Index(e => e.Uri);
        builder.Entity<MediaFile>().Property(e => e.Title).MaxLength(256).Required();

        builder.Entity<PlaylistEntry<MediaFile>>().ForRelational().Table("MediaFileEntries");
        builder.Entity<PlaylistEntry<MediaFile>>().Key(e => e.Uri);
        builder.Entity<PlaylistEntry<MediaFile>>().Reference(e => e.MediaFile).InverseReference().ForeignKey<PlaylistEntry<MediaFile>>(e => e.Uri);
    }

L'utilizzo per inserire dati rimane lo stesso:

 var mediaFile = new MediaFile() { Uri = "irrelevant", Title = "" };
 context.Set<MediaFile>().Add(mediaFile);
 context.SaveChanges();

 context.Set<PlaylistEntry<MediaFile>>().Add(new PlaylistEntry<MediaFile>
 {
     MediaFile = mediaFile,
     PlaylistInfo = "test"
 });

 context.SaveChanges();

Questo codice sopra inserirà "irrilevante" nella proprietà Uri PlaylistEntry poiché viene utilizzato come chiave esterna.

E per recuperare i dati:

    var mediaFiles = context.Set<PlaylistEntry<MediaFile>>().Include(x => x.MediaFile).ToList();

Il join si verificherà sul campo Uri in entrambe le tabelle.




Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché