I am making this autocomplete search bar which contains title, description and category_id but I need the category name which is in another table so I take out the category id from my ads table and check the id with the table in category I know I need to close my connection to the database before I can make a new one so I need another way around it.
public class SetGetAds
{
public string Title { get; set; }
public string Description { get; set; }
public string Category { get; set; }
}
using (var db = new myProjectEnt())
{
var getAds = (from a in db.ads where a.title.Contains(searchQuery) select new { a.title, a.description, a.categories_id }).Take(15);
var ads = new List<SetGetAds>();
foreach (var setAds in getAds)
{
var getCategory = (from c in db.ads where c.title.Equals(setAds.categories_id) select new { c.title }).SingleOrDefault();
ads.Add(new SetGetAds { Title = setAds.title, Description = setAds.description, Category = getCategory.title });
var jsonString = new JavaScriptSerializer();
return jsonString.Serialize(ads);
}
}
getAds is an enumerable sequence that is lazily taking data from the reader - you then loop over that. Then, for each one you are performing a second query - getCategory. The important thing here is that getAds is still reading data - so yes, you have nested commands.
Options (in preference order, highest = preferred):
restructure the query to get the category at the same time, in one go - to avoid both the nested query and the obvious N+1 issue
add a .ToList() on the end of getAds, to complete the first query eagerly
enable MARS so that you are allowed to have nested queries
An N+1 issue is very commonly a source of performance problems; personally I would be looking to write this query in a way that avoids that, for example:
var ads = (from a in db.ads
where a.title.StartsWith(searchQuery)
join c in db.ads on a.categories_id equals c.title
select new { a.title, a.description, a.categories_id,
category = c.title }).Take(15);
Related
I need to perform an update on a table with values from a List of objects in C# .NET Core 3.0. I tried to use the Join method, but receive this error:
Processing of the LINQ expression
DbSet<Room>
.Join(
outer: __p_0,
inner: p => p.RoomId,
outerKeySelector: s => s.ruId,
innerKeySelector: (s, p) => new {
kuku = s,
riku = p
})
by 'NavigationExpandingExpressionVisitor' failed. This may indicate either a bug or a limitation in EF Core. See link for more detailed information.
public class Room
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Key]
public int RoomId { get; set; }
[StringLength(50, MinimumLength = 3)]
public string RoomAddress { get; set; }
}
public class roomsForUpdate
{
public int ruId { get; set; }
public string ruName { get; set; }
}
var roomList = new List<roomsForUpdate>() { new roomsForUpdate { ruId = 1, ruName = "aa" }, new roomsForUpdate { ruId = 2, ruName = "bb" } };
var result = _context.Room.Join(roomList, p => p.RoomId, s => s.ruId, (s, p) => new { kuku = s, riku = p }).ToList();
You cannot join the EF Core LINQ query with a local list, because it can't be translated into SQL. Better first you get the database data and then join in memory.
LINQ is not meant to change the sources, it can only extract data from the sources. If you need to update data, you first fetch the items that must be updated, then you update them. Alternatively you can use plain old SQL to update the data without fetching it first.
In local memory, you have a sequence of RoomsForUpdate. Every RoomForUpdate has an Id (RuId) and a Name.
In your database you have a table with Rooms, Every Room in this table has an Id in RoomId and a RoomAddress.
It seems to me, that you want to update all Rooms that have an RoomId, that is one of the RuIds in your sequence of RoomsForUpdate. In other words: fetch (some properties of) all Rooms that have a value for RoomId that is a RuId in your sequence of RoomsForUpdate:
var roomsToUpdate = new List<roomsForUpdate>()
{
new roomsForUpdate { ruId = 1, ruName = "aa" },
new roomsForUpdate { ruId = 2, ruName = "bb" }
};
// Extract the Ids of the rooms that must be fetched
var roomToUpdateIds = roomsToUpdate.Select(room => room.ruId);
// Fetch all rooms from the database that have a RoomId that is in this sequence
var fetchedRooms = dbContext.Rooms
.Where(room => roomToUpdateIds.Contains(room => room.RoomId)
.ToList();
Of course you can put everything into one big LINQ statement. This will not improve efficiency, however it will deteriorate readability of your code.
Now to update the Rooms, you'll have to enumerate them one by one, and give the fetched rooms new values. You didn't say which new value you want. I have an inkling that you want to assign RuName to RoomAddress. This means that you have to combine the Room with the new value for the RoomAddress.
This can be done by LINQ:
var roomsWithExpectedNewValues = fetchedRooms.Join(roomsToUpdate,
fetchedRoom => fetchedRoom.RoomId, // from every fetched room take the Id
roomToUpdate => roomToUpdate.RuId, // from every room to update take the RuId
// for every fetchedRoom with its matching room to update, make one new:
(fetchedRoom, roomToUpdate) => new
{
Room = fetchedRoom,
NewValue = roomToUpdate.RuName,
})
.ToList();
To actually perform the update, you'll have to enumerate this sequence:
foreach (var itemToUpdate in roomsWithExpectedNewValues)
{
// assign RuName to RoomName
itemToUpdate.Room.RoomName = itemToUpdate.NewValue;
}
dbContext.SaveChanges();
A little less LINQ
Although this works, there seems to be a lot of magic going on. The join will internally make a Dictionary for fast lookup, and throws it away. I think a little less LINQ will make it way more easy to understand what's going on.
// your original roomsToUpdate
var roomsToUpdate = new List<roomsForUpdate>()
{
new roomsForUpdate { ruId = 1, ruName = "aa" },
new roomsForUpdate { ruId = 2, ruName = "bb" }
};
var updateDictionary = roomsToUpdate.ToDictionary(
room => room.RuId, // key
room => room.RuName) // value
The Keys of the dictionary are the IDs of the rooms that you want to fetch:
// fetch the rooms that must be updated:
var fetchedRooms = dbContext.Rooms
.Where(room => updateDictionary.Keys.Contains(room => room.RoomId)
.ToList();
// Update:
foreach (var fetchedRoom in fetchedRooms)
{
// from the dictionary fetch the ruName:
var ruName = updateDicationary[fetchedRoom.RoomId];
// assign the ruName to RoomAddress
fetchedRoom.RoomAddress = ruName;
// or if you want, do this in one statement:
fetchedRoom.RoomAddress = updateDicationary[fetchedRoom.RoomId];
}
dbContext.SaveChanges();
I have a function to get data from my database with join on it. I want to take data from different tables, how can I achieve this? I want to take "libelle_motif" which is from the table "motif_deplacement"
My function right now:
public static List<personne> getPersonne_Deplacement(int numDeplacement)
{
List<personne> desP = new List<personne>();
var query = (from Per in db.personne.ToList()
join Dep in db.deplacement_personne.ToList() on Per.num_personne equals Dep.num_personne
join Mot in db.motif_deplacement.ToList() on Dep.id_motif equals Mot.id_motif
where Dep.id_deplacement == numDeplacement
select new personne
{
nom_personne = Per.nom_personne,
num_personne = Per.num_personne,
ref_personne = Per.ref_personne,
libelle_motif = Mot.libelle_motif,
});
desP = query.ToList();
return desP;
}
And this is how my database looks like :
You will have to create a new class which will act as the model with the properties you want. Construct one of those in your select based on the included relationships.
Also it much easier to manage this query if you model your relationships in your EF entities as opposed to writing out join statements in every query.
Also notice that I removed all the calls to ToList. Your previous code was materializing all entities from each table and then joining and filtering in memory which is extremely inefficient.
public static List<SomeModel> getPersonne_Deplacement(int numDeplacement)
{
var query = from Per in db.personne
join Dep in db.deplacement_personne on Per.num_personne equals Dep.num_personne
join Mot in db.motif_deplacement on Dep.id_motif equals Mot.id_motif
where Dep.id_deplacement == numDeplacement
select new SomeModel
{
nom_personne = Per.nom_personne,
num_personne = Per.num_personne,
ref_personne = Per.ref_personne,
libelle_motif = Mot.libelle_motif,
};
return query.ToList();
}
SomeModel.cs
public class SomeModel
{
public string nom_personne {get;set;}
public string num_personne {get;set;}
public string ref_personne {get;set;}
public string libelle_motif {get;set;}
// add additional properties as needed
}
I don't know if my answer is related to your question. But why don't you use .Include()
Ex:
var result = db.personne.Include(x => x.deplacement_personne).ThenInclude(x => x.motif_deplacement)
It will give you this structure:
obj personne { nom_personne, ... , obj deplacement_personne { ... , obj motif_deplacement }
You can also select any cols with .Select() , or filter it with .Where().
I am trying to join three linq queries into single query and display the records into an angular js application. But the problem is when I enter the account number and click the submit button, it also displays the duplicate record.
Here is the Linq Query:
public string TranscationDetails(string Account_Number)
{
var accountNumber = int.Parse(Account_Number);//It could be better to use TryParse
using (HalifaxDatabaseEntities context = new HalifaxDatabaseEntities())
{
var CombinedQuery = (from x in context.Current_Account_Deposit
join y in context.Current_Account_Withdraw on x.Account_Number equals y.Account_Number
join z in context.Current_Account_Details on y.Account_Number equals z.Account_Number
where x.Account_Number == accountNumber
select new
{
x.Account_Number,
x.Amount,
Amount1 = y.Amount,
z.Account_Balance,
}).ToList();
var js = new System.Web.Script.Serialization.JavaScriptSerializer();
return js.Serialize(CombinedQuery); // return JSON string
}
}
Some people suggest me on this link ..
http://www.advancesharp.com/blog/1108/linq-inner-join-left-outer-join-on-two-lists-in-c-with-example
Here is the database record screenshot:
Here is the screen shot when i clicked the button with account number and Linq query also display the duplicate record.
May be you can use this concept of class
ReturnClass
{
Current_Account_Details detail {get;set;}
List<Current_Account_Withdraw> listofwithdraw {get;set;}
List<Current_Account_Deposit> listofdeposit {get;set;} }
}
Example : (code may not working :D )
I'll use lambda
context.Current_Account_Details.Select(x => new
{
x.Account_Number,
detail = x,
listofwithdraw = x.Current_Account_Withdraw.ToList(),
listofdeposit = x.Current_Account_Deposit.ToList()
}).ToList();
I am grabbing a value and want it to appear in the BatchId of every anonymous type created via a linq statement.
Here is the code:
var batchId = context.Request["batchid"];
using (var db = new StarterSiteEntities())
{ // Get data
var transactions = (from t in db.Transactions
join td in db.TransactionDetails on t.TransactionID equals td.TransactionID
join p in db.Products on td.ProductID equals p.ProductID
where t.Exported == false
select new
{
BatchId = batchId,
t.FirstName,
t.LastName,
t.Address1,
t.Address2,
t.City,
t.State,
t.Zip_Code,
t.Email,
t.Phone,
t.TotalAmount,
t.MonthlyGift,
t.DateCreated,
p.Fund,
ProductFirstName = p.FirstName,
ProductLastName = p.LastName,
ProductUniversity = p.University,
ProductState = p.State,
ProductEmail = p.Email,
ProductAmount = td.Amount
}).ToList();
}
When I do this, I get the error message:
"A parameter is not allowed in this location. Ensure that the '#' sign is in a valid location or that parameters are valid at all in this SQL statement."
How do I reference the batchId variable from within the anonymous type declaration, or should I accomplish this another way?
It looks like you ran into a known bug in the SQL Server CE data access libraries. You should be able to fix it by applying this hotfix to the machine(s) that are accessing the database.
While I think Adam Maras answered my question. Because I did not want to install a hot-fix on the server, I ended up solving the problem using a different method.
Since the Linq query would not allow me to use a string variable and I could not edit the property value of an anonymous type. I stopped using an anonymous type and created an entity class to hold my "transaction summary" data.
Once I have a collection of TransactionSummary objects, I can use the Select() method to update the BatchId property value in each record.
Here is the resulting code:
// Define a custom type to hold the data
private class TransactionSummary
{
public string BatchId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
//...removed lines for brevity...
}
//...here is the updated code snippet...
using (var db = new StarterSiteEntities())
{ // Get data
var transactions = (from t in db.Transactions
join td in db.TransactionDetails on t.TransactionID equals td.TransactionID
join p in db.Products on td.ProductID equals p.ProductID
where t.Exported == false
select new TransactionSummary
{
FirstName = t.FirstName,
LastName = t.LastName,
//...removed lines for brevity...
}).ToList();
// The client would like a batchID added to each record that we return.
var batchId = context.Request["batchid"];
transactions.Select(t => { t.BatchId = batchId; return t; }).ToList();
}
Below is my LINQ Query, that im using to select ITEMS:
protected void Page_Load(object sender, EventArgs e)
{
whatsmydiscountEntities ctx = new whatsmydiscountEntities();
int IdRelationshipItems = Convert.ToInt32(Request.QueryString["IdRelationshipItems"]);
int IdProductService = Convert.ToInt32(Request.QueryString["IdProductService"]);
var Items = (from es in ctx.State
join ie in ctx.ItemsStates on es.StateId equals ie.StateId
join i in ctx.Items on ie.IdItem equals i.IdItem
join iir in ctx.ItemsRelationshipItems on i.IdItem equals iir.IdItem
join ir in ctx.RelationshipItems on iir.IdRelationshipItems equals ir.IdRelationshipItems
join ips in ctx.ItemsProductsServices on i.IdItem equals ips.IdItem
join ps in ctx.ProductsServices on ips.IdProductService equals ps.IdProductService
where iir.IdRelationshipItems == IdRelationshipItems
&& ips.IdProductService == IdProductService
&& ir.Active == 1
&& i.Active == 1
select new
{
ItemName = i.Name,
StateSigla = es.Sigla,
ProductServiceName = ps.Ttitle,
RelationshipItemName = ir.Name,
RelationshipItemImage = ir.Image,
RelationshipItemActive = ir.Active,
ItemSite = i.Site,
ItemDescription = i.Description,
ItemAddress = i.Address,
Iteminformationdiscount = i.information_discount,
ItemLogo = i.Logo,
ItemActive = i.Active,
StateId = ie.StateId,
IdRelationshipItems = iir.IdRelationshipItems,
IdProductService = ips.IdProductService
}).ToList();
}
As you can see, the result will be 1 row for each state, if the user passes the IdRelationshipItems and the IdProductService.
Instead of 1 row for each state with the same information, I'd like to show only 1 row and all the states separated by commas. What do I need to change to do this?
I had to solve this problem today. I have a view model that looks like this:
public class IndexViewModel
{
public string Search { get; set; }
public IPagedList<MembershipUser> Users { get; set; }
public IEnumerable<string> Roles { get; set; }
public bool IsRolesEnabled { get; set; }
public IDictionary<string,string> Tags { get; set; }
}
I needed to return a unique list of users and all of the Tags that are assigned to them as a comma separated string.
The data is stored like this:
"41FFEC0F-B920-4839-B0B5-862F8EDE25BD", tag1
"41FFEC0F-B920-4839-B0B5-862F8EDE25BD", tag2
"41FFEC0F-B920-4839-B0B5-862F8EDE25BD", tag3
And I needed output that looked something like this:
"41FFEC0F-B920-4839-B0B5-862F8EDE25BD", "tag1, tag2, tag3"
I ended up creating a List of UserId's like this (I'm using the MembershipProvider which exposes the UserId as ProviderUserKey):
var userIdList = users.Select(usr => (Guid) usr.ProviderUserKey).ToList();
The object "users" is a MembershipUser object. I then call a function in my service passing the List in like this:
Tags = _usersTagsService.FindAllTagsByUser(userIdList)
And my service function looks like this:
public IDictionary<string, string> FindAllTagsByUser(IList<Guid> users)
{
var query = (from ut in _db.UsersTags
join tagList in _db.Tags on ut.TagId equals tagList.Id
where users.Contains(ut.UserId)
select new {ut.UserId, tagList.Label}).ToList();
var result = (from q in query
group q by q.UserId
into g
select new {g.Key, Tags = string.Join(", ", g.Select(tg => tg.Label))});
return result.ToDictionary(x=>x.Key.ToString(),y=>y.Tags);
}
I'm pretty sure these two linq statements can probably be combined into one but I find it easier to read this way. Still only hits the database once.
Things to watch out for:
I was originally passing just IList users into the function FindAllTagsByUser and linq couldn't infer the type and therefore wouldn't let me use the .Contains extension method. Kept saying that linq to entities didn't support Contains.
You need to do a .ToList() on the first query to materialize it or you will get trouble from linq to entity when you try to use string.Join to create the comma separated list.
Good luck
dnash