how to convert IQueryable<Category> to Category - c#

I have a database with 2 tables: "Albumes" and "Categories".
Each Album has an "id" and a "categoryId" property. although each Category has a "name" property.
I can get the Album.id via QueryString from another page and show the desired Album details in a formview. now i want to show the name of category which contains that album.
I try to do this:
public string GetCategoryName(int albumCategoryId)
{
var _db = new Trying.Models.AlbumContext();
IQueryable<Category> query = _db.Categories;
query = query.Where(c => c.id == albumCategoryId);
Category ca = new Category();
//some code
return ca.name;
}
My problem is in "//some code" section! there, I must convert "IQueryabl query" to "Category ca".
How to do this?

Where returns a set of all matching entities but you only want a single one.
public string GetCategoryName(Int32 albumCategoryId)
{
using (var _db = new Trying.Models.AlbumContext())
{
return _db.Categories.Single(c.id == albumCategoryId).Name;
}
}
Using Single will throw an exception if there is no matching category and you should use this if you are sure that the category has to exist. If you are not sure whether the category exists or not, you can use SingleOrDefault to get null if there is no matching category. First or FirstOrDefault would work, too, but they are semantically wrong because you don't want the first category, you want to only one. SingleOrDefault obviously requires handling the case that there is no matching category.
public string GetCategoryName(Int32 albumCategoryId)
{
using (var _db = new Trying.Models.AlbumContext())
{
var category = _db.Categories.SingleOrDefault(c.id == albumCategoryId);
if (category != null)
{
return category.Name;
}
else
{
// Handle the no category found case by returning a default value,
// throwing an exception or what ever fits your needs.
}
}
}
Also note that you should use your database context in an usingstatement to ensure that it gets early and correctly disposed even in the case of an error.
Further you can access the name of the category directly by navigating through your navigation properties.
var categoryName = album.Category.Name;
This is obviously only useful when you have the album object available and you have to ensure that you load the category by either using lazy loading or explicitly including it if you use eager loading.
var album = _db.Albumes.Include(a => a.Category)
.Single(a => a.Id = 42);
This will load the album with Id 42 and because of Include(a => a.Category) also the category of this album. Whether this is better or worse than explicitly querying the category name depends of course on your requirements.

Try this:
public string GetCategoryName(int albumCategoryId)
{
var _db = new Trying.Models.AlbumContext();
IQueryable<Category> query = _db.Categories;
Category ca = query.First(c => c.id == albumCategoryId);
return ca.name;
}

I will explain with some examples, check the type of the variable because thats what each line is going to return:
list<Category> list = query.Where(c => c.id == albumCategoryId).AsEnumerable();
Category category= query.FisrtOrDefault(c => c.id == albumCategoryId);

IQueryable<T> is a collection.
You need to limit your collection to a single item.
query.Where will also result in a collection. I believe you're looking for a unique item by id. I would recommend Single instead:
var ca = query.Single(c => c.id == albumCategoryId);
return ca.name;

Related

How can I edit my sequence so it returns one element only using LINQ

I have the code shown below:
var subcategories = new List<Subcategory>
{
new Subcategory { SubcategoryName = "Football", CategoryID = categories.Single(c => c.CategoryName == "Sport").CategoryID },
new Subcategory { SubcategoryName = "Basketball", CategoryID = categories.Single(c => c.CategoryName == "Sport").CategoryID },
new Subcategory { SubcategoryName = "Piano", CategoryID = categories.Single(c => c.CategoryName == "Music").CategoryID },
new Subcategory { SubcategoryName = "Violin", CategoryID = categories.Single(c => c.CategoryName == "Music").CategoryID }
};
foreach (Subcategory s in subcategories)
{
var subcategoriesInDB = context.Subcategories.Where(c => c.Category.CategoryID == s.CategoryID).SingleOrDefault();
if (subcategoriesInDB == null)
{
context.Subcategories.Add(s);
}
}
The point is that, the query inside the foreach statement returns two elements in my case. Because as you can see I have two subcategories for my two categories, therefore there are total of four subcategories. This causes an error message: Sequence contains more than one element when I try the Update-Database command.
As you can see my Subcategory names are all different, therefore I want to add also a check for that. So, if the category ids are the same, then we will check whether the subcategory names are different then the ones already existing inside the database, so it will either return one entry or null. If someone can help me achieve it, or give me another solution I would be glad.
Update:
If I change it to:
var subcategoriesInDB = context.Subcategories.FirstOrDefault(c => c.Category.CategoryID == s.CategoryID);
And then do a Update-Database command, I get a new error message saying:
An error occurred while updating the entries. See the inner exception
for details.
var subcategoriesInDB = context.Subcategories.Where(c => c.Category.CategoryID == s.CategoryID).SingleOrDefault();
Should read....
var subcategoriesInDB = context.Subcategories.FirstOrDefault(c => c.Category.CategoryID == s.CategoryID);
In using SingleOrDefault you're implying only one element will be returned. If there are none, you'll get null, if there are more than one, you'll get an exception to let you know there are multiple (i.e. not what you expected). The use of FirstOrDefault implies you are aware there may be multiple elements that match your query, but you only want the first.

