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!!!
Let's define the same two tables with one-to-many relation by means of Entity Framework and Symfony Doctrine.
LitDialect references LitCountry.
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; }
}
<?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;
}
}
<?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;
}
}
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();
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();