Join List Data from two separate Data Contexts - c#

I have been searching for the solution to this problem and this is what I have so far:
var ProductInfo = (from p in twd.Products
orderby p.PC
where p.DELMARK == active
select p).AsEnumerable();
var BuyersData =
(from x in db.MinimumProductInfo
where x != null
orderby x.ItemCode, x.Region
let pnote =
(from pn in db.ProductNotes
where pn != null
where x.MinimumProductInfoID == pn.MinimumProductInfoID
&& pn.NoteTypeFlag == "p"
orderby pn.NoteDate descending
select pn).FirstOrDefault()
let cnote =
(from c in db.ProductNotes
where c != null
where x.MinimumProductInfoID == c.MinimumProductInfoID
&& c.NoteTypeFlag == "c"
orderby c.NoteDate descending
select c).FirstOrDefault()
let product =
(from p in ProductInfo
where x.ItemCode == p.PC
select p).FirstOrDefault()
select new ProductInfoWithNoteList
{
MinimumProductInfoID = x.MinimumProductInfoID,
ItemCode = x.ItemCode,
EquivCode = x.EquivCode,
Description = product.PDESC,
MinimumOnHandQuantity = x.MinimumOnHandQuantity,
MaximumOHandQuantity = x.MaximumOHandQuantity,
MinimumOrderQuantity = x.MinimumOrderQuantity,
LeadTimeInWeeks = x.LeadTimeInWeeks,
Region = x.Region,
Comment = cnote.ItemNote,
PermanentNote = pnote.ItemNote
}).ToArray();
It looks correct but I am getting an error,
'The specified LINQ expression contains references to queries that are
associated with different contexts.'
What this code is supposed to do is pull out all the active product codes from the first table using the twd datacontext then use data from that database in the db.MinimumProductInfo table. The reason they have 2 separate data contexts are they are completely different databases, the first is our ERP and the second is one that we are building in house.
What am I missing? I know it is possible to do this by separating the two datacontexts then adding them together because I have seen it done with single instances but I cannot find how to do it with list data.

Instead of this:
let product =
(from p in ProductInfo
where x.ItemCode == p.PC
select p).FirstOrDefault()
select new ProductInfoWithNoteList
{
MinimumProductInfoID = x.MinimumProductInfoID,
ItemCode = x.ItemCode,
EquivCode = x.EquivCode,
Description = product.PDESC,
MinimumOnHandQuantity = x.MinimumOnHandQuantity,
MaximumOHandQuantity = x.MaximumOHandQuantity,
MinimumOrderQuantity = x.MinimumOrderQuantity,
LeadTimeInWeeks = x.LeadTimeInWeeks,
Region = x.Region,
Comment = cnote.ItemNote,
PermanentNote = pnote.ItemNote
}).ToArray();
Try this by removing the let product clause and not filling the properties associated with ProductInfo because we will do that afterwards (See I have commented out the Description property):
select new ProductInfoWithNoteList
{
MinimumProductInfoID = x.MinimumProductInfoID,
ItemCode = x.ItemCode,
EquivCode = x.EquivCode,
//Description = product.PDESC,
MinimumOnHandQuantity = x.MinimumOnHandQuantity,
MaximumOHandQuantity = x.MaximumOHandQuantity,
MinimumOrderQuantity = x.MinimumOrderQuantity,
LeadTimeInWeeks = x.LeadTimeInWeeks,
Region = x.Region,
Comment = cnote.ItemNote,
PermanentNote = pnote.ItemNote
}).ToArray();
Now that you have your BuyersData and ProductInfo in memory, set the Description property or all the items in BuyersData:
foreach(var thisBuyerData in BuyersData)
{
var thisPi = ProductInfo.SingleOrDefault(x => x.PC == thisBuyerData.ItemCode);
thisBuyerData.Description = thisPi?.PDESC;
}

Related

GridView Only populating 1 result

