I am trying to get all object where the childs parentId equals the page id.
This is what I am trying to do:
public IEnumerable<Route> GetAll(int locationId)
{
_db.Configuration.ProxyCreationEnabled = false;
return _db.Routes.Include(o => o.ConnectionPointRoutes.Select(s => s.Segment))
.Include(o => o.ConnectionPointRoutes.Select(c => c.ConnectionPoint)
.Where( c => c.Location.LocationId == locationId)).ToList();
}
but I keep getting this error :
An exception of type 'System.ArgumentException' occurred in EntityFramework.dll but was not handled in user code
Additional information: The Include path expression must refer to a
navigation property defined on the type. Use dotted paths for
reference navigation properties and the Select operator for collection
navigation properties.
Any thoughts?
I ended up doing this :
public IEnumerable<Route> GetAll(int locationId)
{
return _db.Routes
.Include(o => o.ConnectionPointRoutes.Select(s => s.Segment))
.Include(o => o.ConnectionPointRoutes.Select(c => c.ConnectionPoint))
.Where(c => c.ConnectionPointRoutes.Select(s => s.ConnectionPoint.Location)
.FirstOrDefault(q => q.LocationId == locationId).LocationId == locationId)
.ToList();
}
Related
We are currently trying to get entity framework metadata to our viewmodel and we have queried the model builder and we're able to get the maximum length, however, we cannot get the "isRequired" IProperty.
// What our controller looks like:
var maxLengthOfStrings = _db.Model
.FindEntityType(typeof(Validation))
.GetProperties()
.Where(p => p.ClrType == typeof(string))
.ToDictionary(prop => prop.Name, prop => new {
MaxLegnth = prop.GetMaxLength(),
// The part that is saying required doesn't exist
// in the context
IsRequired = prop.IsRequired()
});
// What our db context file looks like:
modelBuilder.Entity<DeploymentEnvironment>(entity =>
{
entity.HasKey(e => e.Code);
entity.Property(e => e.Code)
.HasMaxLength(100)
.ValueGeneratedNever();
entity.Property(e => e.Name)
.IsRequired()
.HasMaxLength(200);
});
The error being received is "IProperty does not contain a definition for "IsRequired" and no accessible extension method "IsRequired" accepting a first argument of the type "IProperty" could be found.
I believe you need to Cast PropertyInfo to PropertyDescriptor and then, check the Attributes. Something like this:
IsRequired = p.Cast<PropertyDescriptor>().Where(p => p.Attributes.Cast<Attribute>().Any(a => a.GetType() == typeof(RequiredAttribute)))
I have a query like this
IEnumerable<int> companies = Employers
.Where(p => p.Id == User.ID
.SelectMany(p => p.companies)
.Select(p => p.Id);
return Employers
.Where(p => companies.Contains(p.Companies)
.Include(p => p.FirstName)
.ToList();
Here p.Companies refers to
Public Virtual List<Company>
under the class Employers.
This error occurs:
'IEnumerable' does not contain a definition for 'Contains' and the best extension method overload 'Queryable.Contains>(IQueryable>,List)' requires a receiver of type 'IQueryable>'
Doing this:
Where(p => companies.Contains(p.Companies)
you try to check if companies (IEnumerable<int> from previous query) contains some companies (object), not int value.
You can't check if collection of int values contains some value which is not int(Company is some object).
If you want to query all employers which have company with Id that is in companies variable use this Linq query:
return Employers
.Where(p => p.Companies.Any(c => companies.Contains(c.Id)))
.Include(p => p.FirstName)
.ToList();
This question already has answers here:
EF: Include with where clause [duplicate]
(5 answers)
Closed 2 years ago.
I have seen a few answers to similar questions, however I cannot seem to work out how to apply the answer to my issue.
var allposts = _context.Posts
.Include(p => p.Comments)
.Include(aa => aa.Attachments)
.Include(a => a.PostAuthor)
.Where(t => t.PostAuthor.Id == postAuthorId).ToList();
Attachments can be uploaded by the Author (type Author) or Contributor (type Contributor). What I want to do, is only get the Attachments where the owner of the attachment is of type Author.
I know this doesn't work and gives an error:
.Include(s=>aa.Attachments.Where(o=>o.Owner is Author))
I've read about Filtered Projection here
EDIT - link to article:
: http://blogs.msdn.com/b/alexj/archive/2009/10/13/tip-37-how-to-do-a-conditional-include.aspx,
but I just can't get my head around it.
I don't want to include the filter in the final where clause as I want ALL posts, but I only want to retrieve the attachments for those posts that belong to the Author.
EDIT 2: - Post schema requested
public abstract class Post : IPostable
{
[Key]
public int Id { get; set; }
[Required]
public DateTime PublishDate { get; set; }
[Required]
public String Title { get; set; }
[Required]
public String Description { get; set; }
public Person PostAuthor { get; set; }
public virtual ICollection<Attachment> Attachments { get; set; }
public List<Comment> Comments { get; set; }
}
EF Core 5.0 is introducing Filtered Include soon.
var blogs = context.Blogs
.Include(e => e.Posts.Where(p => p.Title.Contains("Cheese")))
.ToList();
Reference: https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-5.0/whatsnew#filtered-include
From the link you posted I can confirm that trick works but for one-many (or many-one) relationship only. In this case your Post-Attachment should be one-many relationship, so it's totally applicable. Here is the query you should have:
//this should be disabled temporarily
_context.Configuration.LazyLoadingEnabled = false;
var allposts = _context.Posts.Where(t => t.PostAuthor.Id == postAuthorId)
.Select(e => new {
e,//for later projection
e.Comments,//cache Comments
//cache filtered Attachments
Attachments = e.Attachments.Where(a => a.Owner is Author),
e.PostAuthor//cache PostAuthor
})
.AsEnumerable()
.Select(e => e.e).ToList();
Remove the virtual keyword from your Attachments navigation property to prevent lazy loading:
public ICollection<Attachment> Attachments { get; set; }
First method: Issue two separate queries: one for the Posts, one for the Attachments, and let relationship fix-up do the rest:
List<Post> postsWithAuthoredAttachments = _context.Posts
.Include(p => p.Comments)
.Include(p => p.PostAuthor)
.Where(p => p.PostAuthor.Id == postAuthorId)
.ToList();
List<Attachment> filteredAttachments = _context.Attachments
.Where(a => a.Post.PostAuthor.Id == postAuthorId)
.Where(a => a.Owner is Author)
.ToList()
Relationship fixup means that you can access these filtered Attachments via a Post's navigation property
Second method: one query to the database followed by an in-memory query:
var query = _context.Posts
.Include(p => p.Comments)
.Include(p => p.PostAuthor)
.Where(p => p.PostAuthor.Id == postAuthorId)
.Select(p => new
{
Post = p,
AuthoredAttachments = p.Attachments
Where(a => a.Owner is Author)
}
);
I would just use the anonymous type here
var postsWithAuthoredAttachments = query.ToList()
or I would create a ViewModel class to avoid the anonymous type:
List<MyDisplayTemplate> postsWithAuthoredAttachments =
//query as above but use new PostWithAuthoredAttachments in the Select
Or, if you really want to unwrap the Posts:
List<Post> postsWithAuthoredAttachments = query.//you could "inline" this variable
.AsEnumerable() //force the database query to run as is - pulling data into memory
.Select(p => p) //unwrap the Posts from the in-memory results
.ToList()
You can use this implementation of an extension method (eg.) Include2(). After that, you can call:
_context.Posts.Include2(post => post.Attachments.Where(a => a.OwnerId == 1))
The code above includes only attachments where Attachment.OwnerId == 1.
try this
var allposts = _context.Posts
.Include(p => p.Comments)
.Include(a => a.PostAuthor)
.Where(t => t.PostAuthor.Id == postAuthorId).ToList();
_context.Attachments.Where(o=>o.Owner is Author).ToList();
For net core
https://learn.microsoft.com/ru-ru/ef/core/querying/related-data/explicit
var allposts = _context.Posts
.Include(p => p.Comments)
.Include(a => a.PostAuthor)
.Where(t => t.PostAuthor.Id == postAuthorId).ToList();
_context.Entry(allposts)
.Collection(e => e.Attachments)
.Query()
.Where(e=> e.Owner is Author)
.Load();
it makes 2 query to sql.
Lambda in Include() may only point to a property:
.Include(a => a.Attachments)
.Include(a => a.Attachments.Owner);
Your condition doesn't makes sense for me because Include() means join and you either do it or not. And not conditionally.
How would you write this in raw SQL?
Why not just this:
context.Attachments
.Where(a => a.Owner.Id == postAuthorId &&
a.Owner.Type == authorType);
?
Assuming "a" being of type "YourType", a conditonal include could be solved by using a method extension, e.g.
public static class QueryableExtensions
{
public static IQueryable<T> ConditionalInclude<T>(this IQueryable<T> source, bool include) where T : YourType
{
if (include)
{
return source
.Include(a => a.Attachments)
.Include(a => a.Attachments.Owner));
}
return source;
}
}
... then just use this like you are using .Include, e.g.
bool yourCondition;
.ConditionalInclude(yourCondition)
I have an class called WorkflowTask that contains a TaskProperties class property. There is a number of subclasses for TaskProperties mapped using JoinedSubclassMapping (such as DeliveryTaskProperties).
I need to execute a NHibernate Query such as the following to get a List of the workflow tasks that I need to handle:
NHibernateSession.Query<WorkflowTask>().Where(x => (x.WorkflowTaskStatus == WorkflowTask.WorkflowTaskStatuses.New && x.TaskProperties.UserAssignedTo=="System")).ToList<WorkflowTask>();
When iterating through this list I notice that the correct subclass of TaskProperties is identified. However when I try to cast the wt.TaskProperties to DeliveryTaskProperties to access its data I get an invalid cast exception.
The only way I can get this to work is to try to load the task properties entity again by specifying the exact subclass:
The following works and I can cast to the DeliveryTaskProperties class:
wt.TaskProperties = NHibernateSession.Load<DeliveryTaskProperties>(wt.TaskProperties.Id);
Immediate Window:
{Workflow.Entities.DeliveryTaskProperties}
[DeliveryTaskPropertiesProxy]: {Workflow.Entities.DeliveryTaskProperties}
base: {Workflow.Entities.DeliveryTaskProperties}
UserAssignedTo: "system"
This doesn't Work and I get an invalid cast exception:
wt.TaskProperties = NHibernateSession.Load<TaskProperties>(wt.TaskProperties.Id);
Immediate Window:
{Workflow.Entities.DeliveryTaskProperties}
[TaskPropertiesProxy]: {Workflow.Entities.DeliveryTaskProperties}
base: {Workflow.Entities.DeliveryTaskProperties}
UserAssignedTo: "system"
Classes Mapping:
public class WorkflowTaskMapping : BaseWorkflowEntityMapping<WorkflowTask>
{
public WorkflowTaskMapping()
{
this.Property(x => x.TaskPropertiesId, map =>
{
map.Column("TaskPropertiesId");
map.Insert(false);
map.Update(false);
map.NotNullable(true);
});
this.ManyToOne<TaskProperties>(x => x.TaskProperties, map =>
{
map.Column("TaskPropertiesId");
map.Cascade(Cascade.All);
map.NotNullable(true);
map.ForeignKey("WFTaskProperties_WFTask_FK");
});
this.Property(x => x.WorkflowTaskStatus, map => map.NotNullable(true));
}
}
public class TaskPropertiesMapping : BaseWorkflowEntityMapping<TaskProperties>
{
public TaskPropertiesMapping()
{
this.Property(x => x.UserAssignedTo, map => map.NotNullable(true));
}
}
public class DeliveryTaskPropertiesMapping : JoinedSubclassMapping<DeliveryTaskProperties>
{
public DeliveryTaskPropertiesMapping()
{
this.Key(x => { x.Column("Id"); x.ForeignKey("DelivTask_TaskProperties_FK"); });
this.Property(x => x.DeliveryAddress, map => map.NotNullable(true));
this.Property(x => x.Deadline, map => map.NotNullable(true));
this.Property(x => x.DeliveryOnDeadline, map => map.NotNullable(true));
}
}
Is there a way to load the subclass data correctly when loading the WorkflowTask entity and without having to specify the exact subclass type?
OK, I figured it out. Although it wasn't very clear on other posts I read dealing with similar issues it seems that Eager Fetching did the trick for me.
If I change my query and add fetching like this:
NHibernateSession.Query<WorkflowTask>().Where(x => (x.WorkflowTaskStatus == WorkflowTask.WorkflowTaskStatuses.New && x.TaskProperties.UserAssignedTo == "System")).Fetch(x => x.TaskProperties).ToList<WorkflowTask>();
Then nhibernate actually loads the subclass in memory although the type remains that of the base class. This allows for later casting to the subclass to access its properties.
Im not sure if is this what you need:
TaskProperties tpAlias = null;
var workflowTasks = NHibernateSession.QueryOver<WorkflowTask>()
.Left.JoinAlias(x => x.TaskProperties, () => tpAlias)
.Where(x => (x.WorkflowTaskStatus == WorkflowTask.WorkflowTaskStatuses.New
&& x.TaskProperties.UserAssignedTo == "System"))
.List();
If I have the following how to do I cast the result of the lambda expression back to the Customer type from IEnumerable<Customer> without having to iterate over it.
public class Customer : CustomerModel
{
public List<Customer> CustomerList {get;set;}
public Customer GetCustomerFromListById(long id)
{
return CustomerList.Select(c => c).Where(i => i.Id == id);
}
}
Use .Single(), also Select is redundant:
return CustomerList.Single(i => i.Id == id);
As mentioned in the comments that way you get an exception if it doesn't contain the needed key, which should be the expected behavior.
Use FirstOrDefault().
return CustomerList.Select(c => c).Where(i => i.Id == id).FirstOrDefault();