Better way to update the underlying lookup via EF?

Here's my situation - I've got a DB which has some tables named recipes, ingredients and recipes_ingredients.
Recipes are composed of 1+ ingredients.
The recipes_ingredients has FKs between the recipes and ingredients table.
The classes that get generated are recipe and ingredient and recipe has a navigation property that looks like so:
public virtual ICollection<ingredients> ingredients { get; set; }
Great, I understand that I get a generated recipe class and a generated ingredient class and that the recipes_ingredients table doesn't get a class generated since EF views this simply as a navigation property.
Now, I've got a function called SetIngredientsForRecipe that looks like so (minus the try-catch code for brevity's sake:
public void SetIngredientsForRecipe(long recipeId, List<string> ingredients)
{
using (var db = new FoodEntities(ConnectionString, null, null))
{
var existing = GetCurrentIngredients(recipeId);
var toRemove = existing.Except(ingredients);
var toAdd = ingredients.Except(existing);
var recipe = db.recipes.Where(r => r.Id == recipeId).FirstOrDefault();
foreach (var name in toRemove)
{
var entry = recipe.ingredients.Where(i => i.Name == name).FirstOrDefault();
recipe.ingredients.Remove(entry);
}
foreach (var name in toAdd)
{
var entry = db.ingredients.Where(i => i.Name == name).FirstOrDefault();
recipe.ingredients.Add(entry);
}
db.SaveChanges();
}
}
The intent, as the name suggests, is to update the ingredient list for the given recipe to only whatever is in the list. I'm still getting comfortable with EF and wondering if there's a better (more efficient?) way to accomplish what I'm trying to do.
Follow-up:
Following the suggestions by ntziolis below, I opted to use
recipe.ingredients.Clear() to clear out whatever was in the recipe/ingredient mapping and then use the mocking that was mentioned to quickly add the new ones. Something like this:
foreach (var name in ingredients)
{
// Mock an ingredient since we just need the FK that is referenced
// by the mapping table - the other properties don't matter since we're
// just doing the mapping not inserting anything
recipe.ingredients.Add(new Ingredient()
{
Name = name
});
}
and this works very nicely.
General performance guidelines are:
try to deal with id's only
mock entities whenever possible, rather than retrieving them from db
use the new features of EF4 like Contains in order to simplify and speed up your code
Based on these principles here is a optimized (not simpler though) solution to your problem:
public void SetIngredientsForRecipe(long recipeId, List<string> ingredients)
{
using (var db = new FoodEntities(ConnectionString, null, null))
{
var recipe = db.recipe.Single(r => r.ID == recipeId);
// make an array since EF4 supports the contains keyword for arrays
var ingrArr = ingredients.ToArray();
// get the ids (and only the ids) of the new ingredients
var ingrNew = new HasSet<int>(db.ingrediants
.Where(i => ingrArr.Contains(i.Name))
.Select(i => I.Id));
// get the ids (again only the ids) of the current receipe
var curIngr = new HasSet<int>(db.receipes
.Where(r => r.Id == recipeId)
.SelectMany(r => r.ingredients)
.Select(i => I.Id));
// use the build in hash set functions to get the ingredients to add / remove
var toAdd = ingrNew.ExpectWith(curIngr);
var toRemove = curIngr.ExpectWith(ingrNew);
foreach (var id in toAdd)
{
// mock the ingredients rather than fetching them, for relations only the id needs to be there
recipe.ingredients.Add(new Ingredient()
{
Id = id
});
}
foreach (var id in toRemove)
{
// again mock only
recipe.ingredients.Remove(new Ingredient()
{
Id = id
});
}
db.SaveChanges();
}
}
If you want it simpler you could just clear all ingredients and re add them if necessary, EF might even be clever enough to figure out that the relations haven't changed, not sure about it though:
public void SetIngredientsForRecipe(long recipeId, List<string> ingredients)
{
using (var db = new FoodEntities(ConnectionString, null, null))
{
var recipe = db.recipe.Single(r => r.ID == recipeId);
// clear all ingredients first
recipe.ingredients.Clear()
var ingrArr = ingredients.ToArray();
var ingrIds = new HasSet<int>(db.ingrediants
.Where(i => ingrArr.Contains(i.Name))
.Select(i => I.Id));
foreach (var id in ingrIds)
{
// mock the ingredients rather than fetching them, for relations only the id needs to be there
recipe.ingredients.Add(new Ingredient()
{
Id = id
});
}
db.SaveChanges();
}
}
UPDATE
Some coding errors have been corrected.
You can condense your Where clauses with the FirstOrDefault calls:
recipe.ingredients.FirstOrDefault(i => i.Name == name);
Though I personally prefer to use SingleOrDefault though I'm not sure what the difference is exactly:
recipe.ingredients.SingleOrDefault(i => i.Name == name);
Also, since the ingredient list that is passed in is a List<string> (as opposed to a list of ingredient IDs), it sort of implies that new ingredients may also be created as part of this process, which isn't handled (though may have been left out for brevity).

