C# Linq to SQL - IEnumerable Join Two Data Tables/Context - c#

I am using Visual Studio 2010, ASP.NET MVC 3, with a SQL 2008 R2 Database.
This, I believe is a simple task but I don't exactly know how to word it. This is the current code I have.
public static IEnumerable GetAPCBundledCodesData(string CPTCode)
{
var result = (from item in new VADataContext().CPT2MODs
where item.CPT == CPTCode
select new { item.MOD }).ToList();
return result;
}
This pulls a list of items in the CPTSMODs table and returns it. What I would like to do is take that result and get another list with matches to every item with the same value in another table called Modifiers with the same field MOD. I'm not sure how to do this part.
I hope I've explained this well enough. Let me know if there is any clarification needed.
I've continued to look for an answer and found that my problem is I can have two DataContext in on statement. I'm not even sure if this would work if it wasn't two DataContext but I tried doing this:
public static IEnumerable GetAPCBundledCodesData(string CPTCode)
{
var result = (from mod in new VADataContext().MODIFIERs
join cpt2mod in new VADataContext().CPT2MODs on mod.MOD equals cpt2mod.MOD
where cpt2mod.CPT == CPTCode
select new { mod.MOD, mod.SHORT }).ToList();
return result;
}
This resulted in an error saying I can't pull data from two different sources. Is there a different way to handle it?
Here is some sample data to show what is going on here.
Table CPT2MOD
CPT** MOD**
31624 TC
31624 A
31624 DC
99213 B
99213 T
00100 AS
Table MODIFIERs
MOD** SHORT**
TC TC Desc
A A Desc
DC DC Desc
B B Desc
T T Desc
AS AS Desc
A CPT Code will be passed to GetAPCBundledCodesData (ex 99213) and it will look for all MOD values associated with it from the CPT2MOD table (B, T). Then it will return those MOD from the MODIFIERs table along with the description. (B, B Desc; T, T Desc).
I hope you can see what I am asking better now.
Solution
public static IEnumerable GetAPCBundledCodesData(string CPTCode)
{
using (var dc = new VADataContext()){
var result = (from a in dc.CPT2MODs
where a.CPT == CPTCode
join b in dc.MODIFIERs on a.MOD equals b.MOD
select new { b.MOD, b.SHORT }).ToList();
return result;
}
}

Use only one ObjectContext - and use a using (very important):
using(var dc = new VADataContext()){
var result = (from mod in dc.MODIFIERs
join cpt2mod in dc.CPT2MODs on mod.MOD equals cpt2mod.MOD
where cpt2mod.CPT == CPTCode
select new { mod.MOD, mod.SHORT }).ToList();
return result;
}
As an aside - entity framework typically doesn't call the classes it generates DataContext - that's a Linq to SQL thing - are you sure that you're actually using entity framework? It doesn't actually have any bearing on the fix, it's just a request for clarification.

Related

Entity Framework join statement in a function

I'm trying to create a function to grab a List<>() of type "tableContact" which is an entity (sort of like when you do a simple select statement: query.ToList();)
But this is becoming frustrating because I kept getting "null object reference" errors. For some reason "join" statement doesn't work, so I tried an alternate linq statement.
I have an Object2Contacts table that I want to LEFT JOIN and I'm looking up my Object and trying to see all the contacts for this Object. I also can't figure out how to change an "anonymous type" back into an entity table.
Also not every object has contacts, so sometimes null List<> should be returned.
public List<tableContact> getContactsForObject(int oid)
{
if (oid > 0)
{
var query = (from s in entities.tableContacts
from o in entities.tableObject2Contacts
where s.contact_id == o.contact_id
where o.object_id == oid
select new { s });
if (query != null)
{
IEnumerable<tableContact> e = (IEnumerable<tableContact>)query.ToList();
return (List<tableContact>)e;
}
}
return new List<tableContact>();
}
I want to then loop through the object returned... e.g.:
foreach ( tableContact c in MyList){
WriteLine(c.Name);
}
EDIT
I also tried:
List<tableContacts> contacts = (from s in entities.tableContacts
join o in entities.tableObject2Contacts
on s.contact_id equals o.contact_id
where o.object_id == oid
select s).ToList();
Then I can't convert it back into a List and still "null reference".
EDIT 2
Yes the query I am running may definitely bring in an "empty" list. I don't mind empty lists, but it shouldn't give "object null reference."
I would write the join statement a little bit different:
var query = (from s in entities.tableContacts
join o in entities.tableObject2Contacts
on new { ContactID = s.contact_id, ObjectID = oid }
equals new { ContactID = o.contact_id, ObjectID = o.objectID }
into oTemp
from o in oTemp.DefaultIfEmpty()
select new { s });
This would be a proper left-join using linq.
Maybe your NullReferenceException is caused by a wrong join or something.
Nevermind, The "I also tried this" is actually correct code, but for some reason I hadn't established my entities properly so that was returning null.
Although I'm still super confused about "anonymoustypes" I wouldn't know how to make a function that returns "anonymoustype".

