I am using Entity Framework Core and I have the following structure:
class Foo
{
public ICollection<Bar> Bars { get; set; }
}
class Bar
{
public XPTO Xpto { get; set; }
}
class XPTO
{
public string Message { get; set; }
}
How can I include in my Linq query the XPTO Object?
I have tried:
context.Foo.Include(o => o.Bars).ToList(); // This gets me the Bars but the XPTO's of them are null
context.Foo.Include(o => o.Bars).ThenInclude(o => o.Select(x => x.Xpto)).ToList(); This throws an error:
System.ArgumentException: The property expression 'o => {from Bar in o
select [x].Xpto}' is not valid. The expression should represent a
property access: 't => t.MyProperty'.
What am I missing here?
Just for someone else facing the same problem using this
context.Foo.Include(o => o.Bars).ThenInclude(x => x.Xpto).ToList();
you may not get any Intellisense help for that but it would still work with ef core.
Related
Apologies if this has been asked before as I am learning EF 4.1 and LINQ, I needed an expert opinion.
I have a viewmodel called HomeIndexData. My code looks something like the following:
string _categoryIDs = "100,101,102,103,104";
List < int > CategoryIDs = _categoryIDs.Split(',').Select(t => int.Parse(t));
Then I got the following view model:
public class HomeIndexData
{
public IEnumerable<Genre> Genres { get; set; }
public IEnumerable<Category> Categories { get; set; }
public IEnumerable<SubCategory> SubCategories { get; set; }
public IEnumerable<Country> Countries { get; set; }
}
At the moment, in my Controller I have the following code -
HomeIndexData viewModel = new HomeIndexData
{
Genres = db.Genres
.Include(i => i.Categories.Where(i => CategoryIDs.Contains(i.CategoryId)))
.Include("Categories.SubCategories")
.OrderBy(g => g.DisplaySequence),
Countries = db.countries
};
But I am getting error :
Cannot convert lambda expression to type 'string' because it is not a delegate type
for the following line :
.Include(i => i.Categories.Where(i => CategoryIDs.Contains(i.CategoryId)))
Could you please put me to the right direction how can I write the lamda expression.
Any help on this will be much appreciated.
Thanks in advance.
EF doesn't support complex expressions in the Include extension at this point, only simple property getters. If you feel this is an important enhancement scenario for EF, consider voting for it at http://data.uservoice.com/forums/72025-entity-framework-feature-suggestions/suggestions/1015345-allow-filtering-for-include-extension-method.
For now, your best option is to project your filter in the Select clause, but that gets tricky with a generic repository.
I'm using the term "child documents" loosely relating to objects stored in a collection property. Given those two classes:
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
public IList<Bar> Bars { get; set; }
}
public class Bar
{
public string Name { get; set; }
public string Description { get; set; }
public string SomeString { get; set; }
public int SomeInt { get; set; }
}
I would like to query for Bars which have the term "roses" in either their Name or Description properties. Please note that Bars are stored within Foo.
This is a two fold question:
Can a query be used over a collection of type Foo to return Bars ("child document")? I want to get a collection of Bars which have "roses" in their Name, other Bars should be skipped. I don't want Foos (aggregate root) returned.
If so, how to do it with full text search?
Regarding #1, I know something like that is impossible in MongoDB, where I would either have to store Foo and Bar separately or query for Foos where any of their Bars has "roses" in the Name and then do something about it on the client. But RavenDB has Live Projections / TransformResults, so I thought it would be possible perhaps.
You can store a copy of each Bar in field storage with the index, so yes - it can be done. But you should make sure you understand the impact of doing this.
Normally when you query from raven, the index is only used to determine which documents get sent back. The documents themselves don't come from the index, but from the document store. The document store has ACID guarantees - meaning you will always get the most current copy of the document regardless of the state of the index. If you project from index entries or index field storage, then the values you get back are as stale as the index itself.
Say you are constantly updating the Bars, and you search before the index has caught up to your last update. You could get back a Bar that has old data. Therefore, you need to weigh in the potential staleness of data into your results, possibly using one of the WaitForNonStaleResultsAsOf... customizations - which will slow the speed your search results return if you have lots of writes going on.
public class Foos_BarsByName
: AbstractIndexCreationTask<Foo, Foos_BarsByName.Result>
{
public class Result
{
public string Name { get; set; }
public Bar Bar { get; set; }
}
public Foos_BarsByName()
{
Map = foos => from foo in foos
from bar in foo.Bars
select new
{
bar.Name,
Bar = bar
};
Index(x => x.Name, FieldIndexing.Analyzed);
Index(x => x.Bar, FieldIndexing.No);
Store(x => x.Bar, FieldStorage.Yes);
}
}
var results = session.Query<Foos_BarsByName.Result, Foos_BarsByName>()
.Customize(x => x.WaitForNonStaleResultsAsOfNow())
.Search(x => x.Name, "roses")
.Select(x => x.Bar);
Another way to handle it might be to let all Foos come back, and then pull out the Bars you are interested in on the client side. At least then, everything comes from the document store:
public class Foos_BarsByName
: AbstractIndexCreationTask<Foo, Foos_BarsByName.Result>
{
public class Result
{
public string Name { get; set; }
}
public Foos_BarsByName()
{
Map = foos => from foo in foos
from bar in foo.Bars
select new
{
bar.Name
};
Index(x => x.Name, FieldIndexing.Analyzed);
}
}
var results = session.Query<Foos_BarsByName.Result, Foos_BarsByName>()
.Search(x => x.Name, "roses")
.As<Foo>()
.AsEnumerable()
.SelectMany(x => x.Bars)
.Where(x => x.Name.IndexOf("roses",
StringComparison.CurrentCultureIgnoreCase)
!= -1)
.AsEnumerable() will force the linq-to-raven query to execute, making everything that follows happen in linq-to-objects on the client side.
Of course, if you are doing a more advanced search than can be expressed with c# string functions, then you won't be able to take this second approach.
I've been fiddling about and trying multiple things, but I'm going wrong somewhere. I tried to make my first attempt using AutoMapper as simple as possible. I'm trying to create a new Brand and save it to the database, using a CreateBrandViewModel. Some of this might look a bit fruity, but I was trying to get it to work in the simplest way possible.
Domain:
public class Brand : EntityBase
{
public virtual string Name { get; set; } //Not Nullable
public virtual bool IsActive { get; set; } // Not Nullable
public virtual Product DefaultProduct { get; set; } // Nullable
public virtual IList<Product> Products { get; set; } // Nullable
}
ViewModel:
public class CreateBrandViewModel
{
public string Name { get; set; }
public bool IsActive { get; set; }
}
Controller
this is where I've been playing about the most for a while, so it looks a bit strange now. The commented out code hasn't resolved my problem.
[HttpPost]
public ActionResult Create(CreateBrandViewModel createBrandViewModel)
{
if(ModelState.IsValid)
{
Mapper.CreateMap<Brand, CreateBrandViewModel>();
//.ForMember(
// dest => dest.Name,
// opt => opt.MapFrom(src => src.Name)
//)
//.ForMember(
// dest => dest.IsActive,
// opt => opt.MapFrom(src => src.IsActive)
//);
Mapper.Map<Brand, CreateBrandViewModel>(createBrandViewModel)
Session.SaveOrUpdate(createBrandViewModel);
return RedirectToAction("Index");
}
else
{
return View(createBrandViewModel);
}
}
Just for the record, BrandController inherits from SessionController (Ayendes way), and transactions are managed through an ActionFilter. Though thats is a bit irrelevant I think. I've tried various different ways so I have different error messages - if you can take a look at whats happening and tell me how you might expect to use it that would be great.
For reference, my fluent nhibernate mapping for Brand:
public class BrandMap : ClassMap<Brand>
{
public BrandMap()
{
Id(x => x.Id);
Map(x => x.Name)
.Not.Nullable()
.Length(50);
Map(x => x.IsActive)
.Not.Nullable();
References(x => x.DefaultProduct);
HasMany(x => x.Products);
}
}
Edit 1
I just tried the following code, but putting a breakpoint on Session.SaveOrUpdate(updatedModel) the fields are null and false, when they shouldn't be:
var brand = new Brand();
var updatedBrand = Mapper.Map<Brand, CreateBrandViewModel>(brand, createBrandViewModel);
Session.SaveOrUpdate(updatedBrand);
return RedirectToAction("Index");
}
You appear to be doing your mapping the wrong way around on your return trip from the post. The alternative syntax may help out here, try:
// setup the viewmodel -> domain model map
// (this should ideally be done at initialisation time, rather than per request)
Mapper.CreateMap<CreateBrandViewModel, Brand>();
// create our new domain object
var domainModel = new Brand();
// map the domain type to the viewmodel
Mapper.Map(createBrandViewModel, domainModel);
// now saving the correct type to the db
Session.SaveOrUpdate(domainModel);
let me know if that cracks the egg... or just egg on yer face again :-)
I am using NHibernate 3.x, along with Fluent NHibernate, and have not had any issues constructing database queries until now.
To simplify my objects for the purposes of this post, I've included a subset of my object and mapping structures below:
IssueItem POCO entity class:
public class IssueItem : DomainEntity, IKeyed<Guid> {
public virtual Guid ID { get; set; }
public virtual string Subject { get; set; }
public virtual string Description { get; set; }
public virtual IList<IssueLocation> Locations { get; set; }
}
Location POCO entity class:
public class Location : DomainEntity, IKeyed<Guid> {
public virtual Guid ID { get; set; }
public virtual string City { get; set; }
public virtual string State { get; set; }
public virtual string Zip { get; set; }
public virtual string Organization { get; set; }
public virtual IssueItem Issue { get; set; }
}
IssueItem Fluent NHibernate map:
public class IssueItemMap : DomainEntityMapping<IssueItem> {
public IssueItemMap()
{
Table("IssueItem");
LazyLoad();
Map(x => x.ID).Column("ID");
Map(x => x.Subject).Column("Subject");
Map(x => x.Description).Column("Description");
HasMany(x => x.Locations).KeyColumn("IssueItemID").LazyLoad().ReadOnly().Inverse();
}
}
Location Fluent NHibernate map:
public class LocationMap : DomainEntityMapping<Location> {
public LocationMap()
{
Table("Location");
LazyLoad();
Map(x => x.ID).Column("ID");
Map(x => x.City).Column("City");
Map(x => x.State).Column("State");
Map(x => x.Zip).Column("Zip");
Map(x => x.Organization).Column("Organization");
References(x => x.IssueItem).ForeignKey("IssueItemID").LazyLoad().ReadOnly();
}
}
Now, I'm using a Unit of Work and Service/Repository pattern in my MVC app. Therefore, I have a domain layer of my project that contains my basic POCO entities, as well as validators and services. In my data layer, I've got my NHibernate-related stuff, such as my repositories that my domain layer access from my services. This is where my NHibernate maps live as well.
In order to ensure that no NHibernate-specific logic creeps into my domain layer (in case I want to use a different ORM in the future), I perform my LINQ statements in my services within my domain layer against IQueryable objects returned from the repositories in my data layer. Therefore, when I write my queries, I am using System.Linq and System.Linq.Expressions instead of the NHibernate.Linq class.
That said, here's my LINQ query I'm having issues with from within one of my service classes in my domain layer:
var issues = _issueRepo.All();
if (!string.IsNullOrWhiteSpace(searchWords)) {
issues = issues.Where(i => i.Subject.Contains(searchWords)
|| i.Description.Contains(searchWords)
|| i.Locations.Where(l => l.Organization.Contains(searchWords)
|| l.City.Contains(searchWords))
.Select(x => x.IssueItemID).Contains(i.ID)
);
}
Now, the IssueItems are queried just fine. However, the one-to-many table (Locations) is not properly queried. This is what I mean...
The generated T-SQL statement is perfect except for the very end of it. Example:
select TOP(100) issueitem0_.ID as ID2_, issueitem0_.Subject as Subject2_, issueitem0_.Description as Description2_
from IssueItem issueitem0_
where issueitem0_.Subject like ('%test%') or issueitem0_.Description like ('%test%')
or exists (select location1_.IssueItemID from Location location1_ where
issueitem0_.ID=location1_.IssueItemID and (location1_.Organization like ('%test%')
or location1_.City like ('%test%')) and location1_.ID=issueitem0_.ID)
See that last bit? It throws in that last "and" statement (and location1_.ID=issueitem0_.ID) that throws a wrench in the whole system. I have tweaked every configuration parameter I could think of with my mapping and have tried many different LINQ statements and I cannot get rid of that last part. I don't know why it adds it.
If I construct the same LINQ statement in LINQPad, it properly generates the T-SQL statement without the last part (and location1_.ID=issueitem0_.ID).
Any ideas?
Thanks!
Joel
Add Any() when you query locations. It will come true if any location property contains what you are looking for. You are trying to select in where clause, then trying to get IssueID from there. I think you will see this query is clearer.
var issues = _issueRepo.All();
if (!string.IsNullOrWhiteSpace(searchWords))
{
issues = issues.Where(i => i.Subject.Contains(searchWords)
|| i.Description.Contains(searchWords)
|| i.Locations.Any(l => l.Organization.Contains(searchWords))
|| i.Locations.Any(l => l.City.Contains(searchWords)) )
}
I have an TaxWork entity which is persisted using NHibernate. This entity has the following properties (among others):
public virtual TaxWorkType Type { get; set; } //Kctc.TaxWorkType is an enumeration
public virtual TaxWorkStatus Status { get; set; } //Kctc.TaxWorkStatus is an enumeration
public virtual LegalWorkPriority Priority { get; set; } //Kctc.LegalWorkType is an enumeration
public virtual User Handler { get; set; } //Kctc.BusinessLayer.Entities.User is another entity
public virtual int? CaseNumber { get; set; }
I am using Linq to NHibernate to pull of a subset of the tax work objects as follows (taxWorkRepository.All obviously returns an IQueryable):
foreach (TaxWork taxWork in taxWorkRepository.All.Where(x => x.CaseNumber == _caseNumber).OrderBy(x => x.DateCreated))
{
...
}
This works fine. I want to use projection in order to query only the columns that are required in this case. I am usnig the following code:
foreach (var taxWorkFragment in taxWorkRepository.All.Where(x => x.CaseNumber == _caseNumber).OrderBy(x => x.DateCreated).Select(x => new { Type = x.Type, DateCreated = x.DateCreated, Handler = x.Handler, Status = x.Status, Priority = x.Priority }))
{
...
}
However, I'm getting the following error when trying to create the anonymous type:
Invalid cast from 'Kctc.TaxWorkStatus' to 'Kctc.BusinessLayer.Entities.User'.
Where on earth is it getting the idea that it should be casting a TaxWorkStatus to a User?
Any suggestions whatsoever what might be going wrong?
Try to make like this:
foreach (var taxWorkFragment in taxWorkRepository.All.Where(x => x.CaseNumber == _caseNumber).OrderBy(x => x.DateCreated)
.Select(x => new TaxWork { Type = x.Type, DateCreated = x.DateCreated, Handler = x.Handler, Status = x.Status, Priority = x.Priority }))
{
...
}
It should help