Stopping LINQ from querying entity sets (and grouping) - linq-to-sql

This is a brief description of my db. You have issues which have categories. I have various queries that get issues based on all sorts of criteria, but that's not important to the question.
I want to be able to take a list of issues I have queried, lets say for example, that occured yesterday and group them by category.
I have a method:
public static IEnumerable<Category> GroupIssuesByCategory(IEnumerable<Issue> issues)
{
return from i in issues
group i by i.Category into c
select c.key
}
Category has a nice mapping which allows it to list Issues within it. That's great for what I want, but in this scenario, it will pull back all the issues in that category, rather than the ones from the set I provided. How do I get around this?
Can I get around this?
I worked out why my original code wasn't compiling and updated the question.
Alas, I still have my main problem.

I'm not sure about the second part of the question, but your compilation problem is that the return type of a grouping is IEnumerable<IGrouping<Category, Issue>>, which I think is what you are looking to return from your method. Also, you don't really need the into c select c bit - that's only useful if you want to do some processing on the result of the grouping to get a different list.
IGrouping<S,T> has a key property which is the Category value, and is IEnumerable<T> to give you the list of Issues in that Category.
Try this as your method:
public static IEnumerable<IGrouping<Category, Issue>> GroupIssuesByCategory(IEnumerable<Issue> issues)
{
return from i in issues
group i by i.Category;
}

Related

Yii2 is there a way to specify tablename in ActiveQuery conditions (like andWhere) in a nice and short way

I make a query (with \yii\db\ActiveQuery) with joins, and some fields in "where" clause become ambigous. Is there a nice and short way to specify the name of the current model`s (ActiveRecord) table (from which one the ActiveQuery was instantiated) before the column name? So I can use this all the time in all cases and to make it short.
Don't like doing smth like this all the time (especially in places where there're no joins, but just to be able to use those methods with joins if it will be needed):
// in the ActiveQuery method initialized from the model with tableName "company"
$this->andWhere(['{{%company}}.`company_id`' => $id]);
To make the "named scopes" to work for some cases with joins..
Also, what does the [[..]] mean in this case, like:
$this->andWhere(['[[company_id]]' => $id]);
Doesn't seem to work like to solve the problem described above.
Thx in advance!
P.S. sorry, don't have enough reputation to create tag yii2-active-query
to get real table name :
Class :
ModelName::getTableSchema()->fullName
Object :
$model::getTableSchema()->fullName
Your problem is a very common one and happens most often with fields liek description, notes and the like.
Solution
Instead of
$this->andWhere(['description'=>$desc]);
you simply write
$this->andWhere(['mytable.description'=>$desc]);
Done! Simply add the table name in front of the field. Both the table name and the field name will be automatically quoted when the raw SQL is created.
Pitfall
The above example solves your problem within query classes. One I struggled over and took me quite some time to solve was a models relations! If you join in other tables during your queries (more than just one) you could also run into this problem because your relation-methods within the model are not qualified.
Example: If you have three tables: student, class, and teacher. Student and teacher probably are in relation with class and both have a FK-field class_id. Now if you go from student via class to teacher ($student->class->teacher). You also get the ambigous-error. The problem here is that you should also qualify your relation definitions within the models!
public function getTeacher()
{
return $this->hasOne(Teacher::className(), ['teacher.id' => 'class.teacher_id']);
}
Proposal
When developing your models and query-classes always fully qualify the fields. You will never ever run into this problem again...that was my experience at least! I actually created my own model-gii-template. So this gets solved automatically now ;)
Hope it helped!

Yii2: ActiveQuery "with" not working

Circumstances
I have three models/db-tables related with 1:n each: An order has multiple commissions and a commission has multiple commission_positions. Therefore the commission_position has an FK-field containing a commission id. The commission itself has an FK-field containing the id of an order.
Order > Commission > CommissionPositions
Problem
What I need to do now is select all the CommissionPositions having a certain value in the related Order-Model. Obvious solution is to use the Query-Object of CommissionPosition which I extended with a named scope. The named scope looks like this:
class CommissionPositionQuery extends ActiveQuery
{
/**
* Named scope to filter positions of a certain alpha order id
* #param integer $id the alpha order id
* #return \common\models\query\CommissionPositionQuery
*/
public function alphaOrderId($id)
{
//TODO: with not working
$this->with(['commission.order']);
$this->andWhere(['alpha_order_id'=>$id]);
return $this;
}
}
The relation commission is defined on the Commission-Model and working. The second relation order is defined on the commission-model and working as well. The filtered field alpha_order_id is in the Order-Table. Now I execute the query like this:
$filteredPositions = CommissionPosition::find()->alphaOrderId(17)->all();
The scope is called successfully and the where-part is used, but when I check the generated SQL I see no join-statements even though I use the with-method to tell yii to fetch the relation together. The response is 'unknown column alpha_order_id' which makes sense as there is no join to the related tables. This is the generated SQL:
SELECT * FROM `commission_position` WHERE (`alpha_order_id`=17)
What am I missing? Is this a bug of Yii2?
Found the soution myself. The naming changes between Yii1 and Yii2 lead to a little confusion. To prevent others from wasting time here the details:
Yii1
In yii 1 you would join in a relation (exemplary: commission) directly like this:
$query->with = 'commission'
$query->together = true;
Yii2 / difference
When calling the with-method like showed in the question the relation was successfully added to the with-array of the ActiveQuery. However, when executing the query, the join part was missing.
Solution
Seems like the with-method is NOT the way to go. Instead I used the method called joinWith with the following signature:
public function joinWith($with, $eagerLoading = true, $joinType = 'LEFT JOIN')
Now as described in the answer I defined the relation as the first argument ('commission.order') and left the rest as is, because the default values are perfectly fine. Pay attention to the default value of the second parameter. this makes sure the relations are joined in directly!
VoilĂ ...the resulting sql contains the needed joins. One pitfall is to be considered though: Ambigious column namings is of course to be handled by ourselves! Link to the documentation of the method:
http://www.yiiframework.com/doc-2.0/yii-db-activequery.html#joinWith()-detail
If you want a JOIN use:
$this->joinWith(['commission.order']);

Business Objects Generating Unexpected Query

I am having problem with BusinessObject Universum and the way it generates queries and consequently yielding the results.
Here is the background: mechanism that is functioning has already been implemented. I was trying to copy the SAME mechanism just to deliver a different field.
Here is the data model: http://tinypic.com/r/ng524g/8
The mechanism that functions is marked with BLUE color. The mechanism that I tried to implement and that is not functioning is marked with RED color.
On business layer I have defined a dimension with aggregate aware function. This function takes first VWF_Party_Collection_A.Collectionstatus_CD column (at the higher level). If a user selects an attribute from contract level, function takes VWF_Contract_Collection_A.Collectionstatus_CD column.
Problem is when I take all attributes from VWD_Kunde_A table and than add the dimension with the mentioned aggregate aware function (ie Collectionstatus_CD), the constructed query from BO side does not make any sense. Here it is:
SELECT
D_ATA_MV_FinanceTreasury.VWF_Party_Collection_A.Collectionstatus_CD,
D_ATA_MV_FinanceTreasury.VWD_Kunde_A.Namespace_TXT,
D_ATA_MV_FinanceTreasury.VWD_Kunde_A.Party_KEY,
D_ATA_MV_FinanceTreasury.VWD_Kunde_A.Legacy_ID
FROM
D_ATA_MV_FinanceTreasury.VWD_Party_A
LEFT JOIN D_ATA_MV_FinanceTreasury.VWF_Party_Collection_A
ON D_ATA_MV_FinanceTreasury.VWD_Party_A.Party_KEY=D_ATA_MV_FinanceTreasury.VWF_Party_Collection_A.Party_KEY,
D_ATA_MV_FinanceTreasury.VWD_Kunde_A
WHERE
(
D_ATA_MV_FinanceTreasury.VWD_Party_A.Party_KEY=D_ATA_MV_FinanceTreasury.VWD_Kunde_A.Party_KEY )
AND
D_ATA_MV_FinanceTreasury.VWD_Kunde_A.Legacy_ID = 102241978
Please notice the strange conctruction in the 'FROM' part (comma has been added). Another strange and unexpected construction is in 'WHERE' part:
( D_ATA_MV_FinanceTreasury.VWD_Party_A.Party_KEY=D_ATA_MV_FinanceTreasury.VWD_Kunde_A.Party_KEY )
The mechanism that is functioning is joining joins VWD_Kunde_A with VWF_Contract_Collection_A table and yields the correct result.
Now, I have tried to define a dimension without the mentioned aggregate aware function that contains only VWF_Contract_Collection_A.Collectionstatus_CD attribute. When I run the same query BO yields CORRECT results and it generates the CORRECT (expected) query.
This is the query I am expecting:
SELECT
D_ATA_MV_FinanceTreasury.VWF_Contract_Collection_A.Collectionstatus_CD,
D_ATA_MV_FinanceTreasury.VWD_Kunde_A.Namespace_TXT,
D_ATA_MV_FinanceTreasury.VWD_Kunde_A.Party_KEY,
D_ATA_MV_FinanceTreasury.VWD_Kunde_A.Legacy_ID
FROM
D_ATA_MV_FinanceTreasury.VWD_Kunde_A LEFT JOIN D_ATA_MV_FinanceTreasury.VWF_Contract_Collection_A ON D_ATA_MV_FinanceTreasury.VWD_Kunde_A.Namespace_TXT = D_ATA_MV_FinanceTreasury.VWF_Contract_Collection_A.Namespace_TXT AND D_ATA_MV_FinanceTreasury.VWD_Kunde_A.Party_KEY = D_ATA_MV_FinanceTreasury.VWF_Contract_Collection_A.Party_KEY AND D_ATA_MV_FinanceTreasury.VWD_Kunde_A.Legacy_ID = D_ATA_MV_FinanceTreasury.VWF_Contract_Collection_A.Legacy_ID
WHERE
D_ATA_MV_FinanceTreasury.VWD_Kunde_A.Legacy_ID = 102241978
Furthermore, I suspected that it can something to do with contexts. However, I did not find any context for the mechanism that already functions and that I tried to copy. Therefore, I did not implement any context for the mechanisam I am tring to implement.
At this point I am clueless since I tried everything I knew. I would appreciate help.
Thanks!
A.
UPDATE: it seems as aggragate aware function is not functioning... This is how it is defined:
#Aggregate_Aware(D_ATA_MV_FinanceTreasury.VWF_Party_Collection_A.Collectionstatus_CD,D_ATA_MV_FinanceTreasury.VWF_Contract_Collection_A.Collectionstatus_CD)
(I just copied the code from Kreditklasse and adapted it... That makes me even more confused...)
UPDATE_2: it really seems as if aggragate aware is not functioning in my case because I selected all attributes from contract_context and it still jumps to party context. Very confused because THE SAME mechasism is functioning as expected when I select Kreditklasse...
Check the aggregate navigation.
Setting up Aggregate Awareness requires two steps (in addition to correctly defining the joins between the tables, of course):
Define the objects with the Aggregate_Aware function
Set table-object incompatibilities through Actions > Set Aggregate Navigation.
It sounds like the second part is not properly configured: make sure that any objects which require the second table are marked incompatible with the first.

LINQ-to-SQL performance question

I am getting an IQueryable from my database and then I am getting another IQueryable from that first one -that is, I am filtering the first one.
My question is -does this affect performance? How many times will the code call the database? Thank you.
Code:
DataContext _dc = new DataContext();
IQueryable offers =
(from o in _dc.Offers
select o);
IQueryable filtered =
(from o in offers
select new { ... } );
return View(filtered);
The code you have given will never call the database since you're never using the results of the query in any code.
IQueryable collections aren't filled until you iterate through them...and you're not iterating through anything in that code sample (ah, the beauty of lazy initialization).
That also means that each of those statements will be executed as its own query against the database which results in no performance cost over doing two completely independent queries.
SO is not a replacement for developer tools. There are many good free tools able to tell you exactly what this code translates into and how it works. Use Reflector on this method and look at what code is generated and reason for yourself what is going on from there.

IQueryable, Where, Guid and

I'm working my way through the MVC Storefront code and trying to follow the path of a repository, a service and a model that is a poco, outside of the dbml/data context. It's pretty easy to follow actually, until I started writing tests and things failed in a way I just don't understand.
In my case, the primary key is a uniqueidentifier instead of an int field. The repository returns an IQueryable:
public IQueryable<Restaurant> All()
{
return from r in _context.Restaurants select new Restaurant(r.Id)
{
Name = r.Name
};
}
In this case, Restaurant is a Models.Restaurant of course, and not _context.Restaurants.Restaurant.
Filtering in the service class (or in repository unit tests) against All(), this works just as expected:
var results = Repository.All().Where(r => r.Name == "BW<3").ToList();
This works just fine, has one Model.Restaurant. Now, if I try the same things with the pkid:
var results = Repository.All().Where(r => r.Id == new Guid("088ec7f4-63e8-4e3a-902f-fc6240df0a4b")).ToList();
If fails with:
The member 'BurningPlate.Models.Restaurant.Id' has no supported translation to SQL.
If seen some similiar posts where people say it's because r => r.Id is [Model.Restaurants] is a class level the linq2sql layer isn't aware of. To me, that means the the first version shouldn't work either. Of course, if my pk is an int, it works just fine.
What's really going on here? Lord knows, it's not very intuitive to have one work and one not work. What am I misunderstanding?
I think the problem here is due to using a constructor overload, and expecting the query to fill it in. When you do a projection like this, you have to put all the things you want to be in the projection query in the actual projection itself. Otherwise Linq won't include that in the SQL query.
So, rewrite your bits like so:
return from r in _context.Restaurants select new Restaurant()
{
Id=r.Id,
Name = r.Name
};
This should fix it up.
Not having actually typed this code out, have you tried
var results = Repository.All().Where(r => r.Id.Equals(new Guid("088ec7f4-63e8-4e3a-902f-fc6240df0a4b")).ToList()
Ninja
this probably has to do with fact that you trying to instantiate the guid in the query, and I think that LINQ to SQL is trying to convert that to actual SQL code before the object is created.
Try instantiating before the query and not on the query.