How to get last category given a following route alias in a self referencing table

My problem solving like this such a code;
string permalink = "computers/hp/computers"; //example for similarity
List<string> aliases = permalink.Split('/').ToList();
Category cat = db.Categories.SingleOrDefault(c => c.Alias == aliases.First());
aliases.Remove(aliases.First());
foreach (string alias in aliases)
{
cat = cat.Categories.SingleOrDefault(c => c.Alias == alias);
}
return cat;
But this is sent many query..
How do I make one time?
If I understand what you want, you can use the Enumerable.Aggregate method. You will have to start with a 'root' category, that encompasses all of db.Categories. That's pretty easy to mock up though. Try this:
var aliases = permalink.Split('/');
var category = new Category // start with a 'root' category
{
Id = 0,
Categories = db.Categories
};
var cat = aliases.Aggregate(category, (c, a) => c.Categories.SingleOrDefault(x => x.Alias == a));
Firstly, if the category table is small it is sometimes better to just grab the whole table and do the selection in memory (perhaps using p.w.s.g's answer).
If the table is large, then a Stored procedure would probably be better than Linq.
But, if you really want to do it in Linq, then I think the only way is to repeatedly add a join to same table.
The following is assuming that your relationship is between fields called ParentID and Id. I have also changed your string permalink to better illustrate the order.
You first need a little helper class
public class Info
{
public Category category;
public int? recordID;
}
then your main code
string permalink ="computers1/hp/computers2";
var aliases = permalink.Split('/');
var query = dc.Categories.Where(r=>r.Alias == aliases[aliases.Length-1])
.Select(r=> new Info { category = r, recordID = r.ParentID});
for(int i = aliases.Length -2 ; i >= 0; i--)
{
string alias = aliases[i];
query = query.Join(dc.Categories ,
a => a.recordID , b => b.Id , (a,b) => new { a , b} )
.Where(r=>r.b.Alias == alias)
.Select(r=> new Info { category = r.a.category, recordID = r.b.ParentID});
}
return query.SingleOrDefault().category;
As you can see the lambda syntax of join is (IMHO) horrendous and I usually try to avoid it, but I can't think of anyway of avoiding it here.
Since I can't test it, it could be totally wrong (maybe I've mixed up the ID, ParentID or my a's and b's ), so it is important to test this and to test how it performs.
I think the sql produced should be something like
SELECT * from Categories AS t0
INNER JOIN Categories AS t1 ON t0.ParentID = t1.id
INNER JOIN Categories AS t2 ON t1.ParentID = t2.id
WHERE t2.Alias = 'computers1'
AND t1.Alias = 'hp'
AND t0.Alias = 'computers2'
The more sections or aliases, then the more joins there are.
Now that you've see all that, you probably want to avoid using this method -)
I'll probably just add to your confusion :), but let me just throw an idea...
Let me just say it that this doesn't work (exactly per your specs) - and it's not the solution but might help you simplify things a bit.
var query =
(from cat in db.Categories
where cat.Alias == "mainAalias"
from subcat in cat.Categories
where aliases.Contains(subcat.Alias)
orderby subcat.Alias descending
select subcat);
query.FirstOrDefault(); // or something
This should produce one relatively simple query
(e.g. SELECT...FROM...JOIN...WHERE... AND...IN...ORDERBY...).
e.g. if you give it 'cat1', 'cat2'...'cat6' - out of cat1 - to cat100 - it gives 'cat6'...'cat1' (I mean the aliases)
However it has a major 'flaw' with the 'sorting' - your specs require a sort that is the order of 'aliases' as they come - which is a bit unfortunate for queries. If you could somehow enforce, or define an order, that could be translated to SQL this (or similar) might work.
I'm assuming - that your 'aliases' are pre-sorted in an ascending
order - for this query to work. Which they are not, and I'm aware of
that.
But I think that your idea is not clearly defined here (and why all of us are having problems) - think through, and optimize - simplify your requirements - and let your C# tier help e.g. by pre-sorting.
You could also try some form of 'grouping' per cat.Alias etc. - but I think the same 'sorting problem' persists.

CRM LINQ Composite join "The method 'Join' is not supported" error