Nhibernate equal restriction on guid

I have a class, Comapny, with userId as system.Guid, i want to filter that company (userId is not the primary key), but when I do it like this:
List<Company> list =
session.CreateCriteria<Company>().Add(Restrictions.Eq("UserId", userId))
.List<Company>().ToList();
The list is returning empty even though there are rows in the db with that property.
I tried doing it like this:
Company companyResult = null;
list.ForEach(delegate (Company company)
{
if (company.UserId.Equals(userId))
{
if (companyResult == null)
{
companyResult = company;
}
}
});
return companyResult;
and it works, is there a reason why Nhibernate eq won't work on this Guid?
Why not using the Query method?
var list = session.Query<Company>().Where(x => x.UserId == userId).ToList();
The best you can do to really investigate this, is to switch on show-sql, apparently you are retrieving something different from the database then you think?

Entity Framework 4.1. Updating many-to-many relationships. Is this the right way?

The code below works, however, I suspect that I'm missing something. Is there a 'better' way?
private void UpdateNew(MarketProduct marketproduct)
{
context.MarketCategories.Load();
MarketProduct dbProd = context.MarketProducts.Find(marketproduct.Id);
dbProd.Categories.Clear();
foreach (var c in marketproduct.Categories ?? Enumerable.Empty<MarketCategory>())
{
var cc = context.MarketCategories.Find(c.Id);
dbProd.Categories.Add(cc);
}
context.Entry(dbProd).CurrentValues.SetValues(marketproduct);
}
I thought it would be possible to do this without using Find
You have three database queries: 1) context.MarketCategories.Load() (hopefully the category table is small, otherwise this would be a no-go as it loads the whole table into memory), 2) ...Find and 3) dbProd.Categories.Clear(): Here must be a lazy loading involved, otherwise this would crash because dbProd.Categories would be null.
An alternative to update with a single database query is the following:
private void UpdateNew(MarketProduct marketproduct)
{
MarketProduct dbProd = context.MarketProducts
.Include(p => p.Categories)
.Single(p => p.Id == marketproduct.Id);
var categories = marketproduct.Categories
?? Enumerable.Empty<MarketCategory>();
foreach (var category in categories)
{
if (!dbProd.Categories.Any(c => c.Id == category.Id))
{
// means: category is new
context.MarketCategories.Attach(category);
dbProd.Categories.Add(category);
}
}
foreach (var category in dbProd.Categories.ToList())
{
if (!categories.Any(c => c.Id == category.Id))
// means: category has been removed
dbProd.Categories.Remove(category);
}
context.Entry(dbProd).CurrentValues.SetValues(marketproduct);
// context.SaveChanges() somewhere
}
I believe you could just do this:
var dbProd = context.MarketProducts.Find(marketproduct.Id);
dbProd.Categories = dbProd.Categories
.Union(marketproduct.Categories).ToList();
context.SaveChanges();
The Union() call will keep any existing products, add new ones, and update ones that overlap. Since your navigation property Categories is probably defined as an ICollection<Category> you have to use the ToList() extension method during assignment.

Help with LINQ to SQL query

I usually do just fine, but for some reason I cannot figure out why this particular query is defeating me.
For simplicity here is the database(Brand and PageTitle):
PageTitle is just a table to hold SEO data(there is only one row per Brand)
I want to : Select one row from PageTitle where brands = p (variable that holds the query string value)
This is an example of what I'm trying to do (If there are no records for the brand in PageTitle I don't want to throw an error).
var pages = da.PageTitles.Where(x => x.Brands.Single(z => z.BrandID == p)).SingleOrDefault();
if (pages.Any())
{
txtSeoTitle.Text = pages.Title;
txtSeoMetaKeywords.Text = pages.Keywords;
txtSeoMetaDesc.Text = pages.Description;
}
Unless i'm missing something obvious, can't you just do this:
var page = db.PageTitles
.FirstOrDefault(x => x.Brands.Any(y => y.BrandId == p));
SingleOrDefault will throw an exception if more than one row is returned, so i think you need FirstOrDefault.

Categories

Resources