I'm currently working to add Data to a GridView. The data comes from 2 tables that are on different databases. Currently I am able to populate the first entry, but it does not populate past that. here is the code:
void FillOrder(int inv)
{
var _ord = new OrdersContext();
var _pro = new ProductContext();
var qryOrder = (from o in _ord.OrderDetails
where o.InvNumberId == inv
select new
{
o.ProductID,
o.Quantity
}).ToList();
foreach (var order in qryOrder)
{
int prodID = order.ProductID;
int itemCount = qryOrder.Count;
var qryProducts = (from p in _pro.Products
where p.ProductID == prodID
select new
{
p.ProductID,
p.ProductName
}).ToList();
var results = (from t in qryOrder
join s in qryProducts
on t.ProductID equals prodID
select new
{
t.ProductID,
t.Quantity,
s.ProductName
}).ToList();
OrderItemList.DataSource = results;
OrderItemList.DataBind();
}
}
Can anyone help as to why it's only populating the first entry?
If the number of products involved is relatively small, (and since this query seems to be relate to one invoice, I would think that is true), then you can probably use something like the code below.
This is removing the loop, but the contains method will probably generate a SQL statement something like select ProductID, ProductName from products where productID in (,,,,,,) so may fail if the number of parameters is extremely large.
var _ord = new OrdersContext();
var _pro = new ProductContext();
var qryOrder = (from o in _ord.OrderDetails
where o.InvNumberId == inv
select new
{
o.ProductID,
o.Quantity
}).ToList();
// Get the productIDs
var productIDS = qryOrder.Select(o=>o.ProductID).Distinct().ToList();
// Get the details of the products used.
var qryProducts = (from p in _pro.Products
where productIDS.Contains(p.ProductID)
select new
{
p.ProductID,
p.ProductName
}).ToList();
// Combine the two in memory lists
var results = (from t in qryOrder
join s in qryProducts
on t.ProductID equals s.ProductID
select new
{
t.ProductID,
t.Quantity,
s.ProductName
}).ToList();
OrderItemList.DataSource = results;
OrderItemList.DataBind();

Grouping by ID and displaying the top 1 with Linq

Looking for a bit of advice on grouping with entity framework and linq.
So i have a table "tbl_ChatLog" that contains userID and sendToUserID etc...and with this data i'm trying to display the "Top 1" from each "SendToUserID"
So in my UI it would look like so:
1001 (Contains multiple but show the top 1)
1003 (Contains multiple but show the top 1)
1008 (Contains multiple but show the top 1)
1009 (Contains multiple but show the top 1)
The start of my code below:
public static List<Chat> getChatMessage()
{
var entities = new FreeEntities();
//My ID
Business.User user = Business.User.getUserBySecurityToken();
List<Chat> chatMessages =
(
from cm in entities.tbl_ChatLog
where cm.UserID == user.uid
select new Chat
{
uid = (int)cm.UserID,
sendToUserID = (int)cm.SendToUserID,
message = cm.Message, dateAdded = (DateTime)cm.DateAdded
}
).OrderByDescending(x => x.dateAdded).ToList();
return chatMessages;
}
Hoping you can help me out on this one. Grouping always seems to throw me.
Much appreciated,
Terry
You could use groupby:
List<Chat> chatMessages =
(from cm in entities.tbl_ChatLog
where cm.UserID == user.uid
orderby cm.DateAdded descending
group cm by cm.sendToUserID into grp
select new Chat
{
uid = grp.FirstOrDefault().UserID,
sendToUserID = grp.Key,
message = grp.FirstOrDefault().Message,
dateAdded = grp.FirstOrDefault().DateAdded
}).ToList()
This will get you a list of your table data grouped by sendToUserID and the first entry of each group containing every property including sendToUserID.
The original problem was trying to select the first message from each unique "SendToUserId" and grouping by UserId and then selecting into a DTO was a nightmare but i managed to get a simple work around. Code below:
getFirstChatMessage()
{
var entities = new FreeEntities();
//User ID
Business.User user = Business.User.getUserBySecurityToken();
// Create new list of Chat
List<Chat> chatList = new List<Chat>();
var res = from c in entities.tbl_ChatLog
where c.UserID == user.uid
group c by c.UserID
into groups
select groups.OrderByDescending(p => p.DateAdded).FirstOrDefault();
foreach (var r in res)
{
chatList.Add(new Chat()
{
uid = (int)r.UserID,
sendToUserID = (int)r.SendToUserID,
message = (from m in entities.tbl_ChatLog where m.UserID == (int)r.SendToUserID
orderby r.DateAdded descending select m.Message).FirstOrDefault(),
dateAdded = (DateTime)r.DateAdded,
fullName = (from b in entities.tbl_Bio where b.UserID == (int)r.SendToUserID
select b.FirstName + " " + b.SurName).FirstOrDefault()
});
}
return chatList;
}

