I'm trying to work through a problem where I'm mapping EF Entities to POCO which serve as DTO.
I have two tables within my database, say Products and Categories. A Product belongs to one category and one category may contain many Products. My EF entities are named efProduct and efCategory. Within each entity there is the proper Navigation Property between efProduct and efCategory.
My Poco objects are simple
public class Product
{
public string Name { get; set; }
public int ID { get; set; }
public double Price { get; set; }
public Category ProductType { get; set; }
}
public class Category
{
public int ID { get; set; }
public string Name { get; set; }
public List<Product> products { get; set; }
}
To get a list of products I am able to do something like
public IQueryable<Product> GetProducts()
{
return from p in ctx.Products
select new Product
{
ID = p.ID,
Name = p.Name,
Price = p.Price
ProductType = p.Category
};
}
However there is a type mismatch error because p.Category is of type efCategory. How can I resolve this? That is, how can I convert p.Category to type Category?
Likewise when I do
return from c in ctx.Categories
where c.ID == id
select new Category
{
ID = c.ID,
Name = c.Name,
ProductList = c.Products;
};
I get a mismatch because ProductList is of type Product, where c.Products is an EntityCollection
I know in .NET EF has added support for POCO, but I'm forced to use .NET 3.5 SP1.
return from p in ctx.Products
select new Product
{
ID = p.ID,
Name = p.Name,
Price = p.Price
ProductType = new Category
{
ID = p.Category.ID,
Name = p.Category.Name // etc.
}
};
For Category you do:
return from c in ctx.Categories
where c.ID == id
select new Category
{
ID = c.ID,
Name = c.Name,
ProductList = from p in c.Products
select new Product
{
ID = p.ID,
Name = p.Name,
Price = p.Price
}
};
Related
I want to get all product names with category if even product doesn't have a category
get informatoin for creation from here
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public virtual ICollection<CategoryProduct> CategoryProducts { get; set; }
}
public class CategoryProduct
{
public int CategoryProductId { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
internal class EFDbContext : DbContext, IDBProductContext
{
public DbSet<Product> Products { get; set; }
public DbSet<CategoryProduct> CategoryProducts { get; set ; }
public EFDbContext()
{
Database.SetInitializer<EFDbContext>(new DropCreateDatabaseIfModelChanges<EFDbContext>());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>().HasMany(p => p.CategoryProducts)
.WithMany(c => c.Products)
.Map(pc => {
pc.MapLeftKey("ProductRefId");
pc.MapRightKey("CategoryProductRefId");
pc.ToTable("CategoryProductTable");
});
base.OnModelCreating(modelBuilder);
}
}
If I write a SQL query like this, I get all of them from joined EF table
SELECT p.Name, cp.Name
FROM CategoryProductTable AS cpt,
CategoryProducts AS cp, Products as p
WHERE
p.ProductId = cpt.ProductRefId
AND cp.CategoryProductId = cpt.CategoryProductRefId
but I want to get all from product names with category if even product doesn't have a category
UPDATED: thanks for SQL solution #Nick Scotney, but now I would want know how it do it in Linq
Could you be after a "LEFT OUTER JOIN" in your Sql?
SELECT
p.Name,
cp.Name
FROM
Products p
LEFT OUTER JOIN CategoryProductTable cpt ON p.ProductId = cpt.ProductRefId
LEFT OUTER JOIN CategoryProducts cp ON cpt.CategoryProductRefId = cp.CategoryProductId
In the above SQL, everything from products will be selected, regardless of if there is a Category or not. When there isn't a category, cp.Name will simply return NULL.
You would want to remove p.ProductId = cpt.ProductRefId predicate if you want to show all products.
SELECT p.Name, cp.Name
FROM CategoryProductTable as cpt, CategoryProducts as cp, Products as p
WHERE cp.CategoryProductId = cpt.CategoryProductRefId
This will assign each category to every product.
How do I read a many-to-many table via EF? I have no idea how to use the many-to-many table. Let's say Product_Category where it got ProductID and CategoryID.
How can I access it trough e.g.
using(Entities db = new Entities)
{
/* cant access these here.. */}
method?? I can however reach Product_Category, but cant access its ProductID or CategoryID.
I want to list every product e.g. where Product_Category.CategoryID == Category.ID.
I have never used many-to-many tables before, so I appreciate some simple examples how to access them trough EF in asp.net.
Thanks
Navigation properties are your friend here. Unless you have other properties in the junction table, you don't need it. This is why there is no Product_Category in your models. So say your models are:
public class Product
{
public Product()
{
this.Categories = new HashSet<Category>();
}
public int ProductId { get; set; }
public string ProductName { get; set; }
public virtual ICollection<Category> Categories { get; set; }
}
public class Category
{
public Category()
{
this.Products = new HashSet<Product>();
}
public int CategoryId { get; set; }
public string CategoryName { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
So now if you want all products in a category you can do something like:
var productsInCategory = db.Categorys
.Where(c => c.CategoryId == categoryId)
.SelectMany(c => c.Products);
If you do want an explicit junction tables see this: https://lostechies.com/jimmybogard/2014/03/12/avoid-many-to-many-mappings-in-orms/
You have to join the product and category tables with the bridge table Product_Category to retrieve the required product info.
using(eShopEntities db = new eShopEntities)
{
var products = (from p in db.Product_Category
join ProductTable pt on p.ID = pt.ProductID
join Category c on c.ID = P.CategoryID
select new
{
p.ID,
p.Name,
p.Description,
p.Price
}).ToList();
}
I have a class that holds Categories.
public class CategoryDto
{
public int Id { get; set; }
public int PortfolioId { get; set; }
public string Description { get; set; }
public List<SubCategoryDto> SubCategories { get; set; }
public CategoryDto()
{
SubCategories = new List<SubCategoryDto>();
}
}
It has a List in it, which is a list of SubCategory classes:
public class SubCategoryDto
{
public int Id { get; set; }
public int CategoryId { get; set; }
public CategoryDto Category { get; set; }
public string Description { get; set; }
}
I then populate this item, but I am getting a list bases on a 'PortfolioId'.
var cats = (from p in Context.transaction_category
where p.account_portfolio_id == portfolioId
&& p.deleted == null
select new CategoryDto
{
Id = p.id,
Description = p.description,
PortfolioId = p.account_portfolio_id
}).ToList();
The Categories table has a foreign key to SubCategories. Each category has 0:n sub categories. So, the entity framework model has a Context.transaction_category.transaction_sub_categories collection.
So now what I do is, foreach through the categories in the list above, and populate the sub categories.
Is there a way to do this in the same link statement? The Categories object has a List list. Can it be done in the above Linq statement?
Edit:
This is the fix attempt, as recommended, but is presenting an error:
var cats = (from p in Context.transaction_category
where p.account_portfolio_id == portfolioId
&& p.deleted == null
select new CategoryDto
{
Id = p.id,
Description = p.description,
PortfolioId = p.account_portfolio_id,
SubCategories = (from s in Context.transaction_sub_category where s.transaction_category_id == p.id
&& s.deleted == null
select new SubCategoryDto
{
Id = s.id,
Description = s.description,
CategoryId = s.transaction_category_id
}).ToList()
}).ToList();
LINQ to Entities does not recognize the method
'System.Collections.Generic.List1[Objects.SubCategoryDto]
ToList[SubCategoryDto](System.Collections.Generic.IEnumerable1[Objects.SubCategoryDto])'
method, and this method cannot be translated into a store expression.
You can do it like this:
var cats = (from p in Context.transaction_category
where p.account_portfolio_id == portfolioId
&& p.deleted == null
select new CategoryDto
{
Id = p.id,
Description = p.description,
PortfolioId = p.account_portfolio_id,
SubCategories = (from s in Context.transaction_category.transaction_sub_categories
where s.CategoryId == p.Id
select new SubCategoryDto {
Id = s.Id,
Description = s.Decription
}).ToList()
}).ToList();
Update: To make it easier change your SubCategories and Category properties like this:
public virtual List<SubCategoryDto> SubCategories { get; set; }
public virtual CategoryDto Category { get; set; }
Then you can use include and simply load your sub categories like this:
var cats = Context.transaction_category
.Where(p => p.account_portfolio_id == portfolioId && p.deleted == null)
.Include(p => p.SubCategories);
I'm newer using C#, linq. I'm trying to add the UserName into a query to show it as part of a DataSource of a ListView, I have tested several way to joined, but always I m'receiving next error:
"Unable to create a constant value of type 'Web.Admin.system.User'. Only primitive types or enumeration types are supported in this context."
My code is:
//Entities
public class Category
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
public class Order
{
public Guid Id { get; set; }
public string Description { get; set; }
public Guid CategoryId { get; set; }
public Guid UserId { get; set; }
}
//class added just for getting the user list (possibly, I do not need)
public class User
{
public Guid Id { get; set; }
public String Name { get; set; }
}
Here is my code preparing the filter
//retrieve the data from Order and Category
IQueryable<Order> orders = orderService.GetAllOrders();
IQueryable<Category> category = categoryService.GetAllCategories();
//obtain the users
MembershipUserCollection members = Membership.GetAllUsers();
// 1st option for managing users directly with memberShip variable
var memberShip = members.Cast<MembershipUser>().ToDictionary(m => m.ProviderUserKey, m => m.UserName).AsQueryable();
// 2nd option, I have added this code to see if I could manage the users as a list
List<User> users = new List<User>();
foreach (var _member in memberShip)
{
users.Add(new User { Id = (Guid)_member.Key, Name = _member.Value });
}
//Getting information to fill a listview
var DDLsource = from i in orders
join c in category on i.CategoryId equals c.Id
join u in users on i.UserId equals u.Id // 1st I tried to use memberShip directly but gave me error of types
select new
{
i.Id,
i.Description,
CategoryName = c.Name,
UserName = u.Name
};
ListViewOrders.DataSource = DDLsource.ToList();
Here is where the Error is triggered, I'm trying to understand the error and do other solution, I tested the query like:
Example 2
var DDLsource = from i in orders
join c in category on i.CategoryId equals c.Id
select new
{
i.Id,
i.Description,
CategoryName = c.Name,
UserName = (from u in users where u.Id == i.UserId select u.Name)
};
Example 3
var DDLsource = from i in orders
join c in category on i.CategoryId equals c.Id
join u in Membership.GetAllUsers().Cast<MembershipUser>() on i.UserId equals ((Guid)u.ProviderUserKey)
select new
{
i.Id,
i.Description,
CategoryName = c.Name,
UserName = u.UserName
};
all with the same results, could someone give me a hand with my mistake will surely be very obvious. Thanks in advance
I would do something like this (sorry, untested code...):
var DDLsource =
from i in orders
join c in category on i.CategoryId equals c.Id
select new
{
i.Id,
i.Description,
CategoryName = c.Name,
i.UserId,
UserName = ""
};
foreach(var ddl1 in DDLsource)
ddl1.UserName = Membership.GetUser(ddl1.UserId).Name;
I am having trouble figuring out how to traverse a one to many relasionship using LINQ-To-SQL in my asp.net site that uses EF 5. I have made the relationships in the class files but when I try to go from parent to child in my where clause I am not given a list of the child columns to filter on. Can anyone tell me what is wrong with my code, I am new to EF and LINQ.
Product.cs:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Category Category { get; set; }
}
}
Category.cs:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public virtual IList<Product> Products { get; set; }
}
Codebehind:
using (var db = new Compleate())
{
rpBooks.DataSource = (from c in db.Categories
where c.Products.Name == "Books"
select new
{
c.Name
}).ToList();
}
Do you want all products in the books category?
from p in db.Products
where p.Category.Name == "Books"
select new
{
p.Name
}
Or do you want to have all categories that contain products that are called called books?
from c in db.Categories
where c.Products.Contains( p => p.Name == "Books")
select new
{
c.Name
}
BTW, if you're only selecting the name, you can skip the anonymous type in the select part...
select p.name
Ok I had to update the codebhind to look like:
using (var db = new Compleate())
{
rpBooks.DataSource = (from c in db.Categories
join p in db.Products on c.ID equals p.id
where c.Products.Name == "Books"
select new
{
c.Name
}).ToList();
}
It should be name = c.Name it's not an issue with traversing, it's an issue with syntax, read the brief article on anonymous types here