J'ai limité ce problème à un problème entre Code First et EFD first data, mais je ne sais pas comment le résoudre. Je vais essayer d'être aussi clair que possible, mais honnêtement, je manque une partie de la compréhension ici. Ceci est Entity Framework 4.4
J'ai hérité d'un projet dans lequel Entity Framework était utilisé, mais bon nombre des fichiers réels ont été supprimés sans véritable moyen de revenir en arrière. J'ai ré-ajouté EF (base de données d'abord) et répliqué une configuration T4 autour de laquelle le projet a été construit. Il a généré des versions de code de tous les modèles de base de données et un fichier de code DBContext.
Si ma chaîne de connexion ressemble à une chaîne de connexion .NET "normale", un message d'erreur concernant un nom de colonne non valide "ProcessState_ID" n'existe pas. ProcessState_ID n'est pas du tout dans la base de code, ce n'est pas dans le fichier EDMX ou quoi que ce soit. Cela semble être une conversion EF automatique dans la requête.
Lorsque je fais correspondre la chaîne de connexion au modèle Entity Framework, cela fonctionne correctement.
Maintenant, pour essayer de faire correspondre le code précédent avec Entity Framework, j'aimerais conserver la chaîne de connexion .NET "normale".
J'ai donc deux questions ici: 1. Quel est le bon moyen de passer d’une chaîne de connexion normale à une chaîne de connexion EF dans le code? 2. Y at-il une autre solution ici que je ne vois pas pour arrêter l'erreur de nom de colonne invalide?
Vérifiez si vous avez des ICollections.
Ce que j’ai compris, c’est que lorsque vous avez une ICollection qui référence une table et qu’aucune colonne ne peut la comprendre, elle en crée une pour vous permettre d’essayer de faire le lien entre les tables. Cela se produit spécifiquement avec ICollection et m'a conduit "batty" en essayant de le comprendre.
Ceci est une entrée tardive pour ceux (comme moi) qui n’ont pas compris immédiatement les 2 autres réponses.
Alors...
EF tente de mapper le nom EXPECTED à partir de la référence de table des tables de parents ... et depuis ... le nom de la clé étrangère a été "modifié ou abrégé" dans la relation entre les bases de données CHILD TABLE ... vous recevrez le message ci-dessus.
(ce correctif peut différer selon les versions de EF)
POUR MOI, LA FIXE ÉTAIT:
AJOUTER l'attribut "ForeignKey" au modèle
public partial class Tour
{
public Guid Id { get; set; }
public Guid CategoryId { get; set; }
[Required]
[StringLength(200)]
public string Name { get; set; }
[StringLength(500)]
public string Description { get; set; }
[StringLength(50)]
public string ShortName { get; set; }
[StringLength(500)]
public string TourUrl { get; set; }
[StringLength(500)]
public string ThumbnailUrl { get; set; }
public bool IsActive { get; set; }
[Required]
[StringLength(720)]
public string UpdatedBy { get; set; }
[ForeignKey("CategoryId")]
public virtual TourCategory TourCategory { get; set; }
}
Sainte vache - après de nombreuses heures d’essais, j’ai finalement compris cela.
Je fais d'abord la base de données EF6 et je m'interrogeais à propos de l'erreur "quantité inconnue de la colonne" - elle générait le nom de la colonne de soulignement du nom de la table pour une raison quelconque et tentait de trouver une colonne inexistante.
Dans mon cas, l'une de mes tables avait deux références de clé étrangère à la même clé primaire dans une autre table - quelque chose comme ceci:
Animals Owners
======= ======
AnimalID (PK) Pet1ID <- FK to AnimalID
Pet2ID <- also FK to AnimalID
EF générait un nom de colonne étrange, comme Owners_AnimalID1
et Owners_AnimalID2
, avant de se rompre.
L'astuce ici est que ces clés étrangères déroutantes doivent être enregistrées avec EF à l'aide de l'API Fluent!
Dans le contexte de votre base de données principale, remplacez la méthode OnModelCreating
et modifiez la configuration de l'entité. De préférence, vous aurez un fichier séparé qui étend la classe EntityConfiguration
, mais vous pouvez le faire en ligne.
Quoi que vous fassiez, vous devrez ajouter quelque chose comme ceci:
public class OwnerConfiguration : EntityTypeConfiguration<Owner>
{
public OwnerConfiguration()
{
HasRequired(x => x.Animals)
.WithMany(x => x.Owners) // Or, just .WithMany()
.HasForeignKey(x => x.Pet1ID);
}
}
Et avec cela, EF va (peut-être) commencer à travailler comme vous le souhaitez. Boom.
En outre, vous obtiendrez la même erreur si vous utilisez ce qui précède avec une colonne nullable - utilisez simplement .HasOptional()
au lieu de .HasRequired()
.
Voici le lien qui m'a mis sur la bosse:
Et puis, les documents de l'API Fluent aident, en particulier les exemples de clés étrangères:
http://msdn.Microsoft.com/en-us/data/jj591620.aspx
Vous pouvez également placer les configurations à l’autre extrémité de la clé, comme décrit ici:
Je rencontre actuellement de nouveaux problèmes, mais c’est l’énorme fossé conceptuel qui faisait défaut. J'espère que ça aide!
Hypothèses:
Table
OtherTable
OtherTable_ID
Maintenant, choisissez l’une des méthodes suivantes:
Supprimer ICollection<Table>
Si vous rencontrez une erreur liée à OtherTable_ID
lorsque vous récupérez Table
, accédez à votre modèle OtherTable
et assurez-vous de ne pas avoir ICollection<Table>
dans celle-ci. Sans relation définie, le framework suppose automatiquement que vous devez avoir un FK vers OtherTable et créer ces propriétés supplémentaires dans le SQL généré.
Tout le crédit de cette réponse appartient à @LUKE. La réponse ci-dessus est son commentaire sous @drewid answer. Je pense que son commentaire est si propre, alors je l’ai réécrit comme une réponse.
OtherTableId
à Table
et
OtherTableId
dans la Table
dans la base de données Dans mon cas, je définissais de manière incorrecte une clé primaire composée de deux clés étrangères comme celle-ci:
HasKey(x => x.FooId);
HasKey(x => x.BarId);
HasRequired(x => x.Foo)
.WithMany(y => y.Foos);
HasRequired(x => x.Bar);
L'erreur que je recevais était "nom de colonne invalide Bar_ID".
La spécification de la clé primaire composite corrigeait correctement le problème:
HasKey(x => new { x.FooId, x.BarId });
...
Pour moi, le problème est que la table a été mappée deux fois dans mon application - une fois via Code First, une fois via Database First.
Supprimer l'un ou l'autre résout le problème dans mon cas.
Pour moi, ce comportement est dû à un problème de mappage défini avec l'API Fluent . J'avais 2 types liés, où le type A avait un objet facultatif de type B et le type B de nombreux objets A.
public class A
{
…
public int? BId {get; set;}
public B NavigationToBProperty {get; set;}
}
public class B
{
…
public List<A> ListOfAProperty {get; set;}
}
J'avais défini la cartographie avec des API fluides comme ceci:
A.HasOptional(p=> p.NavigationToBProperty).WithMany().HasForeignKey(key => key.BId);
Mais le problème était que ce type B avait la propriété de navigation List<A>
, donc en conséquence j'ai eu SQLException Invalid column name A_Id
J'ai attaché Visual Studio Debug à EF DatabaseContext.Database.Log pour générer le code SQL généré vers VS Sortie-> Fenêtre de débogage
db.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
Et le SQL généré avait 2 relations de la table B -> une avec l’identifiant correct et l’autre avec le A_Id
Le problème pour le problème était, que je n'ai pas ajouté cette propriété de navigation B.List<A>
dans la cartographie.
Donc, voici comment, dans mon cas, une cartographie correcte devait être:
A.HasOptional(p=> p.NavigationToBProperty).WithMany(x => x.ListOfAProperty).HasForeignKey(key => key.BId);
J'ai également eu ce problème et il semble y avoir plusieurs causes différentes. Pour moi, il s'agissait d'une propriété id définie à tort comme int au lieu de long dans la classe parente qui contenait un objet de navigation. Le champ id de la base de données a été défini comme bigint, ce qui correspond à long en C #. Cela n'a pas provoqué d'erreur de compilation, mais a provoqué la même erreur d'exécution que celle obtenue par l'OP:
// Domain model parent object
public class WidgetConfig
{
public WidgetConfig(long id, int stateId, long? widgetId)
{
Id = id;
StateId = stateId;
WidgetId = widgetId;
}
private WidgetConfig()
{
}
public long Id { get; set; }
public int StateId { get; set; }
// Ensure this type is correct
public long? WidgetId { get; set; }
public virtual Widget Widget { get; set; }
}
// Domain model object
public class Widget
{
public Widget(long id, string name, string description)
{
Id = id;
Name = name;
Description = description;
}
private Widget()
{
}
public long Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
// EF mapping
public class WidgetConfigMap : EntityTypeConfiguration<WidgetConfig>
{
public WidgetConfigMap()
{
HasKey(x => x.Id);
ToTable(nameof(WidgetConfig));
Property(x => x.Id).HasColumnName(nameof(WidgetConfig.Id)).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).IsRequired();
Property(x => x.StateId).HasColumnName(nameof(WidgetConfig.StateId));
Property(x => x.WidgetId).HasColumnName(nameof(WidgetConfig.WidgetId));
}
}
// Service
public class WidgetsService : ServiceBase, IWidgetsService
{
private IWidgetsRepository _repository;
public WidgetsService(IWidgetsRepository repository)
{
_repository = repository;
}
public List<WidgetConfig> ListWithDetails()
{
var list = _repository.ListWithDetails();
return new WidgetConfigMapping().ConvertModelListToDtoList(list).ToList();
}
}
// Repository
public class WidgetsRepository: BaseRepository<WidgetConfig, long>, IWidgetsRepository
{
public WidgetsRepository(Context context)
: base(context, id => widget => widget.Id == id)
{
}
public IEnumerable<WidgetConfig> ListWithDetails()
{
var widgets = Query
.Include(x => x.State)
.Include(x => x.Widget);
return widgets;
}
}
Dans mon cas, la cause de ce problème était l'absence d'une contrainte FOREIGN KEY sur une base de données migrée. Ainsi, l'ICollection virtuel existant n'a pas été chargé avec succès.
Si vous avez plusieurs fois des références de clé étrangère à la même table, vous pouvez utiliser InverseProperty
Quelque chose comme ça-
[InverseProperty("MyID1")]
public virtual ICollection<MyTable> set1 { get; set; }
[InverseProperty("MyID2")]
public virtual ICollection<MyTable> set2 { get; set; }
Dans mon cas, les données de ma méthode d'origine appelaient toujours une colonne de table qui avait été supprimée lors d'une migration précédente. Vérifiez vos correspondances si vous utilisez Automapper.
Pour moi (à l'aide de Visual Studio 2017 et du modèle de base de données d'abord sous Entity Framework 6.1.3), le problème a disparu après le redémarrage de Visual Studio et la reconstruction.