I am upgrading a website that used Entity Framework and now uses Linq2Db. I notice Linq2Db has no navigation properties. How would I translate this code from Entity Framework? I need to return an order with a list of order items, shipments, and other related objects. I am unable to query order items, shipments, and the other objects one by one.
IQueryable<Order> query = GetExpandedOrderTable();
DateTime beginTime = settings.LastDownloadUtc;
DateTime endTime = settings.LastDownloadUtcEnd;
query = query.Where(a => a.CreatedOnUtc >= beginTime);
query = query.Where(a => a.CreatedOnUtc <= endTime);
List<int> storeIds = GetStoreIds();
if (storeIds.Count() > 0)
query = query.Where(a => storeIds.Contains(a.StoreId));
return new PagedList<Order>(query, 0, 1000).ToList();
/// <summary>
/// Expands order items and other sub properties.
/// Increases performance.
/// </summary>
/// <returns></returns>
private IQueryable<Order> GetExpandedOrderTable()
{
return orderRepository.Table
.Include(a => a.OrderItems)
.Include("OrderItems.Product")
.Include(a => a.OrderNotes)
.Include(a => a.GiftCardUsageHistory)
.Include(a => a.BillingAddress)
.Include(a => a.BillingAddress.StateProvince)
.Include(a => a.BillingAddress.Country)
.Include(a => a.ShippingAddress)
.Include(a => a.ShippingAddress.StateProvince)
.Include(a => a.ShippingAddress.Country)
.Include(a => a.Customer)
.Include(a => a.DiscountUsageHistory)
.Include(a => a.Shipments);
}`
EF Navigation Property => linq2db Association
EF Eager Loading Include() => linq2db LoadWith()
Related
My program contains several tables. Some of these tables are as follows:
Country table - Product table - ProductCountry (junction table).
When filters are applied on the page of the store (example.com/shop), I want to apply the filter of the selected countries to the result, which goes through various stages (the ID of these countries is a list of integer)
I wrote several ways, all of which have errors.
Thank you for your help
IQueryable<ProductLocalization> result = _context.ProductLocalizations
.Include(c => c.Languages)
.Include(c => c.ProductPropertyLocalizations)
.Include(c => c.Product)
.Include(c => c.Product.UserProducts)
.Include(c => c.Product.Brand)
.Include(c => c.Product.Category)
.Include(c => c.Product.Category.CategoryPropertyLocalizations)
.Where(c => c.LanguageId == languageId);
if (countryId.Count != 0)
{
result = result.Where(c => countryId.Contains(c.Product.CountryProducts.Select(b => b.CountryId));
result = result.Where(c => c.Product.CountryProducts.Any(b => b.CountryId == countryId));
result = result.Where(c => countryId.Any(b => b == c.Product.CountryProducts[0].CountryId)); //for example
result = result.Where(c => c.Product.CountryProducts.Select(c => countryId.Any(c.CountryId)));
result = result.Where(c => c.Product.CountryProducts.FindAll(b => b.CountryId == countryId));
result = result.Where(c => c.Product.CountryProducts.Select(c => c.CountryId).Contains(countryId);
result = result.Where(c => countryId.Any(b => b == c.Product.CountryProducts.Select(a=>a.CountryId)));
}
Try the following filter:
result = result.Where(c =>
c.Product.CountryProducts.Any(b => countryId.Contains(b.CountryId))
);
How can I chain this in EF Core Fluent API, instead of repeating line by line for each property of table:
builder.Entity<FoodWeight>().HasIndex(x => x.NutrientDatabankNumber);
builder.Entity<FoodWeight>().Property(x => x.NutrientDatabankNumber).HasColumnName("NDB_No");
builder.Entity<FoodWeight>().Property(x => x.SequenceNumber).HasColumnName("Seq");
builder.Entity<FoodWeight>().Property(x => x.Amount).HasColumnName("Amount");
builder.Entity<FoodWeight>().Property(x => x.Description).HasColumnName("Msre_Desc");
builder.Entity<FoodWeight>().Property(x => x.GramWeight).HasColumnName("Gm_Wgt");
builder.Entity<FoodWeight>().Property(x => x.GramWeight).HasColumnName("Gm_Wgt");
builder.Entity<FoodWeight>().Property(x => x.NumberOfDataPoints).HasColumnName("Num_Data_Pts");
builder.Entity<FoodWeight>().Property(x => x.StandardDeviation).HasColumnName("Std_Dev");
You can always add an extension method to EntityTypeBuilder, but this doesn't reduce the verbosity greatly:
modelBuilder
.Entity<FoodWeight>()
.Configure(
entity => entity.HasIndex(x => x.NutrientDatabankNumber),
entity => entity.Property(x => x.NutrientDatabankNumber).HasColumnName("NDB_No"),
entity => entity.Property(x => x.SequenceNumber).HasColumnName("Seq")
);
And the extension method:
public static class EntityTypeBuilderExtensions
{
public static void Configure<T>(this EntityTypeBuilder<T> modelBuilder, params Action<EntityTypeBuilder<T>>[] builders) where T : class
{
builders
.ToList()
.ForEach(builder => builder(modelBuilder));
}
}
i have following models in yii2:
use frontend\modules\bewerber\models\Bewerber;
use common\modules\basis\models\base\Person;
use common\modules\lookup\models\LAnrede;
How to create following query using methods of ActiveRecord?
SELECT anrede FROM L_anrede JOIN Person ON L_anrede.id=Person.id_anrede WHERE Person.id IN
(SELECT id_person FROM Bewerber WHERE Bewerber.id_person=1);
P.S.: The last WHERE clause should be not fix but variable like this:
var_dump(LAnrede::findOne([$model->id_person])->anrede)
which will put out following result:Mister or Miss
................................................................
Hint for Fabrizio Caldarelli
................................................................
Ur solution won't help me:=(
This is ur code:
$idPerson = 1;
$show=LAnrede::find()->joinWith(['Person' => function($q) use($idPerson) {
$q->andWhere([
'Person.id' => (new \yii\db\Query())->from('Bewerber')->where(['Bewerber.id_person' => $idPerson])
])->anrede;
}]);
and this is var_dump($show);
E:\xampp\htdocs\yii2_perswitch\frontend\modules\bewerber\views\bewerber\index.php:48:
object(common\modules\lookup\models\LAnredeQuery)[207]
public 'sql' => null
public 'on' => null
public 'joinWith' =>
array (size=1)
0 =>
array (size=3)
0 =>
array (size=1)
...
1 => boolean true
2 => string 'LEFT JOIN' (length=9)
public 'select' => null
public 'selectOption' => null
public 'distinct' => null
public 'from' => null
public 'groupBy' => null
public 'join' => null
public 'having' => null
public 'union' => null
public 'params' =>
array (size=0)
empty
private '_events' (yii\base\Component) =>
array (size=0)
empty
private '_behaviors' (yii\base\Component) =>
array (size=0)
empty
public 'where' => null
public 'limit' => null
public 'offset' => null
public 'orderBy' => null
public 'indexBy' => null
public 'emulateExecution' => boolean false
public 'modelClass' => string 'common\modules\lookup\models\LAnrede' (length=36)
public 'with' => null
public 'asArray' => null
public 'multiple' => null
public 'primaryModel' => null
public 'link' => null
public 'via' => null
public 'inverseOf' => null
I use Gridview like this
$gridColumn = [
[
'attribute' => '',
'label' => Yii::t('app', 'Anrede'),
'format' => 'html',
'value' => function($model) {
return "<p><font color='green'>" . LAnrede::findOne([$model->id_person])->anrede . "</p>";
}
],
];
Colud u show me up how to use ur solution in this context?
This should work:
$idPerson = 1;
LAnrede::find()->joinWith(['Person' => function($q) use($idPerson) {
$q->andWhere([
'Person.id' => (new \yii\db\Query())->from('Bewerber')->where(['Bewerber.id_person' => $idPerson])
]);
}])
->all();
'Person' is a relation in LAnrede model (one or many relation?)
public function getPerson()
{
return $this->hasMany(Person::className(), ['id_anrede' => 'id']);
}
I make a sample around the city, the city has areas, they have complexes, and apartment complexes. In the end, I need to get an apartment for a certain city, indicating the associated filters (district, complex, etc.). At the moment I did this:
public function actionIndex()
{
$cities = Cities::find()->where(['id' => Yii::$app->request->get('city_id')])->with([
'districts' => function ($query){
$query->filterWhere([
'id' => Yii::$app->request->get('district_id'),
]);
},
'districts.complexes' => function ($query) {
$query->filterWhere([
'id' => Yii::$app->request->get('complex_id'),
'type_id' => Yii::$app->request->get('complex_type_id'),
'developer_id' => Yii::$app->request->get('developer_id'),
]);
},
'districts.complexes.apartments' => function ($query) {
$query->filterWhere([
'amount_room' => Yii::$app->request->get('amount_room'),
'yardage' => Yii::$app->request->get('yardage'),
'level' => Yii::$app->request->get('level'),
'price' => Yii::$app->request->get('price'),
]);
},
])->all();
$query = [];
foreach ($cities as $city) {
foreach ($city->districts as $district) {
foreach ($district->complexes as $complex) {
foreach ($complex->apartments as $apartment) {
$query[] = $apartment;
}
}
}
}
return new ArrayDataProvider([
'allModels' => $query,
]);
}
But it looks kind of crooked, maybe I went the wrong way, and can I do it better?
UPD: I did almost like Yasin Patel.
$cities = Cities::find()
//->select('cities.id') // list your attributes comma saperated
->leftJoin('districts','cities.id=districts.city_id') // join districts table
->leftJoin('complex','districts.id=complex.district_id') // join complex table
->leftJoin('apartment','complex.id=apartment.complex_id') // join apartment table
->where(['cities.id' => Yii::$app->request->get('city_id')])
->andWhere(['cities.id' => Yii::$app->request->get('city_id')])
->filterWhere(['districts.id' => Yii::$app->request->get('district_id')])
->filterWhere(['complex.id' => Yii::$app->request->get('complex_id')])
->filterWhere(['complex.type_id' => Yii::$app->request->get('complex_type_id')])
->filterWhere(['complex.developer_id' => Yii::$app->request->get('developer_id')])
->filterWhere(['apartment.amount_room' => Yii::$app->request->get('amount_room')])
->filterWhere(['apartment.yardage' => Yii::$app->request->get('yardage')])
->filterWhere(['apartment.level' => Yii::$app->request->get('level')])
->filterWhere(['apartment.price' => Yii::$app->request->get('price')]);
return new ActiveDataProvider([
'query' => $cities,
]);
Returns:
[
{
"name":"City1",
"region":{
"id":7,
"name":"Region city1."
}
}
]
But how now to choose all the apartments found?
Try this query.
$cities = Cities::find()
->select('city.id') // list your attributes comma saperated
->leftJoin('districts','city.id=districts.city_id') // join districts table
->leftJoin('complexes','districts.id=complexes.districts_id') // join complexes table
->leftJoin('apartments','complexes.id=apartments.complexe_id') // join apartments table
->where(['id' => Yii::$app->request->get('city_id')])
->andWhere(['city.id' => Yii::$app->request->get('district_id')])
->andWhere(['districts.id' => Yii::$app->request->get('district_id')])
->andWhere(['complexes.id' => Yii::$app->request->get('complex_id')])
->andWhere(['complexes.type_id' => Yii::$app->request->get('complex_type_id')])
->andWhere(['complexes.developer_id' => Yii::$app->request->get('developer_id')])
->andWhere(['apartments.amount_room' => Yii::$app->request->get('amount_room')])
->andWhere(['apartments.yardage' => Yii::$app->request->get('yardage')])
->andWhere(['apartments.level' => Yii::$app->request->get('level')])
->andWhere(['apartments.price' => Yii::$app->request->get('price')])
->asArray();
Use your table name and attributes names as needed.
Than use this query in ArrayDataProvider.
return new ArrayDataProvider([
'allModels' => $query,
]);
This will return you records as array.
It turned out to optimize the code in this way::
public function actionIndex()
{
$query = Apartment::find()
->joinWith('complex')
->joinWith('complex.district')
->joinWith('complex.district.city')
->where(['cities.id' => Yii::$app->request->get('city_id')])
->filterWhere(['districts.id' => Yii::$app->request->get('district_id')])
->filterWhere(['complex.id' => Yii::$app->request->get('complex_id')])
->filterWhere(['complex.type_id' => Yii::$app->request->get('complex_type_id')])
->filterWhere(['complex.developer_id' => Yii::$app->request->get('developer_id')])
->filterWhere(['apartment.amount_room' => Yii::$app->request->get('amount_room')])
->filterWhere(['apartment.yardage' => Yii::$app->request->get('yardage')])
->filterWhere(['apartment.level' => Yii::$app->request->get('level')])
->filterWhere(['apartment.price' => Yii::$app->request->get('price')]);
return new ActiveDataProvider([
'query' => $query,
]);
}
Thanks to everyone for their help.
I have the need to create a dropdown field with grouped data:
My form:
class RetailerDetailFilterType extends AbstractType
{
public function getActiveRetailerMetrics(): array
{
return range(25,36);
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('month', EntityType::class,
[
'class' => 'AppBundle:ConsolidatedOperatorCategoryLowData',
'query_builder' => function(ConsolidatedOperatorCategoryLowDataRepository $er){
return $er->getMinMaxByMetricQueryBuilder($this->getActiveRetailerMetrics());
}
]);
}
public function getBlockPrefix()
{
return 'key_metric';
}
}
My Repository:
class ConsolidatedOperatorCategoryLowDataRepository extends \Doctrine\ORM\EntityRepository
{
public function getMinMaxByMetricQueryBuilder($metricRange)
{
$qb = $this->getEntityManager()
->createQueryBuilder('d');
$qb
->select('d.id, YEAR(d.date) as dyear, MONTH(d.date) as dmonth')
->from('AppBundle:ConsolidatedOperatorCategoryLowData','d')
->where($qb->expr()->in('d.metric_id', $metricRange))
->groupBy('dyear')
->addGroupBy('dmonth')
->setMaxResults(10)
;
return $qb;
}
I'm getting
Warning: spl_object_hash() expects parameter 1 to be object, integer given
at UnitOfWork ->isScheduledForInsert (3182005)
in vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php at line 710 +
at EntityManager ->contains (3182005)
in vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php at line 116 +
at IdReader ->getIdValue (3182005)
at call_user_func (array(object(IdReader), 'getIdValue'), 3182005)
in vendor/symfony/symfony/src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php at line 205 +
at ArrayChoiceList ->flatten (array('id' => 3182005, 'dyear' => '2016', 'dmonth' => '12'), array(object(IdReader), 'getIdValue'), array(), array(), null)
in vendor/symfony/symfony/src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php at line 200 +
at ArrayChoiceList ->flatten (array(array('id' => 3182005, 'dyear' => '2016', 'dmonth' => '12'), array('id' => 3186685, 'dyear' => '2017', 'dmonth' => '1'), array('id' => 3191365, 'dyear' => '2017', 'dmonth' => '2'), array('id' => 3195595, 'dyear' => '2017', 'dmonth' => '3'), array('id' => 3200275, 'dyear' => '2017', 'dmonth' => '4')), array(object(IdReader), 'getIdValue'), array(), array(), array(null))
in vendor/symfony/symfony/src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php at line 91
I think it has to do with how Doctrine ORM deals with relationships. When you want to retrieve an entity by a referenced object's id, say find all posts by a user id, then you still have to pass the User-object to the QueryBuilder, not just the id. This is because Doctrine will resolve how those entities are connected itself.
It seems that metric_id actually is a reference to some kind of Metric-entity and just passing an array of int instead of the actual objects seems to trip up Doctrine's QueryBuilder.
You could try mapping the id's to a new instance of Metric and then pass that array instead.
Another solution - the one I would prefer - is to use Native SQL for this.