Count in select query of Linq to SQL

I am trying to convert below SQL query into Linq.
select eg.userid, eg.groupid, count(r.RID) as RecipientCount
from eg
join g on eg.groupid = g.groupid
join r on g.RID = r.RID
where eg.UserId = '7F813844-3B93-418E-8141-654082C4E37D'
and eg.IsDeleted = 0
and r.Isdeleted = 0
group by eg.groupid
Above query runs properly in SQL.
My Linq code is:
var v = from eml in dc.egs
join recpingroup in dc.g on eml.GroupID equals recpingroup.GroupID
where eml.aspnet_User.LoweredUserName.Equals(strUserName.ToLower())
&& !eml.IsDeleted
&& !recpingroup.r.IsDeleted
select new Info()
{
CreateDt = eml.CreateDt.ToShortDateString(),
UserId = eml.UserId.ToString(),
LastUpdateDt = eml.LastUpdateDt.ToShortDateString(),
Username = eml.aspnet_User.UserName,
GroupDescription = eml.GroupDescription,
GroupID = eml.GroupID.ToString().ToUpper(),
GroupName = eml.GroupName,
Count = dc.g.Count(r1 => r1.GroupID.Equals(eml.GroupID) && !r1.r.IsDeleted)
};
where dc is my DataContext.
But I am having problems in the last property i.e. Count is coming wrong. I want the counts of recipients from recpingroup.r as RecipientCount.
Also note that tables are linked in SQL internally by PK and FK references.
try following
Count = (from x in dc.g where(r1 => r1.GroupID.Equals(eml.GroupID) && !r1.r.IsDeleted) select x.g).count()

associated data in linq select