Im getting a "The method 'Join' is not supported" error... Funny thing is that i simply converted the 1st LINQ into the 2nd version and it doesnt work...
What i wanted to have was LINQ version #3, but it also doesnt work...
This works
var query_join9 = from s in orgSvcContext.CreateQuery(ServiceAppointment.EntityLogicalName)
join b in orgSvcContext.CreateQuery(bh_product.EntityLogicalName)
on s["bh_contract"] equals b["bh_contract"]
where ((EntityReference)s["bh_contract"]).Id == Guid.Parse("09BDD5A9-BBAF-E111-A06E-0050568B1372")
select new
{
Events = s,
Products = b
};
This doesn't
var query_join9 = from s in orgSvcContext.CreateQuery(ServiceAppointment.EntityLogicalName)
join b in orgSvcContext.CreateQuery(bh_product.EntityLogicalName)
on new { contractid = s["bh_contract"] }
equals new { contractid = b["bh_contract"] }
where ((EntityReference)s["bh_contract"]).Id == Guid.Parse("09BDD5A9-BBAF-E111-A06E-0050568B1372")
select new
{
Events = s,
Products = b
};
Also, this doesn't, which is a composite join and what i really aim for
var query_join9 = from s in orgSvcContext.CreateQuery(ServiceAppointment.EntityLogicalName)
join b in orgSvcContext.CreateQuery(bh_product.EntityLogicalName)
on new { contractid = s["bh_contract"], serviceid = s["serviceid"] }
equals new { contractid = b["bh_contract"], serviceid = s["serviceid"] }
where ((EntityReference)s["bh_contract"]).Id == Guid.Parse("09BDD5A9-BBAF-E111-A06E-0050568B1372")
select new
{
Events = s,
Products = b
};
I tried early binding and still doesnt work...
var query_join9 = from s in orgSvcContext.CreateQuery<ServiceAppointment>()
join b in orgSvcContext.CreateQuery<bh_product>()
on new { foo = s.bh_contract.Id }
equals new { foo = b.bh_Contract.Id }
where s.bh_contract.Id == Guid.Parse("09BDD5A9-BBAF-E111-A06E-0050568B1372")
select new
{
Events = s,
Products = b
};
stil not working
var query_join9 = from s in orgSvcContext.CreateQuery<ServiceAppointment>()
join b in orgSvcContext.CreateQuery<bh_product>()
on new { s.bh_contract.Id, s.ServiceId }
equals new { b.bh_Contract.Id, ServiceId = b.bh_Service }
where s.bh_contract.Id == Guid.Parse("09BDD5A9-BBAF-E111-A06E-0050568B1372")
select new
{
Events = s,
Products = b
};
But im simply trying to do the example(s) here How to do joins in LINQ on multiple fields in single join
What am i missing?
Thanks in advance
While I'm not entirely sure which CRM you're using, I think you're misunderstanding something.
In order for a LINQ query to work, there needs to be a LINQ provider for the underlying data source -- the bit of code responsible for translating chain of e.g. Join, Where, operator usage, etc, etc, into the query API of the data source. This might be SQL, some custom query language, or some chain of methods.
Two LINQ providers (such as, one for LINQ to DataSet and some custom provider you've written yourself) don't have to support the same methods and other code. The precise subset of LINQ methods (and/or other embedded statements) a LINQ provider supports is dependent on its implementation.
Looking at it like that, it's not that surprising that the LINQ provider you're using doesn't seem to comprehend the standard syntax for joins using multiple fields, or doesn't seem to comprehend the usage of anonymous types at all.
My advice is to search the documentation of the supplied LINQ provider to see which query operations it supports (perhaps there is a note about this specific mode of query not being supported). Failing that, you'll have to resort to some sort of other query -- one not involving an equijoin. Perhaps your best option is to perform the joins separately, and then intersect the two result groups. It really depends on the specifics of the case.
Have you looked at the MSDN samples. There are some multiple-column join examples there:
using (ServiceContext svcContext = new ServiceContext(_serviceProxy))
{
var list_join = (from a in svcContext.AccountSet
join c in svcContext.ContactSet
on a.PrimaryContactId.Id equals c.ContactId
where a.Name == "Contoso Ltd" && <<--- multiple join here
a.Address1_Name == "Contoso Pharmaceuticals"
select a).ToList();
foreach (var c in list_join)
{
System.Console.WriteLine("Account " + list_join[0].Name
+ " and it's primary contact "
+ list_join[0].PrimaryContactId.Id);
}
}
This other thread might be relevant

EF using LINQ trying Contains In passing in a Array

Is there a better way to write this CONTAINS from an ARRAY?
I am passing a string/array into my LINQ statement. In SQL the code that is working is
SELECT * FROM dbo.option1
WHERE option1Code IN ('9841','V237','SV02','2057')
In EF using LINQ I am trying
using (var ctx = new ProductEntities())
{
//--------------------------------------------------------------------//
string csvSKU = '984,237,102,207';
string[] mArray = csvSKU.Split(',');
var results = (from o in ctx.option1
join p in ctx.Products on o.option1Code equals p.productSKU
where mArray.Contains(o.option1Code)
orderby o.option1Sort
select o).Distinct().ToList();
return results;
}
This looks perfectly fine and should result in similar SQL as you posted (at least for the Contains part).
Also not really sure what your join is for right now - are there any option codes that have no entry in the Products table / productSKU?

Simulating Cross Context Joins--LINQ/C#

Here's the issue:
I have 2 data contexts that I would like to do a join on. Now I know that LINQ doesn't allow joins from one context to another, and I know that 2 possible solutions would be to either create a single datacontext or to have 2 seperate queries (which is what I'm doing for now). However what I would like to do is to "simulate" a join.
Here's what I've tried.
using (var _baseDataContext = Instance)
{
var query = from a in _baseDataContext.Account.ACCOUNTs
where a.STR_ACCOUNT_NUMBER.ToString() == accountID
join app in _baseDataContext.Account.APPLICATIONs on a.GUID_ACCOUNT_ID equals
app.GUID_ACCOUNT
join l in GetLoans() on app.GUID_APPLICATION equals l.GUID_APPLICATION
select l.GUID_LOAN;
return query.Count() > 0 ? query.First() : Guid.Empty;
}
private static IQueryable<LOAN> GetLoans()
{
using (var _baseDataContext = Instance)
{
return (from l in _baseDataContext.Loan.LOANs
select l).AsQueryable();
}
}
In run time I get is
System.InvalidOperationException: The query contains references to items defined on a different data context
EDIT:
Working Solution:
using (var _baseDataContext = Instance)
{
var query = from a in _baseDataContext.Account.ACCOUNTs
where a.STR_ACCOUNT_NUMBER.ToString() == accountID
join app in _baseDataContext.Account.APPLICATIONs on a.GUID_ACCOUNT_ID equals
app.GUID_ACCOUNT
join l in GetLoans() on app.GUID_APPLICATION equals l.GUID_APPLICATION
select l.GUID_LOAN;
return (query.Count() > 0) ? query.First() : Guid.Empty;
}
private static IEnumerable<LOAN> GetLoans()
{
using (var _baseDataContext = Instance)
{
return (from l in _baseDataContext.Loan.LOANs
select l).AsQueryable();
}
}
Maybe something like this can get you started in the right direction. I made a mock database with similar columns based on your column names and got some results.
class Program
{
static AccountContextDataContext aContext = new AccountContextDataContext(#"Data Source=;Initial Catalog=;Integrated Security=True");
static LoanContextDataContext lContext = new LoanContextDataContext(#"Data Source=;Initial Catalog=;Integrated Security=True");
static void Main()
{
var query = from a in aContext.ACCOUNTs
join app in aContext.APPLICATIONs on a.GUID_ACCOUNT_ID equals app.GUID_ACCOUNT
where app.GUID_APPLICATION.ToString() == "24551D72-D4C2-428B-84BA-5837A25D8CF6"
select GetLoans(app.GUID_APPLICATION);
IEnumerable<LOAN> loan = query.First();
foreach (LOAN enumerable in loan)
{
Console.WriteLine(enumerable.GUID_LOAN);
}
Console.ReadLine();
}
private static IEnumerable<LOAN> GetLoans(Guid applicationGuid)
{
return (from l in lContext.LOANs where l.GUID_APPLICATION == applicationGuid select l).AsQueryable();
}
}
Hope this helps!
This is the "work around" that we have found...
We built our tables from the other database out manually and if it is on the same server then we prefixed the table name with:
<DatabaseName>.<SchemaName>.<YourTableName>
if they are on a linked server then you have to prefix it with the server name as well:
<ServerName>.<DatabaseName>.<SchemaName>.<YourTableName>
This will allow you to do joins and still return an non executed IQueryable... which is what we wanted. The other 2 ways in involve joining in-memory IEnumerables which means your pull all records for each before doing the join (above) and doing an IQueryable join using a contains method which has limitations...
Hopefully in the future the DataContext will be built smart enough to know that if the servers are linked then you can do joins between two different ones.
I favor creating a separate data context that contains just the two tables you want to join. But I suppose you could maintain a temporary table (containing data from the first context) in the second context, and then join the temporary table.
You could create a view in database B that represents the table in database A. Then in context B model that view so you can join tables
Create View vw_Stuff AS
Select prop1,
prop2
from otherDb..Stuff

Categories

Resources