In the following example in Entity Framework, how to find the author of a specified book using linq:
public class Author
{
public int Id { get; set; }
public string AuthorName { get; set; }
public ICollection<Book> Books { get; set; }
}
public class Book
{
public int Id { get; set; }
public int Title { get; set; }
}
Thanks.
Assuming you have a collection of authors, you would simply do
var author = authors.SingleOrDefault(x=> x.Books.Any(y=> y.Title.Equals(bookTitle, StringComparison.OrdinalIgnoreCase))
This assumes that books have only one author.
You can achieve it in this simple way, Demo on dotnetfiddle
var result = authors.SelectMany(a => a.Books.Select(b => new { BookTitle = b.Title, AuthorName = a.AuthorName }));
Related
I have an issue with my Get method. In my BookRepository I have this method:
public Book GetBook(int id)
{
return _context.Books
.Include(a => a.BookAuthors)
.Single(b => b.Id == id);
}
These are my 3 classes:
public class Book
{
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<BookAuthor> BookAuthors { get; set; }
public string PublishingHouse { get; set; }
public int NumberOfPages { get; set; }
public int ISBN { get; set; }
}
public class Author
{
public int Id { get; set; }
public string FirstName { get; set; }
public string SecondName { get; set; }
public string Nationality { get; set; }
public virtual ICollection<BookAuthor> BookAuthors { get; set; }
}
public class BookAuthor
{
public int BookId { get; set; }
public Book Book { get; set; }
public int AuthorId { get; set; }
public Author Author { get; set; }
}
I want display details book on the screen, but when I executed method GetBook, I have get something weird -model where is a Book, which includes BookAuthors, which includes... Book! And again, and again...
Edit: AuthorId is ok, but Author is null - why?
And I don't have any idea how I can display Authors in the view? By foreach?
You only included BookAuthor. You didn't include Author.
Change your GetBook to
public Book GetBook(int id)
{
return _context.Books
.Include(a => a.BookAuthors)
.Include("BookAuthor.Author")
.Single(b => b.Id == id);
}
Database Models of my Application are:
public class Restaurant
{
public int Id { get; set; }
.........
}
public class Review
{
public int Id { get; set; }
public string ReviewTitle { get; set; }
public string ReviewContent { get; set; }
public int UserId { get; set; }
public int RestaurantId { get; set; }
}
public ReviewHelpful
{
public int Id { get; set; }
public int ReviewId { get; set; }
public bool IsHelpfull { get; set; }
}
public ReviewImage
{
public int Id { get; set; }
public string ImageLink { get; set; }
public int ReviewId { get; set; }
}
There is no navigation property in any table. In ReviewHelpful table, If user finds helpfull of this review than value is true otherwise false.
Now I want to create a view-model Like this:
public class ReviewViewModel
{
public int ReviewId { get; set; }
public int RestaurantId { get; set; }
public string ReviewTitle { get; set; }
public string ReviewContent { get; set; }
public int UserId { get; set; }
public int NumberOfHelpfull { get; set; }
public int NumberOfNotHelpfull { get; set; }
public List<string> ImagesLinks { get; set; }
}
For that reason, I want to write this kind of query :
var reviews = (from review in _foodTrackerContext.RestaurantReviews
join helpful in _foodTrackerContext.Helpfuls on review.Id equals helpful.ReviewId
join reviewPicture in _foodTrackerContext.ReviewPictures on review.Id equals reviewPicture.ReviewId
where review.ResturantId == 2
select new ReviewViewModel()
{
Id = review.Id,
RestaurantId = 2,
ReviewTitle = review.ReviewTitle,
ReviewContent = review.ReviewContent,
NumberOfHelpfull = .. ??,
NumberOfNotHelpfull = ... ??,
ImagesLinks = ... ???
}
I can not retrieve HelpfulYes, HelpfulNo, ImagesLinks with this query. What would be query for finding these variables?.
This query produces multiple rows for single review with each ReviewImage and each ReviewHelpful.
The query that ypu need to do is this one:
var model =
from review in ctx.Reviews
where review.RestaurantId == 2
join helpful in ctx.ReviewHelpfuls
on review.Id equals helpful.ReviewId into helpfuls
join image in ctx.ReviewImages
on review.Id equals image.ReviewId into images
select new RestaurantReviewViewModel
{
Id = review.Id,
RestaurantId = 2,
ReviewTitle = review.ReviewTitle,
ReviewContent = review.ReviewContent,
NumberOfHelpfull = helpfuls.Count(h => h.IsHelpfull),
NumberOfNotHelpfull = helpfuls.Count(h => !h.IsHelpfull),
ImagesLinks = (from image in images select image.ImageLink).ToList()
};
Please, note that when you do a one to manyh join you need to include an into to give a nameto the joined entities to be able to work on them.
I've used the dot syntax for selecting the count, but you could use the query syntax if you wanted. Over time, I've found dot synatx more natural.
NOTE: if you used navigation properties this would become much easier. Why are you not using them? With navigation properties you don't need to make the joins explicitly, as they are already available.
List<ReviewViewModel> listModel = new List<ReviewViewModel>();
context.dbRestaurant
.include("Review")
.include("Review.ReviewHelpful")
.include("Review.ReviewImage").ToList().ForEach((item) =>
{
ReviewViewModel model = new ReviewViewModel();
model.ID = item.ID
listModel.Add(model);
});
I am working on Web-API project and using Entity Framework with Generic Repository and Unit Of work. Basically i follow a tutorial for this.
Here is my table architecture.
Entity
public class ProductEntity
{
public int ProductId { get; set; }
public string ProductCode { get; set; }
public string ProductName { get; set; }
public string ProductDescription { get; set; }
public string ProductImgName { get; set; }
public bool IsActive { get; set; }
public int PrimaryCatId { get; set; }
public int SecondaryCatId { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
public System.DateTime CreateDate { get; set; }
public List<PrimaryProductEntity> objPrimaryProduct { get; set; }
public List<SecondaryProductEntity> objSecondaryProduct { get; set; }
}
public class PrimaryProductEntity
{
public int PrimaryCatId { get; set; }
public string PrimaryCatName { get; set; }
}
public class SecondaryProductEntity
{
public int SecondaryCatId { get; set; }
public string SecondaryCatName { get; set; }
public int PrimaryCatId { get; set; }
}
Services Code
public IEnumerable<BusinessEntities.ProductEntity> GetAllProducts()
{
var products = _unitOfWork.ProductRepository.GetAll().ToList();
var primaryProducts = _unitOfWork.PrimaryProductRepository.GetAll().ToList();
var secondaryProducts = _unitOfWork.SecondaryProductRepository.GetAll().ToList();
if (products.Any())
{
Mapper.CreateMap<tblProduct, ProductEntity>();
var proInfo = from P in products
join PP in primaryProducts on P.PrimaryCatId equals PP.PrimaryCatId
join SP in primaryProducts on P.SecondaryCatId equals SP.SecondaryCatId
select P;
var productsModel = Mapper.Map<List<tblProduct>, List<ProductEntity>>(proInfo);//getting error
return productsModel;
}
return null;
}
i know my implementation is wrong, i don't know what to write in code for fetch data from multiple tables. Please help me.
Required Data
ProductID,ProductName, PrimaryCatName, SecondaryCatName,Price, Quantity
Your Product Entity class Doesn't require a List<PrimaryProductEntity> and List<SecondaryProductEntity>. I suppose according to your class diagram Each Product is associated with one PrimaryProductEntity and one SecondaryProductEntity.
Once your model class is corrected, you would be able to access the properties of the navigation. I am not so good with writing a Query the way you want. But i hope you could get an idea of what you should be doing
Post class:
public class Post
{
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
[BsonRepresentation(BsonType.ObjectId)]
public string CreatorId { get; set; }
public string CreatorName { get; set; }
public string Text { get; set; }
public bool IsPublic { get; set; }
public ICollection<Comment> Comments { get; set; }
public DateTime CreationDate { get { return ObjectId.Parse(Id).CreationTime; } }
public DateTime? DeletionDate { get; set; }
}
Comment class:
public class Comment
{
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
[BsonRepresentation(BsonType.ObjectId)]
public string CreatorId { get; set; }
public string CreatorName { get; set; }
public string Text { get; set; }
public DateTime CreationDate { get { return ObjectId.Parse(Id).CreationTime; } }
public DateTime? DeletionDate { get; set; }
}
I would like to find set the DeletionDate property of a Comment inside a Post.Comments given that Comment.Id and Comment.CreatorId equals the given parameters.
How can I do this?
QueryDocument query= new QueryDocument();
query.Add(new BsonDocument("Comments.$.Id","givenCommentId"));
UpdateDocument update = new UpdateDocument();
update.Add(new BsonElement("Comments.$.DeletionDate", "yourUpdatedDate"));
You can use it in FindAndModifyArgs
However I have forgotten whehther positional operator $ can be used for two or more fields,so I dont add new BsonDocument("Comments.$.CreatorId","givenCommentCreatorId") in query.You need to test it.
I solved it with the following:
var query = Query<Post>.ElemMatch(p => p.Comments, builder =>
builder.And(
Query<Comment>.EQ(c => c.Id, commentId),
Query<Comment>.EQ(c => c.CreatorId, creatorId)));
var update = Update.Set("Comments.$.DeletionDate", DateTime.UtcNow);
var args = new FindAndModifyArgs
{
Query = query,
Update = update
};
var result = _db.Posts.FindAndModify(args);
I'm trying to do what seems fairly simple but I'm getting a null reference....
I have a null on the assoc files property in the last statement...
TestInfo.AggregateRoutes.MainBlogEntry = new Blog { BlogType = 1, Title = TestInfo.UniqueRecordIdentifier, Description = TestInfo.UniqueRecordIdentifier, DateAdded = DateTime.Now, User = TestInfo.UniqueRecordIdentifier };
IBlogRepository blogRepo = new BlogRepository();
var assocFile = new AssocFile { Name = TestInfo.UniqueRecordIdentifier, Url = TestInfo.UniqueRecordIdentifier };
TestInfo.AggregateRoutes.MainBlogEntry.AssocFiles.Add(assocFile);
This is the code I have written to support what I'm trying to do...
public class PteDotNetContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<AssocFile> AssocFiles { get; set; }
}
public class Blog
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int BlogId { get; set; }
public int BlogType { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public DateTime DateAdded { get; set; }
public string User { get; set; }
public virtual ICollection<AssocFile> AssocFiles { get; set; }
}
public class AssocFile
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int AssocFileId { get; set; }
public int BlogId { get; set; }
public string Url { get; set; }
public string Name { get; set; }
public virtual Category Category { get; set; }
}
I thought the whole point in declaring it virtual was that it would create a foreign key constraint?
When you instantiate an entity you also need to initialize the collection navigational properties before you access it for the first time. In your case MainBlogEntry.AssocFiles = new List<AssocFile>();. The reason for this is, your property implementation does not contain any logic to initialize the collection.
When EF creates new instances of your entities, it sub classes your entities (ie Proxy Creation) and over ride the default functionality of your properies.
TestInfo.AggregateRoutes.MainBlogEntry = new Blog { BlogType = 1, Title = TestInfo.UniqueRecordIdentifier, Description = TestInfo.UniqueRecordIdentifier, DateAdded = DateTime.Now, User = TestInfo.UniqueRecordIdentifier };
IBlogRepository blogRepo = new BlogRepository();
var assocFile = new AssocFile { Name = TestInfo.UniqueRecordIdentifier, Url = TestInfo.UniqueRecordIdentifier };
TestInfo.AggregateRoutes.MainBlogEntry.AssocFiles = new List<AssocFile>();
TestInfo.AggregateRoutes.MainBlogEntry.AssocFiles.Add(assocFile);