Including an object with where clause in LINQ - sql

I would like to have a LINQ query that is supposed to return all members with VitalSigns where event in the vital signs is equal to surgery.
My Member.cs class:
public class Member
{
public int Id { get; set; }
public string FullName { get; set; }
public ICollection<VitalSign> VitalSigns { get; set; }
public Member()
{
VitalSigns = new Collection<VitalSign>();
}
}
And my VitalSign.cs class is :
public class VitalSign
{
public int Id { get; set; }
public string Event { get; set; }
// relationships
public Member Member { get; set; }
public int MemberId { get; set; }
}
The LINQ query that I wrote is:
return await context. Members.Include(c => c.VitalSigns.Where(t => t.Event == "post surgery")).ToListAsync();
This returns a self-referenced loop. Because there are some data in the VitalSigns where the event is not equal to "post surgery". Am I writing the query wrong?

The query should be:
context.Members.Where(t => t.VitalSigns.Any(u => u.Event == "post surgery"))
.Include(c => c.VitalSigns)
.ToListAsync()
The Include() is only an hint on what tables should be loaded when the query is executed.
The query is something like:
all the members WHERE there is ANY (at least) one VitalSign with Event == post surgery
together with the Members you'll get, please INCLUDE the VitalSigns (the alternative is that they'll be lazily loaded when you try to access them)
return a List<> (ToListAsync) of the elements in an asynchronous way

Related

Get value in 1-to-many relationship (EF Core)

I'm seeking an equivalent to this SQL query in EF Core:
SELECT Id, Name, (Select AdminRoleId From EventAdmins Where EventId = Events.Id And AdminId = [value from cookie]) As EventRoleId From Events
This is what I have so far:
public IList<Event> Events { get; set; }
public IList<EventAdmin> EventAdmins { get; set; }
public async Task<IActionResult> OnGetAsync() {
var adminId = Guid.Parse(Request.Cookies["Adm_AdminId"]);
Events = await _context.Events.SelectMany(e => e.EventAdmins.Where(x => x.EventId == e.Id && x.AdminId == adminId).Select(x => x.AdminRoleId)).AsNoTracking().ToListAsync();
return Page();
}
I'm not sure what's wrong, but I get an error saying "Error CS0452: The type 'Guid' must be a reference type in order to use it as parameter 'TEntity' in the generic type or method".
Event model:
public class Event {
public Guid Id { get; set; }
public string Name { get; set; }
[ForeignKey("Id")]
public IList<EventAdmin> EventAdmins { get; set; }
}
EventAdmin model:
public class EventAdmin {
public Guid Id { get; set; }
public Guid EventId { get; set; }
public Guid AdminId { get; set; }
public Guid AdminRoleId { get; set; }
[ForeignKey("EventId")]
public Event Events { get; set; }
}
Reason
This error occurs because you're trying to ask EF Core not to track a list of GUID. However, the list of Guid is a list of value types.
As you know, EF Core can only track a series of reference type, so the method signature of AsNoTracking<TEntity>() is :
public static IQueryable<TEntity> AsNoTracking<TEntity> (this IQueryable<TEntity> source)
where TEntity : class;
Note the constraints of where TEntity : class.
In other words, you can never invoke AsNoTracking<Guid>():
Events = await _context.Events
.SelectMany(e => e.EventAdmins.Where(x => x.EventId == e.Id).Select(x => x.AdminRoleId))
.AsNoTracking() // Actually, it will invoke `AsNoTracking<Guid>()`
.ToListAsync();
How to Fix
Your SQL doesn't seem valid. I guess you want to return a {Id, Name, EventRoleId}.
If you would like to do that with SelectMany, you could simply query as below:
var Events = await this._context.Events
.SelectMany(
e => e.EventAdmins.Where(x => x.EventId == e.Id).Select(x => x.AdminRoleId),
(p,g) => new {
Id = p.Id,
Name = p.Name,
EventRoleId = g
}
)
// .AsNoTracking()
.ToListAsync();
There's no need to call .AsNoTracking() at all. Because no tracking is performed if the result set does not contain any entity types.
As a side note, you shouldn't decorate the Event.EventAdmins with a [ForeignKey("Id")] attribute :
public class Event {
public Guid Id { get; set; }
public string Name { get; set; }
[ForeignKey("Id")]
public IList EventAdmins { get; set; }
}
Because the Event is the principal entity and the EventAdmin is the dependent entity. Only the EventAdmin has a foreign key that references the Event.
Your LINQ query doesn't seem to include adminId so I can't see how it could work. Try something like this:
var eventAdmin = _context.EventAdmin.SingleOrDefault( e => e.Id == adminId);
var events = eventAdmin.Events;
Try using a tool like Linqpad to dissect your queries one step at a time.

