mapping for foreign keys not generated in EF

Posts   
 
    
Jan VBM avatar
Jan VBM
User
Posts: 24
Joined: 03-Apr-2011
# Posted on: 10-Dec-2014 15:51:27   

LLBLGenPro 4.2 Novermber 2014 Entity Framework v6 (Code First) Template SD_EF_CodeFirst_ModelBuilder to generate Persistence.ModelBuilder (V6\C#\codeFirstModelBuilder.lpt) Project Setting: “emit foreign key field” = true

L.S., the Project Setting “emit foreign key field” leads to these FK fields being generated in the Entity / Classes, along with the "NavigatorSingleValue" classes.

However, the mapping in the Persistence.ModelBuilder class is **not **generated for the FK fields, causing a query to the (in this case PostgreSQL) database to be done with the Entity.Attribute name instead of the Table.Column name, for example document._DocumentTypeCode_ instead of document._document_type_code_, i.e. non working code

When I add variable emitForeignKeyFields to the script "SD_EF_CodeFirst_ModelBuilder" as below, it generates mappings and working code.

        foreach(var fieldMapping in mapping.FieldMappings.Where(fm=>(!fm.MappedFieldInstance.IsDiscriminator && ( !fm.MappedFieldInstance.IsForeignKeyField || emitForeignKeyFields )   ) || fm.MappedFieldInstance.IsPartOfIdentifyingFields).OrderBy(fm=>fm.MappedTarget.OrdinalPosition))

is this a bug or am i overlooking some option/possibility which would have prevented me from having to change the template? tnx

Walaa avatar
Walaa
Support Team
Posts: 14946
Joined: 21-Aug-2005
# Posted on: 10-Dec-2014 20:33:10   

Is this a 1:1 relation?

Jan VBM avatar
Jan VBM
User
Posts: 24
Joined: 03-Apr-2011
# Posted on: 11-Dec-2014 06:51:50   

No this is 1:n relation, e.g. Document with a FK to DocumentType.

Jan VBM avatar
Jan VBM
User
Posts: 24
Joined: 03-Apr-2011
# Posted on: 11-Dec-2014 09:11:49   

Postgresql code:

CREATE TABLE expro.document_types
(
  code character varying(15) NOT NULL,
  name character varying(50),
  CONSTRAINT dce_pk PRIMARY KEY (code)
);
CREATE TABLE expro.documents
(
  guid uuid NOT NULL DEFAULT uuid_generate_v4(),
  document_type_code character varying(15) NOT NULL,
  title character varying(50) NOT NULL,
  file_version character varying(10) NOT NULL,
  document_date date,
  keywords text,
  description text,
  CONSTRAINT doc_pk PRIMARY KEY (guid),
  CONSTRAINT doc_document_type_code_fk FOREIGN KEY (document_type_code)
      REFERENCES expro.document_types (code) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
);
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 11-Dec-2014 11:02:49   

Will look into it. It's rather odd though, it's a standard situation which we should have picked up in the tests we have ...

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 11-Dec-2014 11:10:21   

I can't reproduce it: FK fields generate properly:


        /// <summary>Defines the mapping information for the entity 'Crm.Order'</summary>
        /// <param name="config">The configuration to modify.</param>
        protected virtual void MapOrder(EntityTypeConfiguration<Order> config)
        {
            config.ToTable("Order", "Crm");
            config.HasKey(t => t.Id);
            config.Property(t => t.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
            config.Property(t => t.OrderDate).IsRequired();
            config.Property(t => t.ShippingDate);
            config.HasRequired(t => t.Customer).WithMany(t => t.Orders).HasForeignKey(t => t.CustId).WillCascadeOnDelete(false);
            config.HasRequired(t => t.Employee).WithMany(t => t.Orders).HasForeignKey(t => t.EmployeeId).WillCascadeOnDelete(true);
        }

(in our test project with all possible situations)

In the FK side (the Document) in your case, do you have the FK definition being generated? If so, does it contain HasForeignKey() calls? Or does it have .Map() calls, like the code below (same definition as above, but now without FK fields being generated)


        /// <summary>Defines the mapping information for the entity 'Crm.Order'</summary>
        /// <param name="config">The configuration to modify.</param>
        protected virtual void MapOrder(EntityTypeConfiguration<Order> config)
        {
            config.ToTable("Order", "Crm");
            config.HasKey(t => t.Id);
            config.Property(t => t.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
            config.Property(t => t.OrderDate).IsRequired();
            config.Property(t => t.ShippingDate);
            config.HasRequired(t => t.Customer).WithMany(t => t.Orders).Map(m => m.MapKey("CustomerId")).WillCascadeOnDelete(false);
            config.HasRequired(t => t.Employee).WithMany(t => t.Orders).Map(m => m.MapKey("EmployeeId")).WillCascadeOnDelete(true);
        }

Frans Bouma | Lead developer LLBLGen Pro
Jan VBM avatar
Jan VBM
User
Posts: 24
Joined: 03-Apr-2011
# Posted on: 11-Dec-2014 11:16:48   

See below, what we wanted to generate is this one line:

config.Property(t => t.DocumentTypeCode).HasColumnName("document_type_code").HasMaxLength(15).IsRequired();
        protected virtual void MapDocument(EntityTypeConfiguration<Document> config)
        {
            config.ToTable("documents", "expro");
            config.HasKey(t => t.Guid);
            config.Property(t => t.Guid).HasColumnName("guid").HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
        /* this was not generated in original */    config.Property(t => t.DocumentTypeCode).HasColumnName("document_type_code").HasMaxLength(15).IsRequired();
            config.Property(t => t.Title).HasColumnName("title").HasMaxLength(50).IsRequired();
            config.Property(t => t.FileVersion).HasColumnName("file_version").HasMaxLength(10).IsRequired();
            config.Property(t => t.DocumentDate).HasColumnName("document_date");
            config.Property(t => t.Keywords).HasColumnName("keywords");
            config.Property(t => t.Description).HasColumnName("description");
            config.HasRequired(t => t.DocumentType).WithMany(t => t.Documents).HasForeignKey(t => t.DocumentTypeCode).WillCascadeOnDelete(false);
        }
        /// <summary>Defines the mapping information for the entity 'expro.DocumentType'</summary>
        /// <param name="config">The configuration to modify.</param>
        protected virtual void MapDocumentType(EntityTypeConfiguration<DocumentType> config)
        {
            config.ToTable("document_types", "expro");
            config.HasKey(t => t.Code);
            config.Property(t => t.Code).HasColumnName("code").HasMaxLength(15);
            config.Property(t => t.Name).HasColumnName("name").HasMaxLength(50);
        }
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 11-Dec-2014 15:57:57   

Uhoh frowning I see what you mean! That's an embarrassing bug as we should have caught it. If the field has a different target field name, it isn't generated as a field mapping. My code snippet shows it loud and clear too, no 'CustId' mapping present, while it is mapped onto 'CustomerId' !

Will fix!

Frans Bouma | Lead developer LLBLGen Pro
Jan VBM avatar
Jan VBM
User
Posts: 24
Joined: 03-Apr-2011
# Posted on: 11-Dec-2014 16:00:35   

tnx

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 11-Dec-2014 17:02:32   

Tests ran over the wrong folder: code was generated into a different folder than the tests expected them flushed

Ok, your issue: reproduced, fixed other issues found (currently being fixed/already fixed) - project with typed view will leave namespace reference to typedviews in context class; code first doesn't allow typed views (they're ignored), so the namespace shouldn't be there. - a field which has as type a value type gets wrong field names generated as it requires a specific field name specification being present, as EF isn't using the value type field as the target name: config.Property(t => t.AddressVtField.Address).HasMaxLength(60); -> error, should be: config.Property(t => t.AddressVtField.Address).HasColumnName("Address").HasMaxLength(60);

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 11-Dec-2014 17:30:16   

Attached is the fixed model builder template and the fixed context include template. copy as administrator.

copy modelbuilder in: <llblgen pro installation folder>\Frameworks\Entity Framework\Templates\V6\C#

copy context include in: <llblgen pro installation folder>\Frameworks\Entity Framework\Templates\Shared\C#

Hope this helps simple_smile and again sorry for the inconvenience.

Attachments
Filename File size Added on Approval
codeFirstModelBuilder.lpt 15,692 11-Dec-2014 17:33.28 Approved
contextClassInclude.lpt 35,305 11-Dec-2014 17:33.33 Approved
Frans Bouma | Lead developer LLBLGen Pro
Jan VBM avatar
Jan VBM
User
Posts: 24
Joined: 03-Apr-2011
# Posted on: 11-Dec-2014 19:16:12   

I replaced the scripts; the FK attributes are now generated, it works now, thanks!