Ces modèles peuvent-ils être représentés dans EF7?

c# entity-framework-core

Question

J'essaie d'utiliser certaines classes d'un autre assemblage de mon propre projet en tant qu'entités que je peux conserver avec EF7, plutôt que d'écrire une série de classes très similaires et plus conviviales pour les bases de données.

Les versions simplifiées ressemblent à ceci:

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; }
}

Il existe plusieurs implémentations de IMediaFile, je n'en montre qu'une. Ma classe PlaylistEntry utilise un argument générique pour activer différents traits pour ces différentes implémentations, et je travaille simplement avec IPlaylistEntry.

J'ai donc commencé à le modéliser comme suit:

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();

Comme simple test, j’ignore le CPlaylistEntry <> et je fais juste:

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

Cela jette:

NotSupportedException: le 'MediaFile' du type d'entité 'CPlaylistEntry' n'a pas de valeur définie et aucun générateur de valeur n'est disponible pour les propriétés de type 'CMediaFile'. Définissez une valeur pour la propriété avant d'ajouter l'entité ou configurez un générateur de valeur pour les propriétés de type 'CMediaFile'`

Je ne comprends même pas cette exception et je ne vois pas pourquoi CPlaylistEntry apparaît alors que j'essaie seulement de stocker une entité CMediaFile. J'imagine que cela est lié à la définition de mon modèle, c'est-à-dire que la clé primaire de CPlaylistEntry n'est pas un type simple, mais un type complexe, une autre entité. Cependant, je m'attendrais à ce que EF soit suffisamment intelligent pour comprendre que tout se résume à une chaîne Uri, car ce type complexe a déjà sa propre clé primaire déclarée et j'ai déclaré la propriété comme une clé étrangère de ce type.

Est-il possible de modéliser ces classes dans EF sans les redéfinir de manière radicale afin de se rapprocher de ce que pourraient être les tables de base de données correspondantes? J'ai déjà travaillé avec la base de données EF6 dans le passé, c'est donc ma première tentative de création d'un motif d'abord code, et j'espère vraiment pouvoir isoler le fouillis qu'une base de données pourrait ressembler à ma définition de modèle, et conservez les classes "propres" avec lesquelles j'interagis dans .NET.

Si vous avez besoin de plus d'explications sur ces types et leurs relations, demandez simplement: je tente de rester bref.

Réponse acceptée

Doute que ceci est actuellement supporté (ne sachant pas si cela finira par le devenir ou pas). | J'ai essayé de recréer votre modèle avec de légères modifications et lorsque j'essaie de créer la base de données, je reçois:

System.NotSupportedException: la propriété 'PlaylistEntry`1MediaFile' ne peut pas être mappée car elle est du type 'MediaFile' qui n'est actuellement pas supporté.

Mise à jour 1

Je pense que le fait de placer MediaFile comme une clé crée des problèmes. J'ai apporté quelques modifications à votre modèle. J'espère que cela ne cassera rien de négatif de votre côté:

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; }
}

Mappages:

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();
 }

Usage:

  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();

Cela fonctionne et enregistre les données correctes dans la base de données.

Vous pouvez récupérer les données en utilisant:

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

Mise à jour 2

Puisque vous ne souhaitez pas que l'identité soit la clé, vous pouvez ajouter une propriété Uri à votre classe playlistentry qui sera utilisée pour la relation entre PlaylistEntry et 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; }
}

Voici à quoi ressemblerait la cartographie dans ce cas:

    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'utilisation pour insérer des données reste la même:

 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();

Ce code ci-dessus mettra "non pertinent" dans la propriété PlaylistEntry Uri puisqu'il est utilisé comme clé étrangère.

Et pour récupérer des données:

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

La jointure aura lieu sur le champ Uri dans les deux tables.




Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi