- Home
- LLBLGen Pro
- LLBLGen Pro Runtime Framework
How can we use the original alias rather than LPA_Rename?
Joined: 24-Nov-2015
We found that LLBLgen pro 4.2 will rename the given alias at runtime, because the rename is not totally correct for some custom entity fields.
Here is an example I create it from a large project. We have 3 business tables that are Member, Account and Branch, and a custom table which is BranchEx. In this Ex table the fields are changing and we can only get the names. The structure of these tables are as below:
Branch(BranchId) Account(AccountId, MemberId, BranchId) Member(MemberId, BranchId) BranchEx(BranchId)
Right now there is a custom field in BranchEx which is [a4cb6a8e-b6b1-4ba2-8845-401722731621], and we are going to execute a sql to get Accounts and order by the custom field of Branch of Account, and then by the custom field of Branch of Member. The implementation with alias is as below:
//Relations
var BranchAccount = new EntityRelation(BranchApiViewFields.BranchId, AccountApiViewFields.BranchId, RelationType.OneToMany) { HintForJoins = JoinHint.Left };
BranchAccount.SetAliases(string.Empty, "A0");
var BranchEx_Account = new EntityRelation(BranchExFields.BranchID, BranchApiViewFields.BranchId, RelationType.OneToOne) { HintForJoins = JoinHint.Left };
BranchEx_Account.SetAliases("A0", "A1");
var MemberAccount = new EntityRelation(MemberApiViewFields.MemberId, AccountApiViewFields.MemberId, RelationType.OneToMany) { HintForJoins = JoinHint.Left };
MemberAccount.SetAliases(string.Empty, "A2");
var BranchMember = new EntityRelation(BranchApiViewFields.BranchId, MemberApiViewFields.BranchId, RelationType.OneToMany) { HintForJoins = JoinHint.Left };
BranchMember.SetAliases("A2", "A3");
var BranchEx_Member = new EntityRelation(BranchExFields.BranchID, BranchApiViewFields.BranchId, RelationType.OneToOne) { HintForJoins = JoinHint.Left };
BranchEx_Member.SetAliases("A3", "A4");
//Fields which will used
var BEx1_InBranchOfAccount = new EntityField2("a4cb6a8e-b6b1-4ba2-8845-401722731621", "BranchEx", typeof(string));
BEx1_InBranchOfAccount.SetObjectAlias("A1");
var BEx2_InBranchOfMember = new EntityField2("a4cb6a8e-b6b1-4ba2-8845-401722731621", "BranchEx", typeof(string));
BEx2_InBranchOfMember.SetObjectAlias("A4");
var bucket = new RelationPredicateBucket();
bucket.Relations.Add(BranchAccount, string.Empty, "A0", JoinHint.Left);
bucket.Relations.Add(BranchEx_Account, "A0", "A1", JoinHint.Left);
bucket.Relations.Add(MemberAccount, string.Empty, "A2", JoinHint.Left);
bucket.Relations.Add(BranchMember, "A2", "A3", JoinHint.Left);
bucket.Relations.Add(BranchEx_Member, "A3", "A4", JoinHint.Left);
var sorting = new SortExpression();
sorting.Add(BEx1_InBranchOfAccount | SortOperator.Descending);
sorting.Add(BEx2_InBranchOfMember | SortOperator.Descending);
And eventually the running sql is:
SELECT *
FROM
(
(
(
(
(
[Clubware].[dbo].[BranchApiView] [LPA_A1] RIGHT JOIN [Clubware].[dbo].[AccountApiView] ON [LPA_A1].[BranchId]=[Clubware].[dbo].[AccountApiView].[BranchId]
)
LEFT JOIN [Clubware].[dbo].[BranchEx] [LPA_A2] ON [LPA_A2].[BranchID]=[LPA_A1].[BranchId]
)
LEFT JOIN [Clubware].[dbo].[MemberApiView] [LPA_A3] ON [LPA_A3].[MemberId]=[Clubware].[dbo].[AccountApiView].[MemberId]
)
LEFT JOIN [Clubware].[dbo].[BranchApiView] [LPA_A4] ON [LPA_A4].[BranchId]=[LPA_A3].[BranchId]
)
LEFT JOIN [Clubware].[dbo].[BranchEx] [LPA_A5] ON [LPA_A5].[BranchID]=[LPA_A4].[BranchId]
)
ORDER BY [A1].[a4cb6a8e-b6b1-4ba2-8845-401722731621] DESC, [A4].[a4cb6a8e-b6b1-4ba2-8845-401722731621] DESC
This sql is incorrect that the A1 and A4 cannot be found in join part. We can see that for those custom fields which do not have ContainingObject (also it cannot be set) will still use the original alias. But for others, the alias are renamed.
We found possible 2 solutions: 1. If there is a switch to disable this kind of renames, that would be perfect. 2. A dodgy way to follow the rule to add alias, like:
//Fields which will used
var BEx1_InBranchOfAccount = new EntityField2("a4cb6a8e-b6b1-4ba2-8845-401722731621", "BranchEx", typeof(string));
BEx1_InBranchOfAccount.SetObjectAlias("LPA_L2");
var BEx2_InBranchOfMember = new EntityField2("a4cb6a8e-b6b1-4ba2-8845-401722731621", "BranchEx", typeof(string));
BEx2_InBranchOfMember.SetObjectAlias("LPA_L5");
So do we have a way to switch of the renames?
Filename | File size | Added on | Approval |
---|---|---|---|
Program.cs | 9,216 | 24-Nov-2015 03:18.18 | Approved |
Could you please try this?
...
var sorting = new SortExpression();
sorting.Add(BEx1_InBranchOfAccount.SetObjectAlias("A1") | SortOperator.Descending);
sorting.Add(BEx2_InBranchOfMember..SetObjectAlias("A4") | SortOperator.Descending);
..
BTW, What is the exact SQL error? Also, What is your exact LLBLGen runtime library version? (http://www.llblgen.com/TinyForum/Messages.aspx?ThreadID=7725)
Joined: 24-Nov-2015
daelmo wrote:
Could you please try this?
... var sorting = new SortExpression(); sorting.Add(BEx1_InBranchOfAccount.SetObjectAlias("A1") | SortOperator.Descending); sorting.Add(BEx2_InBranchOfMember..SetObjectAlias("A4") | SortOperator.Descending); ..
BTW, What is the exact SQL error? Also, What is your exact LLBLGen runtime library version? (http://www.llblgen.com/TinyForum/Messages.aspx?ThreadID=7725)
Hi, Daelmo, we have tried this way to set the alias, and checked the memory that the alias are all set, but problem is not solved. The problem is about the alias of custom created field which will not rename by prefix LPA.
The version is LLBLgen pro 4.2.0.0
SELECT *
FROM
(
(
(
(
(
[Clubware].[dbo].[BranchApiView] [LPA_A1] RIGHT JOIN [Clubware].[dbo].[AccountApiView] ON [LPA_A1].[BranchId]=[Clubware].[dbo].[AccountApiView].[BranchId]
)
LEFT JOIN [Clubware].[dbo].[BranchEx] [LPA_A2] ON [LPA_A2].[BranchID]=[LPA_A1].[BranchId]
)
LEFT JOIN [Clubware].[dbo].[MemberApiView] [LPA_A3] ON [LPA_A3].[MemberId]=[Clubware].[dbo].[AccountApiView].[MemberId]
)
LEFT JOIN [Clubware].[dbo].[BranchApiView] [LPA_A4] ON [LPA_A4].[BranchId]=[LPA_A3].[BranchId]
)
LEFT JOIN [Clubware].[dbo].[BranchEx] [LPA_A5] ON [LPA_A5].[BranchID]=[LPA_A4].[BranchId]
)
ORDER BY [A1].[a4cb6a8e-b6b1-4ba2-8845-401722731621] DESC, [A4].[a4cb6a8e-b6b1-4ba2-8845-401722731621] DESC
You can see that all alias are renamed by prefix LPA except the custom created field that its alias is not renamed. As a result the exception is that cannot find [A1].[a4cb6a8e-b6b1-4ba2-8845-401722731621].
If there can be a way to keep the given alias, which means do not rename them, that would be perfect. Because partially renaming the alias would break the logic.
BTW, I found a property which is ToggleArtificialAliasingForTargetPerEntityRelations in RelationCollection class. I have tried to set it to be true/false before/after I add all relations into bucket. But seems it has no use and no info for it can be found in google.
Joined: 24-Nov-2015
I re-created a very simple example as below, which is select Account table and join Branch table and BranchEx table.
Database is sql server 2012 Product version of SD.LLBLGen.Pro.ORMSupportClasses.dll is 4.2.14.1017 Product version of SD.LLBLGen.Pro.DQE.SqlServer.dll is 4.2.14.1017
static void TinySample2()
{
//Relations
var BranchAccount = new EntityRelation(BranchApiViewFields.BranchId, AccountApiViewFields.BranchId, RelationType.OneToMany) { HintForJoins = JoinHint.Left };
BranchAccount.SetAliases(string.Empty, "A0");
var BranchEx_Account = new EntityRelation(BranchExFields.BranchID, BranchApiViewFields.BranchId, RelationType.OneToOne) { HintForJoins = JoinHint.Left };
BranchEx_Account.SetAliases("A0", "A1");
//Fields which will used
var BEx1_InBranchOfAccount = new EntityField2("a4cb6a8e-b6b1-4ba2-8845-401722731621", "BranchEx", typeof(string));
BEx1_InBranchOfAccount.SetObjectAlias("A1");
var bucket = new RelationPredicateBucket();
bucket.Relations.Add(BranchAccount, string.Empty, "A0", JoinHint.Left);
bucket.Relations.Add(BranchEx_Account, "A0", "A1", JoinHint.Left);
bucket.Relations.ToggleArtificialAliasingForTargetPerEntityRelations(false);
var sorting = new SortExpression();
var sort1 = BEx1_InBranchOfAccount | SortOperator.Descending;
sort1.ObjectAlias = BEx1_InBranchOfAccount.ObjectAlias;
sorting.Add(sort1);
using (var dataAdapter = IOData.NewDataAdapter())
{
var results = new AccountApiViewTypedView();
dataAdapter.FetchTypedView(results.GetFieldsInfo(), results, bucket, 0, sorting, true);
}
}
And the running sql is:
SELECT *
FROM
(
(
[Clubware].[dbo].[BranchApiView] [LPA_A1] RIGHT JOIN [Clubware].[dbo].[AccountApiView] ON [LPA_A1].[BranchId]=[Clubware].[dbo].[AccountApiView].[BranchId]
)
LEFT JOIN [Clubware].[dbo].[BranchEx] [LPA_A2] ON [LPA_A2].[BranchID]=[LPA_A1].[BranchId]
)
ORDER BY [A1].[a4cb6a8e-b6b1-4ba2-8845-401722731621] DESC
Could you please first update to the latest build (download it from the website)? We can then rule out whether it's a bug that's been fixed already or an issue that's still present. The aliases are rewritten to avoid having the same alias on tables in inheritance scenarios.
Joined: 24-Nov-2015
Otis wrote:
Could you please first update to the latest build (download it from the website)? We can then rule out whether it's a bug that's been fixed already or an issue that's still present. The aliases are rewritten to avoid having the same alias on tables in inheritance scenarios.
Hi, Otis, I checked the download page and is version 4.2 the latest one? If so, that is the version of our runtime dlls.
And in the example, there is no inheritance, there are only two simple business table (columns will not change at runtime) and a custom table (dynamic columns changing at runtime)
We did a lot of experiment on setting alias and monitor the output sql. You can see as above that I have figured out the problem which is related with custom created field. Normally the alias of tables and fields will be renamed with LPA prefix. But for the custom created field, will not. I can imagine that the ContaingObjectName property is involved to do alias mapping things, and a custom created field does not have a value of this property. So I'm thinking maybe we can switch off the LPA prefix(the default value can be true, and only set it to be false when needed) and let developer to manage the alias.
Appended: Hi, Otis, I tried the latest version of LLBLgen pro v4.2.15.1112, and the output sql has no change as previous for the example.
AndyWang wrote:
Otis wrote:
Could you please first update to the latest build (download it from the website)? We can then rule out whether it's a bug that's been fixed already or an issue that's still present. The aliases are rewritten to avoid having the same alias on tables in inheritance scenarios.
Hi, Otis, I checked the download page and is version 4.2 the latest one? If so, that is the version of our runtime dlls.
And in the example, there is no inheritance, there are only two simple business table (columns will not change at runtime) and a custom table (dynamic columns changing at runtime)
That doesn't matter, it requests a 'real' alias at query generation at that moment it doesn't know anymore whether it's in an inheritance scenario. That also doesn't matter as it works properly. It's also not the cause of your problem (read on).
We did a lot of experiment on setting alias and monitor the output sql. You can see as above that I have figured out the problem which is related with custom created field. Normally the alias of tables and fields will be renamed with LPA prefix. But for the custom created field, will not. I can imagine that the ContaingObjectName property is involved to do alias mapping things, and a custom created field does not have a value of this property. So I'm thinking maybe we can switch off the LPA prefix(the default value can be true, and only set it to be false when needed) and let developer to manage the alias.
Appended: Hi, Otis, I tried the latest version of LLBLgen pro v4.2.15.1112, and the output sql has no change as previous for the example.
Couple of things: 1) why don't you use the generated relation objects? You then don't need to create EntityRelation objects yourself. Also you don't have to set the join hint on the relation objects, you can do so when you add them to the collection. You actually do that, so you have a lot of code that's not needed. If possible, switch to e.g. QuerySpec or Linq, you then don't need to write the verbose API statements.
You also don't need aliasing if there are no duplicates in the joins.
2) you make a mistake here:
var BranchAccount = new EntityRelation(BranchApiViewFields.BranchId, AccountApiViewFields.BranchId, RelationType.OneToMany) { HintForJoins = JoinHint.Left };
BranchAccount.SetAliases(string.Empty, "A0")
Here you alias BranchApiView with "" and AccountAPiView as "A0".
but on the next lines
var BranchEx_Account = new EntityRelation(BranchExFields.BranchID, BranchApiViewFields.BranchId, RelationType.OneToOne) { HintForJoins = JoinHint.Left };
BranchEx_Account.SetAliases("A0", "A1");
You alias BranchEx as "A0" and BranchApiView as "A1".
Then:
bucket.Relations.Add(BranchAccount, string.Empty, "A0", JoinHint.Left);
bucket.Relations.Add(BranchEx_Account, "A0", "A1", JoinHint.Left);
here, you again state the aliases, which causes a problem, as you now have two entities aliased with the same alias: Both AccountApiView and BranchEx have the alias A0.
Your custom field then states that it uses as object alias 'BranchEx' in the ctor, but that's not an alias you specified at all. You then overwrite that alias with "A1".
But BranchEx has the alias A0, see the second Relations.Add.
So, to fix it:
1) use the generated entity relations 2) don't specify any aliases on the relations unless you join an entity multiple times or you want to target them with custom fields without persistence info 3) specify that alias also for your custom fields
Be aware when specifying aliases that they're for 'start' and 'end' points. So
var BranchAccount = new EntityRelation(BranchApiViewFields.BranchId, AccountApiViewFields.BranchId, RelationType.OneToMany)
means BranchApiView is start, AccountApiView is end. Specifying as aliases then "" and "A0" means BranchApiView gets as alias "" and AccountApiView "A0". Again, aliasing is not needed here, you can suffice with aliasing BranchEx.
I have no idea what the guid named fields are for btw, if these are mapped fields, use those, don't use this construct.
Again, look into our QuerySpec API, even if it's only for the extension methods it brings for writing joins etc. for the low level api
Hope this helps.
Joined: 24-Nov-2015
Hi, Otis, the generated library is used in the project, just because I wish to explain the relations amount the tables which is like: Account (alias is empty string) left join Branch (alias is A0) left join BranchEx (alias is A1) I still think the issue is related with renaming alias, that if a custom field is used in predicate or sorting, its alias will not be renamed in final sql, but other fields will. I think of a way to solve it by switching off the renaming.
Below is some explains 1) The generated entity relations are used, all tables, fields and relations are generated into the library, except for the GUID column. The GUID column is one of the customer defined fields which means we do not know how many and what they are during design time. But we can know their names at runtime. So we do need to target them with custom fields without persistence info
2) We need to specify alias because users may order by some fields of same table multiple times. For example a grid shows the data based on: Account left join Branch left join BranchEx left join Member left join Branch left join BranchEx And then users may order by a field of the first BranchEx, then by a field of the second BranchEx
Here we found the problem that a LPA_renamed alias is given to the BranchEx table, but the alias is not set to the field used in order by, it still use the original one. In fact the renamed alias is not set to the field used in predicate neither.
3) We specified alias to all fields used in order by.
4) For the sequence of alias, I do not know why, but just like the code:
var BranchEx_Of_Branch = new EntityRelation(BranchApiViewFields.BranchId, BranchExFields.BranchID, RelationType.OneToOne);
BranchEx_Of_Branch.SetAliases("A1", "A0");
The final sql shows that A1 maps to BranchEx(later one), and A0 maps to BranchApiView(former one). Please ref the 4 attachments that shows the 4 combinations for different pk-fk and start-end alias. I only add the relations into bucket without any predicate and sorting. Only example 2 and 3 can work.
Filename | File size | Added on | Approval |
---|---|---|---|
alias example.zip | 281,919 | 26-Nov-2015 22:44.15 | Approved |
I had a reply written, but I deleted it, because I think I know what's wrong, and the post didn't cover it. You create the EntityRelation objects manually instead of using the generated ones. In general this is a needlessly verbose option, you're highly recommended to use the generated relation objects, instead of creating the relation objects yourself. In this case you must I think, because you're joining two typed views together, not entities. Typed views aren't designed for this though. If you want to join elements, it's best to join entities, which can be mapped onto database views as well (and marked as readonly in the designer).
Anyway, the problem is: the constructor of EntityRelation() works with PK and FK fields. You specify that the relation you create is a 1:1 relation. The SetAliases and relationCollection.Add() however work with start-entity and end-entity.
If you do:
var r = new EntityRelation(AccountApiViewFields.BranchId, BranchApiViewFields.BranchId, RelationType.OneToOne);
It means AccountApiViewFields.BranchId is the primary key field, and BranchApiViewFields.BranchId is the foreign key field. However 'start entity is pk side' is false by default, so the side you specified as pk side is the 'end side'. (as customer 1:n order is equal to order m:1 customer, so it has to be told which side is the pk side, with relations like 1:n and m:1 this is obvious. For 1:1 it's not ).
This is confusing for the end user, however this is meant to be used by generated code anyway, so it's not a big deal. As you do use the EntityRelation in your code, you have to take this into account: you have to make sure the start entity is the fk side, or you have to specify in a different overload of the constructor that the start entity is the PK side.
I think from this the swapping of the aliases takes place and why example 2 works while you think example 1 should work: the fk side is the start entity by default, not the pk side. You assumed (and I did too as this is a part of the API that's so old I forgot the details about it!) the first entity specified in the constructor is the start entity, which isn't the case, it's the PK entity. As the FK side is the start entity by default, things got swapped and the wrong side gets the alias you specified.
As you're using old-format datatable based typedviews, you can't use the newer API in QuerySpec to join elements more easily or linq. Not sure whether this is a fixed requirement, if you specify to generate the typed views as linq or queryspec poco typed view in the designer, you can use a less verbose query api to specify your queries, joins etc. and still fetch them as a datatable (although not typed).
I hope this clear things up a bit.
Joined: 24-Nov-2015
Otis wrote:
I had a reply written, but I deleted it, because I think I know what's wrong, and the post didn't cover it. You create the EntityRelation objects manually instead of using the generated ones. In general this is a needlessly verbose option, you're highly recommended to use the generated relation objects, instead of creating the relation objects yourself. In this case you must I think, because you're joining two typed views together, not entities. Typed views aren't designed for this though. If you want to join elements, it's best to join entities, which can be mapped onto database views as well (and marked as readonly in the designer).
Anyway, the problem is: the constructor of EntityRelation() works with PK and FK fields. You specify that the relation you create is a 1:1 relation. The SetAliases and relationCollection.Add() however work with start-entity and end-entity.
If you do:
var r = new EntityRelation(AccountApiViewFields.BranchId, BranchApiViewFields.BranchId, RelationType.OneToOne);
It means AccountApiViewFields.BranchId is the primary key field, and BranchApiViewFields.BranchId is the foreign key field. However 'start entity is pk side' is false by default, so the side you specified as pk side is the 'end side'. (as customer 1:n order is equal to order m:1 customer, so it has to be told which side is the pk side, with relations like 1:n and m:1 this is obvious. For 1:1 it's not
).
This is confusing for the end user, however this is meant to be used by generated code anyway, so it's not a big deal. As you do use the EntityRelation in your code, you have to take this into account: you have to make sure the start entity is the fk side, or you have to specify in a different overload of the constructor that the start entity is the PK side.
I think from this the swapping of the aliases takes place and why example 2 works while you think example 1 should work: the fk side is the start entity by default, not the pk side. You assumed (and I did too as this is a part of the API that's so old I forgot the details about it!) the first entity specified in the constructor is the start entity, which isn't the case, it's the PK entity. As the FK side is the start entity by default, things got swapped and the wrong side gets the alias you specified.
As you're using old-format datatable based typedviews, you can't use the newer API in QuerySpec to join elements more easily or linq. Not sure whether this is a fixed requirement, if you specify to generate the typed views as linq or queryspec poco typed view in the designer, you can use a less verbose query api to specify your queries, joins etc. and still fetch them as a datatable (although not typed).
I hope this clear things up a bit.
Hi, Otis
As you said that the pk-fk is not a big problem, I found there is a property StartEntityIsPkSide in EntityRelation class, and setting it to be true can make the pk-fk in order.
Branch_Of_Account.StartEntityIsPkSide = true;
Then the alias is still wrong, please check the attachement.
1) The first example shows that the pk-fk is working by setting StartEntityIsPkSide to be true, and if no sorting is set, then it is working fine.
2) The second example is the first example plus a sorting for the custom-created-field. And the alias is still using the given one (A2), but other alias are all renamed with LPA prefix.
The sql in profiler is as below:
SELECT *
FROM
(
(
[Clubware].[dbo].[AccountApiView] LEFT JOIN [Clubware].[dbo].[BranchApiView] [LPA_A1] ON [Clubware].[dbo].[AccountApiView].[BranchId]=[LPA_A1].[BranchId]
) LEFT JOIN [Clubware].[dbo].[BranchEx] [LPA_A2] ON [LPA_A1].[BranchId]=[LPA_A2].[BranchID]
)
ORDER BY [A2].[a4cb6a8e-b6b1-4ba2-8845-401722731621] DESC
And the error is:
Additional information: An exception was caught during the execution of a retrieval query: The multi-part identifier "A2.a4cb6a8e-b6b1-4ba2-8845-401722731621" could not be bound
3) The third one is with dodgy alias renaming and it can work, but obviously it is dodgy and not safe.
sort1.ObjectAlias = "LPA_A2"; //Dodgy alias renaming
I am thinking of how to make it perfect. Because the custom-created-field does not have the value of ContainingObjectName and neither can be set a value, so it might really hard to put it into renaming list. How about adding a flag and let developer to determine whether LLBL uses the default LPA renaing or keep using the given alias?
Filename | File size | Added on | Approval |
---|---|---|---|
20151201.zip | 279,620 | 30-Nov-2015 22:52.54 | Approved |
ah, we're getting somewhere finally The sort clause is now gives the wrong fragment. This is indeed a problem caused by the fact the field doesn't have a table target specified, as target+objectalias is used to determine what new real alias to assign to it.
You can simply work around this by not specifying an object alias for the field in the sortclause. If you don't specify an object alias it will simply emit [fieldalias] as the field to sort on. If you do specify an object alias for the sort field, it will use that in the output, and will try to find the real alias etc.
As order by fields work on the resultset produced by select (even though they're part of the SELECT statement, they're run after the select has been completed by the DB and before a limit (e.g. TOP) has been executed) it's enough to specify a field alias for the field to sort on, as long as it's part of the projection.
test:
[Test]
public void DynamicJoinSortOfTypedView()
{
var invoicesCustomer = new EntityRelation(CustomerFields.CustomerId, InvoicesFields.CustomerId, RelationType.OneToMany, true, string.Empty);
invoicesCustomer.SetAliases("C", "I");
var invoicesOrder = new EntityRelation(OrderFields.OrderId, InvoicesFields.OrderId, RelationType.OneToMany, true, string.Empty);
invoicesOrder.SetAliases("O", "I");
var sortClause = new EntityField2("Oid", string.Empty, typeof(int)) | SortOperator.Descending;
var sorter = new SortExpression(sortClause);
var bucket = new RelationPredicateBucket();
bucket.Relations.Add(invoicesCustomer);
bucket.Relations.Add(invoicesOrder);
// some fields to fetch, to make sure the field to sort on is realiased.
var fields = new ResultsetFields(2);
fields.DefineField(InvoicesFields.OrderId.SetFieldAlias("Oid").SetObjectAlias("I"), 0);
fields.DefineField(InvoicesFields.CustomerId.SetObjectAlias("I"), 1);
var results = new DataTable();
using(var adapter = new DataAccessAdapter())
{
adapter.FetchTypedView(fields, results, bucket, 0, sorter, true);
}
Assert.AreEqual(2015, results.Rows.Count);
}
Sorry for this hodgepodge experience. It's an old part of our API and in general joining typed views together isn't intended so there's no API part to make that easy. (if the typed views were generated as POCO for linq / queryspec, joining would be much easier though).