I need to select data from different tables associated, I have a hospital table, another specialty, and other services, and what is held to select all hospitals and their specialties and services, there is a way to do this in linq?
this is what I'm trying
var hospitalesQuery = from hospital in App.ViewModel.ChristusDB.Hospitales
where hospital.id_tipo == "2" && hospital.christus == "1"
orderby hospital.distancia ascending
from e in App.ViewModel.ChristusDB.EspHosp
where e.fk_hospital==hospital.pk_hospital
from s in App.ViewModel.ChristusDB.ServHosp
where s.fk_hospital==hospital.pk_hospital
select new Hospitale()
{
pk_hospital = hospital.pk_hospital,
nombre = hospital.nombre,
direccion = hospital.direccion,
ciudad = hospital.ciudad,
id_ciudad = hospital.id_ciudad,
estado = hospital.estado,
id_estado = hospital.id_estado,
id_tipo = hospital.id_tipo,
tipo_descripcion = hospital.tipo_descripcion,
imagen = hospital.imagen,
gps_lat = hospital.gps_lat,
gps_lng = hospital.gps_lng,
abierto_lv = hospital.abierto_lv,
abierto_sd = hospital.abierto_sd,
telefono_1 = hospital.telefono_1,
telefono_2 = hospital.telefono_2,
telefono_3 = hospital.telefono_3,
telefono_4 = hospital.telefono_4,
telefono_5 = hospital.telefono_5,
inactivo = hospital.inactivo,
christus = hospital.christus,
especialidades= new List<Especialidade>(e),
servicios= new List<Servicio>(s)
};
You are looking for joining tables by a foreign key. LINQ (asn SQL as well) provide join for this:
from hospital in App.ViewModel.ChristusDB.Hospitales
where hospital.id_tipo == "2" && hospital.christus == "1"
join e in App.ViewModel.ChristusDB.EspHosp on hospital.pk_hospital equals e.fk_hospital
join s in App.ViewModel.ChristusDB.ServHosp on hospital.pk_hospital on s.fk_hospital
orderby hospital.distancia ascending
select new Hospitale()
{...
Update. If you want to load list dependencies for each of the hospitals, I do not think you can do this with one query in EF. This is as close as you can get:
var hospitals = (from hospital in App.ViewModel.ChristusDB.Hospitales
where hospital.id_tipo == "2" && hospital.christus == "1"
orderby hospital.distancia ascending
select new Hospitale()
{
pk_hospital = hospital.pk_hospital,
...
}).ToList();
foreach (var h in hospitals)
{
h.especialidades = App.ViewModel.ChristusDB.EspHosp.Where(e => e.fk_hospital==h.pk_hospital).ToList();
h.servicios = App.ViewModel.ChristusDB.ServHosp.Where(e => s.fk_hospital==h.pk_hospital).ToList();
}
However this would be a lot of queries to the DB, which might perform poorly. You should really think about redesigning your EF data model properly so that the framework does this job of loading dependencies for you.

I need to Optimize this Linq Query

I have a Linq query that needs optimizing.
Edit
I guess over all what I am trying to do is select all the rows in the first table, but select the last or default row in Survey, CB and Science tables...
Edit
When this creates a query, I hit the database once for the entire query and then once more for EVERY single result I get back from the first query. So if TestClass returns 30 results, I then hit the DB 30 more times for the Survey Object.
I really need a better way to make this work.
Thanks in advance!
Example Query
return (from xx in db.test
select new TestClass {
Added_By_User_ID = xx.Added_By_User_ID,
Survey = (from ru in db.Utopia
where ru.Province_ID == xx.Province_ID
orderby ru.uid descending
select ru).Take(1).ToList(),
}).ToList();
Real Query
return (from xx in db.Utopia_Province_Data_Captured_Gens
where xx.Owner_Kingdom_ID == ownerKingdomID
where kingdomList.Contains((Guid)xx.Kingdom_ID)
select new ProvinceClass
{
Kingdom_ID = xx.Kingdom_ID,
Kingdom_Island = xx.Kingdom_Island,
Kingdom_Location = xx.Kingdom_Location,
Owner_Kingdom_ID = xx.Owner_Kingdom_ID,
Province_ID = xx.Province_ID,
Province_Name = xx.Province_Name,
Owner_User_ID = xx.Owner_User_ID,
Race_ID = xx.Race_ID,
Updated_By_DateTime = xx.Updated_By_DateTime,
Networth = xx.Networth,
Land = xx.Land,
Monarch_Display = xx.Monarch_Display,
Owner = xx.Owner,
Sub_Monarch = xx.Sub_Monarch,
CB_Updated_By_Province_ID = xx.CB_Updated_By_Province_ID,
uid = xx.uid,
Formatted_By = xx.Formatted_By,
Utopian_Day_Month = xx.Utopian_Day_Month,
Utopian_Year = xx.Utopian_Year,
Ruler_Name = xx.Ruler_Name,
Personality_ID = xx.Personality_ID,
Nobility_ID = xx.Nobility_ID,
Money = xx.Money,
Daily_Income = xx.Daily_Income,
Food = xx.Food,
Runes = xx.Runes,
Population = xx.Population,
Peasents = xx.Peasents,
Peasents_Non_Percentage = xx.Peasents_Non_Percentage,
Trade_Balance = xx.Trade_Balance,
Building_Effectiveness = xx.Building_Effectiveness,
Military_Efficiency_Off = xx.Military_Efficiency_Off,
Military_Efficiency_Def = xx.Military_Efficiency_Def,
Draft = xx.Draft,
Soldiers = xx.Soldiers,
Soldiers_Regs_Off = xx.Soldiers_Regs_Off,
Soldiers_Regs_Def = xx.Soldiers_Regs_Def,
Soldiers_Elites = xx.Soldiers_Elites,
War_Horses = xx.War_Horses,
//Prisoners = xx.Prisoners,
Military_Net_Off = xx.Military_Net_Off,
Military_Net_Def = xx.Military_Net_Def,
Military_Current_Off = xx.Military_Current_Off,
Military_Current_Def = xx.Military_Current_Def,
Mil_Training = xx.Mil_Training,
Mil_Wage = xx.Mil_Wage,
Mil_Overall_Efficiency = xx.Mil_Overall_Efficiency,
Mil_Total_Generals = xx.Mil_Total_Generals,
Wizards = xx.Wizards,
Wizards_Value_Type = xx.Wizards_Value_Type,
Thieves = xx.Thieves,
Thieves_Value_Type = xx.Thieves_Value_Type,
Plague = xx.Plague,
Monarch_Vote_Province_ID = xx.Monarch_Vote_Province_ID,
Protected = xx.Protected,
Hit = xx.Hit,
Honor = xx.Honor,
Province_Notes = xx.Province_Notes,
CB_Export_Line = xx.CB_Export_Line,
Army_Out = xx.Army_Out,
Army_Out_Expires = xx.Army_Out_Expires,
Updated_By_Province_ID = xx.Updated_By_Province_ID,
SOM_Updated_By_Province_ID = xx.SOM_Updated_By_Province_ID,
SOM_Updated_By_DateTime = xx.SOM_Updated_By_DateTime,
CB_Updated_By_DateTime = xx.CB_Updated_By_DateTime,
CB_Requested = xx.CB_Requested,
CB_Requested_Province_ID = xx.CB_Requested_Province_ID,
SOM_Requested = xx.SOM_Requested,
SOM_Requested_Province_ID = xx.SOM_Requested_Province_ID,
SOS_Requested = xx.SOS_Requested,
SOS_Requested_Province_ID = xx.SOS_Requested_Province_ID,
Survey_Requested = xx.Survey_Requested,
Survey_Requested_Province_ID = xx.Survey_Requested_Province_ID,
Last_Login_For_Province = xx.Last_Login_For_Province,
Date_Time_User_ID_Linked = xx.Date_Time_User_ID_Linked,
Added_By_User_ID = xx.Added_By_User_ID,
NoteCount = (from yy in db.Utopia_Province_Notes
where yy.Province_ID == xx.Province_ID
select yy).Count(),
SOM = (from uu in db.Utopia_Province_Data_Captured_Type_Militaries
where uu.Province_ID == xx.Province_ID
where uu.Owner_Kingdom_ID == ownerKingdomID
where uu.DateTime_Added == (from ru in db.Utopia_Province_Data_Captured_Type_Militaries //datetime can be same for multiple items.
where ru.Province_ID == xx.Province_ID
where ru.Owner_Kingdom_ID == ownerKingdomID
orderby ru.uid descending // To get the last most inserted rows
select ru.DateTime_Added).FirstOrDefault()
select uu).ToList(),
SOS = (from zz in db.Utopia_Province_Data_Captured_Sciences
where ru.Province_ID == xx.Province_ID
where ru.Owner_Kingdom_ID == ownerKingdomID
orderby ru.uid descending
select ru).Take(1).ToList(),
Survey = (from ru in db.Utopia_Province_Data_Captured_Surveys
where ru.Province_ID == xx.Province_ID
where ru.Owner_Kingdom_ID == ownerKingdomID
orderby ru.uid descending
select ru).Take(1).ToList(),
CB = (from ru in db.Utopia_Province_Data_Captured_CBs
where ru.Province_ID == xx.Province_ID
where ru.Owner_Kingdom_ID == ownerKingdomID
orderby ru.uid descending
select ru).Take(1).ToList()
}).ToList();
You can select from your Utopia table and join the second one for getting the user_id like:
from ru in db.Utopia
join xx in test on ru.Province_ID equals xx.Province_ID
orderby ru.uid descending
select new TestClass
{
Added_By_User_ID = xx.Added_By_User_ID,
Survey = ru
}
Update for the real query
This is one massive query, you should consider an approach based on joins as i suggested and based on local caching as Jim McKeeth suggested. Or you should create a new table all together.
When you need to query from multiple tables with optional data, you can perform a left join:
from ru in db.Utopia
join user in db.Users on ru.UserId equals user.Id // User is required
join survey in db.Surveys on ru.Province_ID equals survey.Province_ID into j1
from survey in j1.DefaultIfEmpty() // Survey is optional (left join)
join address in db.Address on ru.UserId equals address.UserId into j2
from survey in j2.DefaultIfEmpty() // Address is optional (left join)
orderby ru.uid descending
select new TestClass
{
Added_By_User_ID = xx.Added_By_User_ID,
Survey = survey,
Street = address.Street,
UserName = new UserName
{
FirstName = user.FirstName,
LastName = user.LastName
}
}
Be aware that the amount of joins will influence the performance of your query. Sooner or later it will become more efficient do find a better way with caching.
Another note is that calling ToList() in your query will actually execute that part of the query (or the whole query).
Something like:
from xx in db.test
join ru in db.Utopia on xx.Province_ID equals ru.Province_ID
orderby ru.uid descending
select new ..
The complexity of your real query to me indicates you should be moving this LINQ statement into a stored procedure, then calling that instead.
I recommend this because (a) you will have complete control over the how the query executes and, (b) profiling and performance optimizing your query is much easier in TSQL. This is the strategy we have used in larger projects in the past when result-set generation becomes non-trivial.
I would also suggest you use AutoMapper to perform the task of transferring the sproc results into your DTO, because you appear to have a near 1:1 mapping

Categories

Resources