E1.02 Php Symfony Doctrine vs. Entity Framework. Flat data insertion issue. (Wpf, Xamarin, Angular SPA, Reactjs SPA) - chempkovsky/CS2WPF-and-CS2XAMARIN GitHub Wiki

Important for us difference: Symfony Doctrine does not work with a flat data if the Entity has many-to-one relation.

With Symfony Doctrine before insert current Entity we have to populate all object fields with a correct data, i.e. make select operation for all direct master entities. It is important to emphasize: any additional select operation is a very expensive thing!!!

Here is an explanation with a code samples

Let's define the same two tables with one-to-many relation by means of Entity Framework and Symfony Doctrine.

LitDialect references LitCountry.

Entity Framework: LitCountry, LitDialect and LitDbContext
    public class LitCountry
    {
        //[Key]
        //[Column("Iso3", Order = 0)] // zero-based order of the column the property is mapped to
        [Display(Description = "Country Iso 3 code", Name = "Iso 3 code", Prompt = "Enter Iso 3 code", ShortName = "Iso 3")]
        [StringLength(3, MinimumLength = 3)]
        [Required]
        public string Iso3 { get; set; }

        //[Key]
        //[Column("Iso2", Order = 1)]
        [Display(Description = "Country Iso 3 code", Name = "Iso 2 code", Prompt = "Enter Iso 2 code", ShortName = "Iso 2")]
        [StringLength(3, MinimumLength = 2)]
        [Required]
        public string Iso2 { get; set; }

        [Display(Description = "Country Name (no more than 30 characters in length)", Name = "Country Name", Prompt = "Enter Country Name", ShortName = "Country")]
        [StringLength(40, MinimumLength = 3)]
        [Required]
        public string CountryName { get; set; }

        public List<LitDialect> Dialects { get; set; }
    }

    public class LitDialect
    {

        [Display(Description = "Dialect Id", Name = "Dialect Id", Prompt = "Dialect Id", ShortName = "Id")]
        [StringLength(14, MinimumLength = 5)]
        [Required]
        public string DialectId { get; set; }

        [Display(Description = "Dialect Name (no more than 40 characters in length)", Name = "Dialect Name", Prompt = "Enter Dialect Name", ShortName = "Dialect")]
        [StringLength(52, MinimumLength = 2)]
        [Required]
        public string DialectName { get; set; }

        [Display(Description = "Country Iso 3 code", Name = "Country Iso 3 code", Prompt = "Enter Country Iso 3 code", ShortName = "Country Iso 3")]
        [StringLength(3, MinimumLength = 3)]
        [Required]
        public string Iso3CntrRef { get; set; }

        [Display(Description = "Country Iso 2 code", Name = "Iso 2 code", Prompt = "Enter Country Iso 2 code", ShortName = "Country Iso 2")]
        [StringLength(3, MinimumLength = 2)]
        [Required]
        public string Iso2CntrRef { get; set; }

        public LitCountry Country { get; set; }
    }

    public class LitDbContext : DbContext
    {

        public LitDbContext()
          : base("name=LitConnection")
        {
        }
        public LitDbContext(string ConnectionString)
          : base("name=" + ConnectionString)
        {
        }
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<LitCountry>().HasKey(p => new { p.Iso3, p.Iso2 });
            modelBuilder.Entity<LitDialect>().HasKey(p => p.DialectId);
            modelBuilder.Entity<LitDialect>().HasRequired(d => d.Country)
                .WithMany(m => m.Dialects)
                .HasForeignKey(d => new { d.Iso3CntrRef, d.Iso2CntrRef });
        }
        public DbSet<LitCountry> LitCountryDbSet { get; set; }
        public DbSet<LitDialect> LitDialectDbSet { get; set; }
    }
Symfony Doctrine LitCountry
<?php

namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;

/**
 * LitCountryViewEntity
 *
 * @ORM\Table(name="litcountries")
 * @ORM\Entity
 */
class LitCountryViewEntity
{

    /**
     * @var string
     * @ORM\Column(name="Iso3", type="string"  , length=3, nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="NONE")
     */
    private $iso3;

    /**
     * @var string
     * @ORM\Column(name="Iso2", type="string"  , length=3, nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="NONE")
     */
    private $iso2;

