I have 2 related Linq to SQL questions. Please see the image below to see what my Model looks like.
Question 1
I am trying to figure how to eager load the User.AddedByUser field on my User class/table. This field is generated from the relationship on the User.AddedByUserId field. The table is self-referencing, and I am trying to figure out how to get Linq to SQL to load up the User.AddedByUser property eagerly, i.e. whenever any User entity is loaded/fetched, it must also fetch the User.AddedByUser and User.ChangedByUser. However, I understand that this could become a recursive problem...
Update 1.1:
I've tried to use the DataLoadOptions as follows:
var options = new DataLoadOptions();
options.LoadWith<User>(u => u.ChangedByUser);
options.LoadWith<User>(u => u.AddedByUser);
db = new ModelDataContext(connectionString);
db.LoadOptions = options;
But this doesn't work, I get the following exception on Line 2:
System.InvalidOperationException occurred
Message="Cycles not allowed in LoadOptions LoadWith type graph."
Source="System.Data.Linq"
StackTrace:
at System.Data.Linq.DataLoadOptions.ValidateTypeGraphAcyclic()
at System.Data.Linq.DataLoadOptions.Preload(MemberInfo association)
at System.Data.Linq.DataLoadOptions.LoadWith[T](Expression`1 expression)
at i3t.KpCosting.Service.Library.Repositories.UserRepository..ctor(String connectionString) in C:\Development\KP Costing\Trunk\Code\i3t.KpCosting.Service.Library\Repositories\UserRepository.cs:line 15
InnerException:
The exception is quite self-explanatory - the object graph isn't allowed to be Cyclic.
Also, assuming Line 2 didn't throw an exception, I'm pretty sure Line 3 would, since they are duplicate keys.
Update 1.2:
The following doesn't work either (not used in conjuction with Update 1.1 above):
var query = from u in db.Users
select new User()
{
Id = u.Id,
// other fields removed for brevityy
AddedByUser = u.AddedByUser,
ChangedByUser = u.ChangedByUser,
};
return query.ToList();
It throws the following, self-explanatory exception:
System.NotSupportedException occurred
Message="Explicit construction of entity type 'i3t.KpCosting.Shared.Model.User' in query is not allowed."
I am now REALLY at a loss on how to solve this. Please help!
Question 2
On every other table in my DB, and hence Linq to SQL model, I have two fields, Entity.ChangedByUser (linked to Entity.ChangedByUserId foreign key/relationship) and Entity.AddedByUser (linked to Entity.AddedByUserId foreign key/relationship)
How do I get Linq to SQL to eageryly load these fields for me? Do I need to do a simple join on my queries?, or is there some other way?
Linq to SQL eager loading on self referencing table http://img245.imageshack.us/img245/5631/linqtosql.jpg
Any type of cycles just aren't allowed. Since the LoadWith<T> or AssociateWith<T> are applied to every type on the context, there's no internal way to prevent an endless loop. More accurately, it's just confused on how to create the SQL since SQL Server doesn't have CONNECT BY and CTEs are really past what Linq can generate automatically with the provided framework.
The best option available to you is to manually do the 1 level join down to the user table for both of the children and an anonymous type to return them. Sorry it's not a clean/easy solution, but it's really all that's available thus far with Linq.
Maybe you could try taking a step back and seeing what you want to do with the relation? I'm assuming you want to display this information to the user in e.g. "modified by Iain Galloway 8 hours ago".
Could something like the following work? :-
var users = from u in db.Users
select new
{
/* other stuff... */
AddedTimestamp = u.AddedTimestamp,
AddedDescription = u.AddedByUser.FullName,
ChangedTimestamp = u.ChangedTimestamp,
ChangedDescription = u.ChangedByUser.FullName
};
I've used an anonymous type there for (imo) clarity. You could add those properties to your User type if you preferred.
As for your second question, your normal LoadWith(x => x.AddedByUser) etc. should work just fine - although I tend to prefer storing the description string directly in the database - you've got a trade-off between your description updating when ChangedByUser.FullName changes and having to do something complicated and possibly counterintuitive if the ChangedByUser gets deleted (e.g. ON DELETE CASCADE, or dealing with a null ChangedByUser in your code).
Not sure there is a solution to this problem with Linq to Sql. If you are using Sql Server 2005 you could define a (recursive like) Stored Procecdure that uses common table expressions to get the result that you want and then execute that using DataContext.ExecuteQuery.
Related
We are using C#. VS2012, EF 5.0, and Oracle 11g. Approach is code first. I have a table that is defined, and it is plainly visible in looking at the code that it is defined with all the correct columns (and none that are not there.)
Still, when I run certain LINQ queries (joins) and attempt to select the results into a new object, things break. Here is the LINQ:
IQueryable<CheckWage> query =
from clientWage in context.ClientWages
join paycheckWage in context.PaycheckWages
on
new {clientWage.PermanentClientId, clientWage.WageId} equals
new {paycheckWage.PermanentClientId, paycheckWage.WageId}
where
(paycheckWage.PermanentClientId == Session.PermanentClientId) &&
(clientWage.PermanentClientId == Session.PermanentClientId)
select new CheckWage
{
CWage = clientWage,
PWage = paycheckWage
};
Now, here is the SQL it emits (as captured by Devart's DbMonitor tool):
SELECT
"Extent1".ASSOCIATE_NO,
"Extent1".PCLIENT_ID,
"Extent1".CLIENT_NO,
"Extent1".CLIENT_NAME,
"Extent1".ADDRESS1,
"Extent1".ADDRESS2,
"Extent1".CITY,
"Extent1".STATE,
"Extent1".ZIP,
"Extent1".COUNTRY,
"Extent1".CLIENT_TYPE,
"Extent1".DOING_BUSINESS_AS,
"Extent1".CONTACT,
"Extent1".PHONE,
"Extent1".EXTENSION,
"Extent1".FAX,
"Extent1".FAX_EXTENSION,
"Extent1".EMAIL,
"Extent1".NEXTEMP,
"Extent1".PAY_FREQ,
"Extent1".EMPSORT,
"Extent1".DIVUSE,
"Extent1".CLIENT_ACCESS_TYPE,
"Extent1".AUTOPAY_WAGE_ID,
"Extent1".FEIN,
"Extent1".HR_MODULE,
"Extent1".BANK_CODE,
"Extent1".ACH_DAYS,
"Extent1".ACH_COLLECT,
"Extent1".UPDATED,
"Extent1".IAT_FLAG,
"Extent1".ORIG_EMAIL,
"Extent1"."R1",
"Extent1"."R2"
FROM INSTANTPAY.CLIENT "Extent1"
WHERE "Extent1".PCLIENT_ID = :EntityKeyValue1'
There are no such columns as "R1" and "R2." I am guessing is has something to do with the join into a new object type with two properties, but I am pulling my hair out trying to figure out what I've done or haven't done that is resulting in this errant SQL. Naturally, the error from the Oracle server is "ORA-00904: "Extent1"."R2": invalid identifier." Strange that is doesn't choke on R1, but perhaps it only lists the last error or something...
Thanks in advance,
Peter
5/23/2014: I left out an important detail. The SQL is emitted when I attempt to drill into one of the CheckWage objects (using Lazy loading), as both of the contained objects have a navigation property to the "Client" entity. I can access the client table just fine in other LINQ queries that do not use a join, it is only this one that creates the "R1" and "R2" in the SELECT statement.
Peter
we are developing a framework that through the one url, generate a query based on the mapped entities by entity framework.
We are using the Dynamic Library ( http://lcs3.syr.edu/faculty/fawcett/handouts/CoreTechnologies/CSharp/samples/CSharpSamples/LinqSamples/DynamicQuery/Dynamic%20Expressions.html) and we are struggling to return to the fields of a relationship 1..N.
Example:
TB_PEOPLE > TB_PHONE
Based on this relationship, I need to accomplish the same idea of following linq:
var sql = from p in context.SomeTable
select new {
NAME = p.NAME,
PHONES = p.TB_PHONE.Select(ph => ph.PHONE)
};
Since I'm not working with typing, we chose to use dynamic library because apparently allowed us the flexibility to manipulate strings to return.
Then following the same idea , we set up the following line:
var sql = context.SomeTable.Select("new (TB_PEOPLE.TB_PHONE.PHONE)");
In this case , the returns an error stating that the attribute "PHONE" does not exist "TB_PEOPLE" of course ! So much so that we try to say that this attribute belongs to the table "TB_PHONE" but he does not understand.
So I ask you, how do I return to only certain fields of an entity where the relationship can be N? Also tried to call the method "Select":
var sql = context.SomeTable.Select("new (TB_PEOPLE.TB_PHONE.Select(PHONE))");
...there but I am informed that this method can not be used.
I do not know what else to do, any help will be appreciated!
Thank you.
I am using Lightswitch to build my application and I have the following problem.
In my database, I have three tables:
Article
Provider
ArticleProvider
Article and Provider have a many-to-many relation, therefore junction table ArticleProvider is needed.
Now, I want a screen in my application where the user can choose a provider and sees all articles which have a relation to this provider.
Using SQL, I would to it like this (123 is the Provider_Id I want to select).
SELECT *
FROM Article a
WHERE a.Id IN
(SELECT ap.Article_Id FROM ArticleProvider ap WHERE ap.Provider_Id=123)
In my Lightswitch application, I created a Query by clicking on the "Articles" Table in my Datasource and choosed "Add Query". I added a parameter ProviderId and switched to the source code editor to create my custom query:
partial void ArticleByProvider_PreprocessQuery(int? ProviderId,
ref IQueryable<Article> query)
{
...
}
Next I started to create my Linq Query. I think I need an IQueryable<ArticleProvider> Query to filter by them, so I tried:
(from art in query select art.ProviderQuery).AsQueryable<ArticleProvider>()
But, when trying this, I get a compile time error saying that this type can not be converted. So I tried this and it compiles fine:
(from art in query select art.ProviderQuery)
.AsQueryable<IDataServiceQueryable<ArticleProvider>>()
However, when using the returned IQueryable apList in my next query:
from ap in apList where ap.Provider.Id == 123 select ap.Article.Id
It seems that the fields Provider and Article can not be found. Also Visual Studio's code completion does not suggest these fields, only lots of methods and fields which are not in my database.
How can I solve this problem?
I played around with casts and other method calls like ToList(), but I get always stuck at this point. I am new to Linq and C#. Thank you in advance for any help.
EDIT:
I checked the return type of the first query by using:
var temp = (from art in query select art.ProviderQuery).AsQueryable()
The returned type is System.Linq.IQueryable<Microsoft.LightSwitch.IDataServiceQueryable<LightSwitchApplication.ArticleProvider>>
Your problem is that you are in the PreprocessQuery
This is for filtering data further, not adding extra.
If you look around a little this is mentioned a lot.
Give this query a try and see if this works:
partial void ArticleByProvider_PreprocessQuery(int? ProviderId,
ref IQueryable<Article> query)
{
query.Where(art => art.ArticleProviders
.Any(artProv => artProv.Provider.Id == ProviderId));
}
The idea is to get all Articles that at least matches the Provider Id.
Note: Haven't tested this code myself. But the idea should be there.
I believe the answer to this question may be to use Linq to Sql, but wanted to see if this is something which is possible using QueryExpressions:-
I create a query expression which queries against Entity A, it also links to Entity B (via LinkEntity) and imposes additional criteria. It is possible to retrieve columns from Entity B by adding the appropriate attribute names. However, it will only retrieve the linked entity (inner join).
Is it possible using QueryExpression to retrieve all related records (and required columns) from Entity B related to Entity A (e.g. all cases associated with contact where contact passes specified criteria). Normally I would consider inverting the query and searching for Entity B relatig to Entity A with the appropriate LinkEntity Conditions, but there are a number of linked entities which I would like to retrieve for the same contact query.
So I'm left with some options:-
(1) Perform a second query (not ideal when iterating over a large number of results from the initial query),
(2) Perform a query using Linq to CRM on the filtered views,
(3) A different method entirely?
Any thoughts would be appreciated.
EDIT:
I ended up using Linq-to-Sql to complete this task and the code used is similar to that below (albeit with a few more joins for the actual query!):-
var dataCollection = (from eA in xrmServiceContext.EntityASet
join eB in xrmServiceContext.EntityBSet on new EntityReference(EntityA.EntityLogicalName, eA.Id) equals (EntityReference)eB.EntityBLookupToEntityA
select new
{
Id = eA.Id,
EntityBInterestingAttribute = eB.InterestingAttributeName
}
So this will bring back a row per Entity A, per Entity B. To make things easier I then defined a custom class "MyEntityAClass" which had properties which were Lists so I could return one object for filling of GridView etc. This is more to do with the processing of these results though so I haven't posted that code here.
I hope that makes sense. Essentially, it is getting the multiple rows per record a la SQL which makes this method work.
QueryExpression can only return fields from one type of entity, the one specified in QueryExpression.EntityName.
You can use FetchXML which allows you to also get the fields of any link entities, which would be an option 3 for you, unfortunately it returns the data as XML which you would then have to parse yourself.
It might be quicker to run the FetchXML, but it will take longet to write and test, and its not the easiest thing to maintain either.
Sample Code, this gets the first 101 of all Cases that are active for all accounts that are active
string fetch = "<fetch count='101' mapping='logical'><entity name='account'><filter type='and'><condition attribute='statecode' operator='eq' value='1'/></filter><link-entity name='incident' from='customerid' to='accountid'><all-attributes/><filter type='and'><condition attribute='statecode' operator='eq' value='1'/></filter></link-entity></entity></fetch>";
string data = yourCrmServiceObject.Fetch(fetch);
I have a setup on SQL Server 2008. I've got three tables. One has a string identifier as a primary key. The second table holds indices into an attribute table. The third simply holds foreign keys into both tables- so that the attributes themselves aren't held in the first table but are instead referred to. Apparently this is common in database normalization, although it is still insane because I know that, since the key is a string, it would take a maximum of 1 attribute per 30 first table room entries to yield a space benefit, let alone the time and complexity problems.
How can I write a LINQ to SQL query to only return values from the first table, such that they hold only specific attributes, as defined in the list in the second table? I attempted to use a Join or GroupJoin, but apparently SQL Server 2008 cannot use a Tuple as the return value.
"I attempted to use a Join or
GroupJoin, but apparently SQL Server
2008 cannot use a Tuple as the return
value".
You can use anonymous types instead of Tuples which are supported by Linq2SQL.
IE:
from x in source group x by new {x.Field1, x.Field2}
I'm not quite clear what you're asking for. Some code might help. Are you looking for something like this?
var q = from i in ctx.Items
select new
{
i.ItemId,
i.ItemTitle,
Attributes = from map in i.AttributeMaps
select map.Attribute
};
I use this page all the time for figuring out complex linq queries when I know the sql approach I want to use.
VB http://msdn.microsoft.com/en-us/vbasic/bb688085
C# http://msdn.microsoft.com/en-us/vcsharp/aa336746.aspx
If you know how to write the sql query to get the data you want then this will show you how to get the same result translating it into linq syntax.