Query entries by nested collection elements in RavenDB

I'm new to RavenDB and I'm struggling with this simple (i guess) issue.
I have a Subscriber with a collection of Subscriptions. And I want to make search by Subscription's fields, and return related Subscriber.
Here are simplified class examples:
public class Subscriber
{
public string Email { get; set; }
public string Name { get; set; }
public string Phone { get; set; }
public List<Subscription> Subscriptions { get; set; }
}
public class Subscription
{
public Guid Id { get; set; }
public string EventType { get; set; }
}
I've tried to make an index, as it is said in RavenDB docs:
public class Subscriber_BySubscription : AbstractIndexCreationTask<Subscriber>
{
public Subscriber_BySubscription()
{
Map = subscribers => from subscriber in subscribers
from subscription in subscriber.Subscriptions
select new
{
subscription.EventType,
subscription.QueueName
};
}
}
But I'm not sure that this is what I need, since query by collection using Select and Contains doesn't work. Moreover, the code looks so ugly that I feel that this is not the way how it should be.
So, I'd like to query Subscriptions by EventType, and have corresponding Subscriber as a result. In LINQ it would look like this: subscribers.Where(x => x.Subscriptions.Select(c => c.EventType).Contains(myEventType))
Managed to do it. Here is the right index:
public class Subscriber_BySubscription : AbstractIndexCreationTask<Subscriber>
{
public class Result
{
public string EventType { get; set; }
}
public Subscriber_BySubscription()
{
Map = subscribers => from subscriber in subscribers
from subscription in subscriber.Subscriptions
select new
{
subscription.EventType
};
}
}
And that's how it should be used:
var results = uow.Session
.Query<Subscriber_BySubscription.Result, Subscriber_BySubscription>()
.Where(x => x.EventType == eventType)
.OfType<Subscriber>()
.ToList();

Modeling a 1:1 optional-on-both-sides relationship

