Linq2Couchbase joins on List<> to id - c#

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};

Related

Query 1:n relationships in RavenDb

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

Order by repeated times LINQ ASP.NET MVC

I have this Report model:
public class Report
{
[Key]
public int Id { get; set; }
public string ReporterId { get; set; }
[ForeignKey("ReporterId")]
public virtual ApplicationUser Reporter { get; set; }
public int ProductId { get; set; }
[ForeignKey("ProductId")]
public virtual Product Product { get; set; }
public ReportType ReportType { get; set; }
}
Now, I want to return a List<Report> ordered by the most repeated element based on ProductId to the least repeated element.
Is there any way on how to do this with linq?
Try this:
var newList = List.GroupBy(p=> p.ProductId )
.OrderByDescending(x=> x.Count())
.SelectMany(x=> x).ToList();

Fetch multiple images from .NET backend and display them in angular

I'm trying to build a full-stack car rental project. I've got CarImage and Car, CarDetailDto classes, and relative Business/Data operations. I'm sending CarDetailDto properties to the Angular project, not Car class properties. I wrote a Linq in order to send CarDetailDto values to the front-end project. I try to show CarDetailDto values on a component that's called CarDetailComponent.
Here are my classes.
Car
public class Car:IEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid CarId { get; set; }
[ForeignKey("Brand")]
public Guid BrandId { get; set; }
[ForeignKey("Color")]
public Guid ColorId { get; set; }
public int ModelYear { get; set; }
public decimal DailyPrice { get; set; }
public string Description { get; set;}
}
CarImage
public class CarImage : IEntity //no naked class principle.
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid CarImageId { get; set; }
[ForeignKey("Car")]
public Guid CarId { get; set; }
public string ImagePath { get; set; }
public DateTime Date { get; set; }
}
CarDetailDto
public class CarDetailDto:IDto
{
public Guid CarId { get; set; }
public string BrandName { get; set; }
public Guid BrandId { get; set; }
public Guid ColorId { get; set; }
public List <string> ImagePaths { get; set; }
public ImagePath {get; set;}
public string ColorName { get; set; }
public int ModelYear { get; set; }
public decimal DailyPrice { get; set; }
public string Description { get; set; }
}
The Linq that I wrote to send CarDetailDto values to the Angular project through the WebAPI.
public class EfCarDal:EfEntityRepositoryBase<Car, ReCapDBContext>, ICarDal
{
public async Task<List<CarDetailDto>> GetCarDetails()
{
using(ReCapDBContext context = new ReCapDBContext())
{
var allDetails = await (
from c in context.Cars
join b in context.Brands
on c.BrandId equals b.BrandId
join col in context.Colors
on c.ColorId equals col.ColorId
join Imp in context.CarImages
on c.CarId equals Imp.CarId
select new CarDetailDto
{
CarId = c.CarId,
BrandName = b.BrandName,
ColorName = col.ColorName,
DailyPrice = c.DailyPrice,
Description = c.Description,
ModelYear = c.ModelYear,
BrandId = c.BrandId,
ColorId = c.ColorId,
ImagePath = Imp.ImagePath
}).ToListAsync();
return allDetails; //return queryable to list.
}
}
}
The GetCarDetails() function that I'm using in my Business Layer where I'm calling the linq above.
public async Task<IDataResult<List<CarDetailDto>>> GetCarDetails()
{
var result = await _carDal.GetCarDetails();
return new SuccessDataResult<List<CarDetailDto>>(result, Messages.CarDetails);
}
I'm trying to fetch photos that have the same CarId in Linq query as you can see above. Problem with the function above is: it brings the same CarDetail object repeatedly whenever it finds a match between CarImage & Car objects. Only the ID value of the CarDetailDto object changes.
GetCarDetails() / Swagger
What I'm trying to do is: create a List, put the CarImages with the same CarId in the same List and send the List to the Angular project. I don't want GetCarDetails() function to bring the same CarID for each CarImageId.
How can I do this?
What I've tried:
Tried to create a List in CarDetailDto object and add values to the list in the Linq above.

EF Core join on sub table

I have three classes set up like this:
public class Item
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<UserItem> UserItems { get; set; }
}
public class User
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<UserItem> UserItems { get; set; }
}
public class UserItem
{
public int ID { get; set; }
public int UserID { get; set; }
public int ItemID { get; set; }
}
Entity Framework generates the tables in the SQL Server database with the correct foreign keys. I can query the Item and User to get the UserItem collections within like this:
var a = context.DBUser.Include(s => s.UserItems);
var b = context.DBItem.Include(s => s.UserItems);
What I am not happy about is how I get from the User to the Item. In SQL I would do something like this
SELECT * FROM Users U
LEFT JOIN UserItems UI
ON U.ID = UI.UserID
LEFT JOIN Items I
ON UI.ItemID = I.ID
In C# I've needed to resort to this:
var c = from user in _context.DBUsers
join userItem in _context.DBUserItems
on user.ID equals userItem.UserID into ui
from userItem in ui.DefaultIfEmpty()
join item in _context.DBItems
on userItem.ItemID equals item.ID into i
from item in i.DefaultIfEmpty()
select new
{
user,
userItem,
item
};
Which given there should already be relationships between the tables in EF doesn't feel right
You need to add navigations to UserItem also:
public class UserItem
{
public int ID { get; set; }
public int UserID { get; set; }
public int ItemID { get; set; }
public User User { get; set; }
public Item Item { get; set; }
}
Then your query can be like this:
var users = context.DBUser
.Include(x => x.UserItems)
.ThenInclude(x => x.Item);
Also note that if you are using .NET 5, the UserItem "join entity" can be omitted altogether:
public class Item
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<User> Users { get; set; }
}
public class User
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Item> Items { get; set; }
}
Which would simplify your query:
var users = context.DBUser.Include(x => x.Items);

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;
}
}
}

Categories