Query 1:n relationships in RavenDb - c#

i'm having problems indexing my data in RavenDB in a way I can query it as deserved:
I have a list of Documents of type Product:
public class Product
{
public string Id => Asin;
public string Url { get; set; }
public string Name { get; set; }
public string Asin => Url.Split("/").LastOrDefault();
public string Description { get; set; }
public List<Category> Categories { get; set; } = new List<Category>();
}
and a list of type Snapshot:
public class Snapshot
{
public string Asin { get; set; }
public DateTime Timestamp { get; set; }
public string Price { get; set; }
public int? Ratings { get; set; }
}
I should be able to create a 1:n relation between Product and Snapshot, meaning:
One Product with a unique field Asin can have many Snapshots with that Asin that can be identified by a unique field Timestamp like:
public class ProductWithSnapshots
{
public Product P { get; set; }
public List<Snapshot> S { get; set; }
}
=> which I could query the way I need...
I am absolutely new to RavenDb and would be delighted if there was anyone out there who could help. I digged into the documentation (Indexing / Map-Reduce, Querying, etc.) and many posts, but I simply cannot find a better way to address my problem.
Many thanks, guys!
Max

Related

Linq2Couchbase joins on List<> to id

I have two objects
public class Card : IEntity
{
public Guid Id { get; set; }
public string Title { get; set; }
public string BackgroundColour { get; set; }
public string Texture { get; set; }
public List<Guid> Category { get; set; }
public string Notes { get; set; }
}
and
public class Category : IEntity
{
public Guid Id { get; set; }
public string Name { get; set; }
public Guid Parent { get; set; }
}
What I would like to do is, using the Couchbase SDK, join these two objects together (Card.Category => Category.Id) in one database hit (Like a classic SQL Stored Procedure that brings me back all the data to populate both objects), but all of the (examples I found have it from id => id rather than a List of Ids (Many) to (one) id.
An example of something I tried to do...
var query = from card in context.Query<Card>()
from categoryId in card.Category.Ids
join category in context.Query<Category>()
on categoryId equals category.Id
select new CardView { Id = card.Id, Categories = category};

Entity Framework 6 Code First - Relationship between 2 tables using an existing DB

I am having some hard time to get data from Detail table, but I can get data back from the DetailKey table. This is what I have done so far and thank you for any help.
public class DetailKey
{
public int Id { get; set; }
public string System { get; set; }
public string KeyValue { get; set; }
public Nullable<int> DetailId { get; set; }
}
public class Detail
{
public Detail()
{
this.DetailKeys = new HashSet<DetailKey>();
}
public int Id { get; set; }
public System.DateTimeOffset CreatedDatetime { get; set; }
public System.DateTimeOffset UpdatedDatetime { get; set; }
public string Name { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public DateTime BirthDate { get; set; }
public virtual ICollection<DetailKey> DetailKeys { get; set; }
}
public class MyDbContext : DbContext
{
public MyDbContext() :base("name=dbConnectionString"){}
public DbSet<Detail> Details {get; set;}
public DbSet<DetailKey> DetailKeys {get; set;}
}
using(var db = new MyDbContext())
{
db.Details; // I get no result.
db.DetailKeys; // I get an exception System.Data.Entity.Core.EntityCommandExecution.Exception
{
If you mean that why there is no related Detail class in DetailKey class;
You should add foreign key to DetailKey table
I think the problem with the Detail table is that there is some unmatched field. However, I come around this problem by using ADO.NET Entity Data Model and then select Code First from database. Now I get all my entities and all the data back. I cannot believe I had spend so much time on this.

Custom relationship table in many to many relationship

So, I'm writing an e-commerce application and I'm trying to create a many to many relationship between the Product and Size classes. Entities looks like that:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
public class Size
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ProductSize
{
public int Id { get; set; }
public int ProductId { get; set; }
public int SizeId { get; set; }
public string Amount { get; set; }
public virtual Size Size { get; set; }
public virtual Product Product { get; set; }
}
Normally I wouldn't explicitly create this ProductSize table, because EF would create it for me. However, I need the Amount column to sit there. Now, for getting info about products with sizes and amounts I have to create a very explicit query (2 joins). If I hadn't manually created the relationship class and just give the Product class a virtual ICollection of Size, getting the complete info would be just a simple matter. So if I had selected a Product instance, then all sizes would be loaded into it's virtual ICollection<Size>.
The question is - can I achieve the same level of simplicity with the entities structure given above? Instead of writing a 2 join query where I explicitly mention every column I want, and then pack it into some ViewModel I'd like to use the simpler syntax for getting a Product instance and its related data (so also the data that's sitting in the intermediate table).
Add an Inverse Navigation Property to the ProductSize from Product and Size like this:
public class Product {
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public virtual ICollection<ProductSize> ProductSizes { get; set; }
}
public class Size {
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ProductSize> ProductSizes { get; set; }
}
public class ProductSize {
public int Id { get; set; }
public int ProductId { get; set; }
public int SizeId { get; set; }
public string Amount { get; set; }
public virtual Size Size { get; set; }
public virtual Product Product { get; set; }
}
Doing so, you should be able to do like in the following example:
from p in Context.Products
where p.ProductSizes.Where(ps => ps.Amount > 0 && ps.Size.Name.Equals("Big")).Any()
select p;

How do I get the EntityFramework to check for 2 parameters?

I have 3 related objects (non relevant properties omitted for brevity):
public class Product
{
public int ID { get; set; }
public virtual ProductPrice Price { get; set; }
}
public class ProductPrice
{
public int ID { get; set; }
public int ProductID { get; set; }
public int VerticalID { get; set; }
public decimal Value { get; set; }
public virtual Product Product { get; set; }
public virtual Vertical Vertical { get; set; }
public override string ToString()
{
return Value.ToString("C");
}
}
public class Vertical
{
public int ID { get; set; }
public string Name { get; set; }
}
Product price varies based on the current "vertical". The current vertical will probably (eventually) be stored in a session, but for the time being, let's assume that this will be a query string parameter. (e.g. mydomain.com?VerticalID=2).
My question
When a user visits mydomain.com/products?VerticalID=2 or mydomain.com/products/?VerticalID=2 how can I get the Entity Framework to select/assign the correct price based on the ProductID and the VerticalID - making this possible?:
#Model.Price.ToString()
Update 1 (sample data and DB structure)
Here are my tables with dummy content:
Products
ProductPrices
Verticals
Relationship Explanation
There should be one price, per product, per vertical. The query would look something like:
-- Let's assume ProductID = 2 and VerticalID = 1 (e.g. mydomain.com/products/2?VerticalID=1)
SELECT * FROM ProductPrices WHERE ProductID = 2 AND VerticalID = 1
The above query would return 1 row (which is what it should always return)
Update 2 (another example)
For illustrative purposes I added the VerticalID property to Product:
public class Product
{
public int ID { get; set; }
public string Manufacturer { get; set; }
public string Model { get; set; }
public string PartNumber { get; set; }
public int CategoryID { get; set; }
public string Description { get; set; }
[NotMapped]
public int VerticalID = 1;
public virtual ProductCategory Category { get; set; }
public virtual ProductPrice Price { get; set; }
public virtual ICollection<ProductImage> Images { get; set; }
public virtual ICollection<ProductDocument> Documents { get; set; }
public virtual ICollection<ProductDetail> Details { get; set; }
public virtual ICollection<RelatedProduct> RelatedProducts { get; set; }
}
Now, when actually trying to execute this, I am getting the following error:
Unable to determine the principal end of an association between the types 'Print_Solutions.Models.ProductPrice' and 'Print_Solutions.Models.Product'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
How can I tell entity to use both VerticalID and Product.ID when retrieving the price? (using the test data I have, if this was product 1, this product should map to ID 1 of the product price table, and cost $100).
Supposing your DbContext has a collection of ProductPrice named ProductPrices, using LINQ you simply has to make this query:
var price = ctx.ProductPrices.Where(pp =>
pp.ProductId = productId && pp.VerticalId == verticalId).SingleOrDefault();
Where productId and verticalId are the available paramters that come from the action paramters, the session, or wherever they are.
The use of single or default warranties that there's only one value on the database, or that there is none, and, on that case, you get null as a result of the query.
As for your updates I see that your problem is also related to the definition of the relations in the model.
There are 3 ways to achieve it:
using EF conventions. To achive this, change the name of the ID properties of your entites: for example use ProductId, instead of ID and the conventions will build the model for you
using attributes. In this particular case use ForeignKeyAttribute where it applies
using the fluent API
You have some more info on relationships here, with a few simple samples.
I was never able to figure out how to fix the models or use the fluent API. I did some lazy loading instead. If anyone has a better solution, please post it.
public class Product
{
public int ID { get; set; }
public string Manufacturer { get; set; }
public string Model { get; set; }
public string PartNumber { get; set; }
public int CategoryID { get; set; }
public string Description { get; set; }
public int VerticalID = 1;
private ProductPrice _price;
public virtual ProductCategory Category { get; set; }
public virtual ICollection<ProductImage> Images { get; set; }
public virtual ICollection<ProductDocument> Documents { get; set; }
public virtual ICollection<ProductDetail> Details { get; set; }
public virtual ICollection<RelatedProduct> RelatedProducts { get; set; }
// Lazy Loading
public ProductPrice Price
{
get
{
if (_price == null)
{
var db = new ApplicationContext();
_price = db.Prices.FirstOrDefault(p => p.ProductID == ID && p.VerticalID == VerticalID);
}
return _price;
}
}
}

Recipe - ingredients database in Entity Framework ASP.NET MVC

This will create two tables "Ingredient" and "Recipe" and an additional table for many-to-many mapping.
public class DC : DbContext {
public DbSet<Ingredient> Ingredients { get; set; }
public DbSet<Recipe> Recipes { get; set; }
}
public class Ingredient {
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Recipe> Recipes { get; set; }
}
public class Recipe {
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Ingredient> Ingredients { get; set; }
}
Question: I want to include additional column "quantity" in the third mapping table that will be created by Entity Framework. How to make that possible? Thanks in advance.
When you've got some extra information, I suspect it won't really count as a mapping table any more - it's not just a many-to-many mapping. I think you should just model it as another table:
public class Ingredient {
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<RecipePart> RecipeParts { get; set; }
}
public class RecipePart {
public int Id { get; set; }
public Ingredient { get; set; }
public Recipe { get; set; }
// You'll want to think what unit this is meant to be in... another field?
public decimal Quantity { get; set; }
}
public class Recipe {
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<RecipePart> Parts { get; set; }
}
So now you don't really have a many-to-many mapping - you have two ordinary many-to-one mappings. Do you definitely need to "ingredient to recipes" mapping exposed in your model at all? If you want to find out all the recipes which use a particular ingredient, you could always do a query such as:
var recipies = DB.Recipies.Where(r => r.Parts
.Any(p => p.Ingredient == ingredient));

Categories