I have three entities:
User - can have many Reviews and can have many Transactions
Transaction - must have a FromUser and ToUser, can have FromUserReview or ToUserReview
Review - Can have Transaction, must have FromUser and ToUser
The idea is that users may write reviews on one another, may send payments to each other. A user can only write one non-transactional review for another user - otherwise, reviews must be attached to transactions.
Essentially, this becomes a 1:1 optional-on-both-sides relationship between Transactions and Reviews. I was thinking about modeling this with a join table that contains:
ReviewId
TransactionId
And calling it TransactionReview. This seems to eliminate model/code duplication, but complicates my business logic.
Another alternative I see is creating two entities: UserReview and TransactionReview - which will simplify logic but will force me into code repetition and having two tables for what should be a single entity.
What is the right way to go about this? I am using Entity Framework code-first, in case it matters.
I have prepare some code, please check and try.
public class User
{
// properties
public int Id { get; set; }
public string Name { get; set; }
public virtual Address Address { get; set; }
public virtual ICollection<UserReview> UserReviewsFromMe { get; set; }
public virtual ICollection<UserReview> UserReviewsToUsers { get; set; }
public virtual ICollection<TransactionReview> TransactionReviews { get; set; }
}
public class Review
{
public int Id { get; set; }
public string Content { get; set; }
public string EntityName { get; set; }
public int EntityId { get; set; }
public virtual TransactionReview TransactionReview { get; set; }
public virtual UserReview UserReview { get; set; }
}
public class Transaction
{
public int Id { get; set; }
public string Note { get; set; }
public DateTime CreatedOnUtc { get; set; }
public virtual ICollection<TransactionReview> TransactionReviews { get; set; }
}
public class UserConfiguration : EntityTypeConfiguration<User>
{
public UserConfiguration()
{
ToTable("User");
HasKey(p => p.Id);
}
}
public class ReviewConfiguration : EntityTypeConfiguration<Review>
{
public ReviewConfiguration()
{
ToTable("Review");
HasKey(x => new { x.Id });
}
}
public class TransactionConfiguration : EntityTypeConfiguration<Transaction>
{
public TransactionConfiguration()
{
ToTable("Transaction");
HasKey(x => new { x.Id });
}
}
public class UserReview
{
public int Id { get; set; }
public int FromUserId { get; set; }
public int ToUserId { get; set; }
public virtual User FromUser { get; set; }
public virtual Review Review { get; set; }
public virtual User ToUser { get; set; }
}
public class TransactionReview
{
public int Id { get; set; }
public int TransactionId { get; set; }
public int UserId { get; set; }
public virtual Transaction Transaction { get; set; }
public virtual Review Review { get; set; }
public virtual User User { get; set; }
}
public class UserReviewConfiguration : EntityTypeConfiguration<UserReview>
{
public UserReviewConfiguration()
{
ToTable("UserReview");
HasKey(x => new { x.Id });
Property(a => a.Id).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None);
this.HasRequired(ur => ur.FromUser)
.WithMany(u => u.UserReviewsFromMe)
.HasForeignKey(ur => ur.FromUserId)
.WillCascadeOnDelete(false);
this.HasRequired(ur => ur.Review)
.WithOptional(r => r.UserReview);
this.HasRequired(ur => ur.ToUser)
.WithMany(u => u.UserReviewsToUsers)
.HasForeignKey(ur => ur.ToUserId)
.WillCascadeOnDelete(false);
}
}
In the above UserReviewConfiguration class, I mapped like this: A user can have zero or more UserReview's posted, a UserReview is posted by one user only and can be for one user only, and is mapped with one review only, making the Review and User entities independent as well if someone needs.
public class TransactionReviewConfiguration : EntityTypeConfiguration<TransactionReview>
{
public TransactionReviewConfiguration()
{
ToTable("TransactionReview");
HasKey(x => new { x.Id });
Property(a => a.Id).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None);
this.HasRequired(tr => tr.Transaction)
.WithMany(t => t.TransactionReviews)
.HasForeignKey(tr => tr.TransactionId);
this.HasRequired(tr => tr.Review)
.WithOptional(r => r.TransactionReview);
this.HasRequired(tr => tr.User)
.WithMany(u => u.TransactionReviews)
.HasForeignKey(tr => tr.UserId);
}
}
In the above TransactionReviewConfiguration class, I mapped like this: A user can have zero or more TransactionReview's posted, a TransactionReview is posted by one user only and can be for one Transaction only, and is mapped with one review only, making the User, Review and Transaction entities independent as well if someone needs.
Hope this helps...
I'd probably go with a simple data model:
User
Transaction (without storing the information about reviews here)
Review (a review must either be for a particular user or a transaction)
You could differentiate a review by it's type (a dictionary table) to know which review is standalone and which comes with a transaction.
You could go two ways about it:
have two columns for storing ids of Transaction and User entity and keeping nulls depending on the type
or having one column that would identify the id of an entity thanks to the type of a review
I don't see a need for TransactionReview entity, since one review can only be attached to 0..1 transaction. Since transactions can have 0..2 reviews this becomes a one-to-many relationship with optional zero elements.
I agree that it might complicate the logic of retrieval (having to remember that) but I find it very handy when dealing with data modelled like that.