    /**
     * @var string
     * @ORM\Column(name="CountryName", type="string"  , length=40, nullable=false)
     */
    private $countryname;

    public function getIso3(): ?string
    {
        return $this->iso3;
    }
    public function setIso3(string $iso3): self
    {
        $this->iso3 = $iso3;
        return $this;
    }
    public function getIso2(): ?string
    {
        return $this->iso2;
    }
    public function setIso2(string $iso2): self
    {
        $this->iso2 = $iso2;
        return $this;
    }
    public function getCountryname(): ?string
    {
        return $this->countryname;
    }
    public function setCountryname(string $countryname): self
    {
        $this->countryname = $countryname;
        return $this;
    }
}
Symfony Doctrine LitDialect
<?php

namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;

/**
 * LitDialectViewEntity
 *
 * @ORM\Table(name="litdialects")
 * @ORM\Entity
 */
class LitDialectViewEntity
{
    /**
     * @var string
     * @ORM\Column(name="DialectId", type="string"  , length=14, nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="NONE")
     */
    private $dialectid;

    /**
     * @var string
     * @ORM\Column(name="DialectName", type="string"  , length=52, nullable=false)
     */
    private $dialectname;

    /**
     * @var string
     * @ORM\Column(name="Iso3CntrRef", type="string"  , length=3, nullable=false)
     */
    private $iso3cntrref;

    /**
     * @var string
     * @ORM\Column(name="Iso2CntrRef", type="string"  , length=3, nullable=false)
     */
    private $iso2cntrref;

    /**
     * @var LitCountryViewEntity
     * @ORM\ManyToOne(targetEntity="LitCountryViewEntity")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="Iso3CntrRef", referencedColumnName="Iso3", nullable=false),
     *   @ORM\JoinColumn(name="Iso2CntrRef", referencedColumnName="Iso2", nullable=false)
     * })
     */
    private $country;

    public function getDialectid(): ?string
    {
        return $this->dialectid;
    }
    public function setDialectid(string $dialectid): self
    {
        $this->dialectid = $dialectid;
        return $this;
    }
    public function getDialectname(): ?string
    {
        return $this->dialectname;
    }
    public function setDialectname(string $dialectname): self
    {
        $this->dialectname = $dialectname;
        return $this;
    }
    public function getIso3cntrref(): ?string
    {
        return $this->iso3cntrref;
    }
    public function setIso3cntrref(string $iso3cntrref): self
    {
        $this->iso3cntrref = $iso3cntrref;
        return $this;
    }
    public function getIso2cntrref(): ?string
    {
        return $this->iso2cntrref;
    }
    public function setIso2cntrref(string $iso2cntrref): self
    {
        $this->iso2cntrref = $iso2cntrref;
        return $this;
    }
    public function getCountry(): ?LitCountryViewEntity
    {
        return $this->country;
    }
    public function setCountry(?LitCountryViewEntity $country): self
    {
        $this->country = $country;
        return $this;
    }
}

Now we show insert operation using Entity Framework

The most important thing here is that we can work with flat data. We do not populate Country-property before insert. More over Entity Framework do not make internal additional select operation for LitCountry-object right before insert operation !!!!!!!!!!
            LitDialect entityToAdd = new LitDialect();
            entityToAdd.DialectId =  "aa-DJ";
            entityToAdd.DialectName =  "Afar (Djibouti)";
            entityToAdd.Iso3CntrRef =  "DJI";
            entityToAdd.Iso2CntrRef =  "DJ";
            db.LitDialectDbSet.Add(entityToAdd);
            db.SaveChanges();

Now we show insert operation using Doctrine Entity Manager

The code below thows an exception with a message about undefined "$country"-field. I.e. before insert operation we have to select LitCountryViewEntity-object and populate "$country"-field with a data
        $entityNew = new LitDialectViewEntity();
        $entityNew->setDialectid("aa-DJ");
        $entityNew->setDialectname("Afar (Djibouti)");
        $entityNew->setIso3cntrref("DJI");
        $entityNew->setIso2cntrref("DJ");
        $em = $this->getDoctrine()->getManager('litdbcontextmanager');
        $em->persist($entityNew);
        $em->flush();
⚠️ **GitHub.com Fallback** ⚠️