Posting DropDownList value from View to Controller in MVC4

I have a MVC4 application and although I have get parameters for my DropDownList from the database, I encounter some kind of problems while posting the DropDownList value to the database. There is lots of samples for different approach, but I would like to apply a method without using an extra approach i.e. Ajax, Javascript, etc. On the other hand, I have run into "FormCollection" to pass data, but I am not sure if FormCollection is the best way in this scene. Here are some part of the view, controller and model I use:
View:
#using (Html.BeginForm("Add", "Product", FormMethod.Post,
new { enctype = "multipart/form-data" }))
{
<p>Product Type : #Html.DropDownListFor(m => m.SelectedLookupId, new SelectList(Model.Lookups.Where(x => x.LookupType == "Product Type"), "LookupID", "LookupValue"), "--- Select ---") </p>
Controller:
[HttpPost]
public ActionResult Add(Product product)
{
if (ModelState.IsValid)
{
product.ProductType = // ??? Cannot get the SelectedLookupId
...
repository.SaveProduct (product);
TempData["message"] = string.Format("{0} has been saved", product.Name);
return View("Completed");
}
else
{
//there is something wrong with the data values
return View(product);
}
}
ViewModel:
public class ProductViewModel
{
public IEnumerable<Product> Products { get; set; }
public IEnumerable<Lookup> Lookups { get; set; } //Lookup for Product Types
public int SelectedLookupId { get; set; }
public Product Product { get; set; }
}
Thanks in advance for your helps.
Your action method should be receiving the view model, not the Product itself, like so:
[HttpPost]
public ActionResult Add(ProductViewModel productViewModel)
Unless I'm confused. But I assume the view snippet you posted above is from the Add view and that view's model is of type ProductViewModel. In your action method you are returning the Add view when the model state is invalid however you are passing a Product to that view. Again I may be confused because this should give you a runtime error that the types don't match.
Thanks for reply. Actually by using ViewModel rather than View, I have managed to solve the problem. On the other hand, after some research, I have applied another effective method in order to populate Dropdownlist without needing ViewModel. Furthermore with this example, I could use multiple foreign keys on the same Lookup table as shown below. Here is an an Applicant entity having 3 foreign keys and Lookup entity related to these keys. What I wanted to achieve with this example is exactly to use a Lookup table for only several Dropdownlist parameters i.e. Gender, Yes/No, Status,... due to no needing to create a table for the several parameters (these parameters are distinguished LookupType property on Lookup table). Here is the full example (I have shorted unrelated properties for brevity) below:
Applicant Entity:
public class Applicant
{
[Key]
public int ApplicantID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
// for using "Multiple foreign keys within same table using Fluent API"
public int? HasDoneAnyProject { get; set; }
public int? IsInterestedAnyProgramme { get; set; }
public int? InterestedProgrammeId { get; set; }
public virtual Lookup PrimaryLookup { get; set; }
public virtual Lookup SecondaryLookup { get; set; }
public virtual Lookup TertiaryLookup { get; set; }
}
Lookup Entity:
public class Lookup
{
[Key]
public int LookupID { get; set; }
public string LookupType { get; set; }
public string LookupValue { get; set; }
// for using "Multiple foreign keys within same table using Fluent API"
public virtual ICollection<Applicant> PrimaryLookupFor { get; set; }
public virtual ICollection<Applicant> SecondaryLookupFor { get; set; }
public virtual ICollection<Applicant> TertiaryLookupFor { get; set; }
}
DbContext:
public class EFDbContext : DbContext
{
public DbSet<Applicant> Applicants { get; set; }
public DbSet<Lookup> Lookups { get; set; }
//for using "Multiple foreign keys within same table using Fluent API"
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Applicant>()
.HasOptional(b => b.PrimaryLookup)
.WithMany(a => a.PrimaryLookupFor)
.HasForeignKey(b => b.HasDoneAnyProject)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Applicant>()
.HasOptional(b => b.SecondaryLookup)
.WithMany(a => a.SecondaryLookupFor)
.HasForeignKey(b => b.IsInterestedAnyProgramme)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Applicant>()
.HasOptional(b => b.TertiaryLookup)
.WithMany(a => a.TertiaryLookupFor)
.HasForeignKey(b => b.InterestedProgrammeId)
.WillCascadeOnDelete(false);
}
}
Controller:
private void PopulateLookupsDropDownList(string lookupType, string foreignKey, object selectedLookups = null)
{
var lookupsQuery = repository.Lookups
.Select(x => x)
.Where(x => x.LookupType == lookupType)
.OrderBy(x => x.ParentLookupID).ToList();
ViewData[foreignKey] = new SelectList(lookupsQuery, "LookupID", "LookupValue", selectedLookups);
}
and for calling the Method for each of three Dropdownlist:
PopulateLookupsDropDownList("YesNo", "HasDoneAnyProject", applicant.HasDoneAnyProject);
PopulateLookupsDropDownList("YesNo", "IsInterestedAnyProgramme", applicant.IsInterestedAnyProgramme);
PopulateLookupsDropDownList("Programme", "InterestedProgrammeId", applicant.InterestedProgrammeId);
View: : Populating each of three Dropdownlist from the same Lookup table with different LookupType parameter:
<label>Has done any project before?</label>
#Html.DropDownList("HasDoneAnyProject", "---- Select ----")
<label>Are you interested in any programme?</label>
#Html.DropDownList("IsInterestedAnyProgramme", "---- Select ----")
<label>Interested programme name?</label>
#Html.DropDownList("InterestedProgrammeId", "---- Select ----")
I hope this approach will be useful for those who want to populate Dropdownlists from the same Lookup table. On the other hand, it is not only suitable for this, also can be used for populating Dropdownlists from different tables.
Regards.

Conditional Restrictions in Fluent NHibernate using Query Object Pattern

I'm a complete noob to Fluent NHibernate, and I'm using the Query Object Pattern based on a recommendation. Which I'm also new to. I'll try to keep the code samples concise and helpful.
User class:
public class User {
public Guid ID { get; set; }
public string Name { get; set; }
}
Visibility:
public enum VisibilityType {
Anybody,
OwnersOnly,
Nobody
}
Car class:
public class Car {
public Guid ID { get; set; }
public VisibilityType Visibility { get; set; }
public ICollection<User> Owners { get; set; }
}
So I need to write a conditional restriction method for the query object. Return all cars that have VisibilityType.Public, but if a car has Visibility property value of VisibilityType.OwnersOnly, restrict the return to users who belong to that group.
Here's the current restriction method that I have working, but without the condition:
public class CarQueryObject
{
private User user { get; set; }
private const string OwnersProperty = "Owners";
private const string OwnersIDProperty = "Owners.ID";
public CarQueryObject RestrictToOwners()
{
// How do I add a conditional criteria here? Only restrict by owner
// if the QueryObject has VisibilityType.OwnersOnly? Note that it should
// *NOT* restrict VisibilityType.Anybody
CreateOwnersAlias();
Criteria.Add(Restrictions.Eq(OwnersIDProperty, user.Id));
return this;
}
public CarQueryObject JoinFetchOwned()
{
Criteria.SetFetchMode(OwnersProperty, FetchMode.Join);
return this;
}
public void CreateOwnersAlias()
{
Criteria.CreateAlias(OwnersProperty, OwnersProperty, JoinType.LeftOuterJoin);
JoinFetchOwned();
}
}
?_?
an idea to get shown cars
var carsShown = session.CreateCriteria<Car>()
.JoinAlias("Owner", "owner")
.Add(Expressions.Or(
Expression.Eq("Visibility", Visibility.Anybody),
Expression.Eq("Visibility", Visibility.OwnersOnly) && Expression.Eq("owner.Id", currentUser.Id)
))
.List<Car>();