I building my first application with c# and sp.net MVC 5, so far so good :)
Now I have a problem, we using 2 User Tables, first one contains the username, other the user data.
string user = User.Identity.Name;
var data = db.FE_Benutzer;
var collection = data.Where(o => o.Benutzername == user).Select(x => new
{
id = x.ID,
name = x.Name,
hauptbereiche = x.Hauptbereich.ToList()
});
var dataHauptbereich = db.Hauptbereich;
var collectionHauptbereich = dataHauptbereich.Where(o => collection.ElementAt(0).hauptbereiche.Contains(o)).Select(x => new
{
id = x.ID,
name = x.Name
});
return Json(collectionHauptbereich, JsonRequestBehavior.AllowGet);
I getting this error
LINQ to Entities does not recognize the method '<>f__AnonymousType63[System.Int32,System.String,System.Collections.Generic.List1[scorring.Models.Hauptbereich]] ElementAt[<>f__AnonymousType63](System.Linq.IQueryable1[<>f__AnonymousType63[System.Int32,System.String,System.Collections.Generic.List1[scorring.Models.Hauptbereich]]], Int32)' method, and this method cannot be translated into a store expression.
hauptbereiche = x.Hauptbereich.ToList()
contains a list of ids where the user have premission to.
When I fetching the data
dataHauptbereich.Where
I wont to include only the ids I have in the list
how is this possible?
Entity Framework doesn't know how to turn ElementAt into SQL. See this answer for more information: Getting the first result from a LINQ query - why does ElementAt<T>(0) fails when First<T>() succeeds?
Try
dataHauptbereich.Where(o => collection.ElementAt(0).hauptbereiche.Any(h => h.ID == o.ID))
Or
dataHauptbereich.Where(o => collection.Any(c => c.hauptbereiche.Any(h => h.ID == o.ID)))
I'm having a bit of a time deciphering exactly what you're trying to achieve with your code here, but it looks to me like your simply querying Hauptbereichs that belong to a particular user. Your first query selects an anonymous object composed of id, name and hauptbereiche, but of these you only ever use the hauptbereiche property. Then, in your second query, you merely selecting Hauptbereichs that match an item in this hauptbereiche property's collection. Actually, here, you're only comparing values from the first item in the original collection, which begs the question of why you're selecting anything other than the first item. That, and this second query is entirely redundant because if the items match that means you already had the items in the first place. You could get the same info directly from collection.ElementAt(0).hauptbereiche without issuing the second query.
So, here's a couple of simpler options:
If you're trying to get all the Hauptbereichs that belong to all the FE_Benutzers where Benutzername == user then just do:
var collectionHauptbereich = db.FE_Benutzer.Where(m => m.Benutzername == user)
.Include(m => m.Hauptbereich)
.SelectMany(m => m.Hauptbereich);
If you want just the first FE_Benutzer item's Hauptbereichs, then do:
var benutzer = db.FE_Benutzer.Where(m => m.Benutzername == user)
.Include(m => m.Hauptbereich)
.FirstOrDefault();
var collectionHauptbereich = benutzer != null
? benutzer.Hauptbereich.ToList()
: new List<Hauptbereich>();
Related
This question is specific to taking in a formatted url value and comparing to an unformatted database value.
I have a url /companies/my-company/
The company's name is stored in the database field as "My Company" - note: no hyphen, space and uppercase.
It could be stored in an even more complex format like "John's Company >>" because the user has the ability to input this in the CMS.
So how do I compare my-company to My Company in LINQ to Entites?
var type = Context.Set<Domain.Content.Organisation>()
.AsNoTracking()
.Select(x => new DataContracts.Content.Organisation
{
Id = x.Id
,Name = x.Name
,OrganisationTypeId = x.OrganisationTypeId
,OrganisationTypeName = (x.OrganisationType != null) ? x.OrganisationType.Name : string.Empty
,Categories = x.Categories.Select(i => i.Id)
,IsCustomer = x.IsCustomer
,CountryId = x.CountryId
,CountryName = (x.Country != null) ? x.Country.Name : string.Empty,
SeoName = x.SeoName,
Description = x.Description,
Website = x.Website
})
.FirstOrDefault(x => x.Id == id || Urls.MeaningfulURL(x.SeoName.ToLower(), null) == seoName.ToLower());
The problem here is:
Urls.MeaningfulURL(x.SeoName.ToLower(), null) == seoName.ToLower()
It generates an error:
LINQ to Entities does not recognize the method Urls.MeaningfulURL method, and this method cannot be translated into a store expression.
I understand what it means, but cannot figure out a way around it.
Urls.MeaningfulURL is a method that does a lot of string replacements to create the url format 'my-company'.
How can I format column x.SeoName at query time correctly so it matches my url input ?
Thanks
EF cann't translate Urls.MeaningfulURL to SQL. There are so many options to resolve it:
1. Load data first:
var type = Context.Set<Domain.Content.Organisation>()
.AsNoTracking()
.Select(your select)
.ToList()
.FirstOrDefault(x => x.Id == id || Urls.MeaningfulURL(x.SeoName.ToLower(), null) == seoName.ToLower());
it's simplest but it produces perfomance problems cause all Organisation will be loaded from db into memory.
2. Rewrite your logic.
You cann't apply custom method to x.SeoName so just don't do that. Try to rewrite your query to:
var type = Context.Set<Domain.Content.Organisation>()
.AsNoTracking()
.Select(your select)
.FirstOrDefault(x => x.Id == id || computedString.Contains(x.SeoName));
And yes, you can use Contains, at least for SQL Server provider.
3. Move your logic to SQL:
If SQL Server is used, you can try computed column, view, and even CLR code on SQL Server etc.
4. Store the computed value in db:
Sometimes it is better to store Urls.MeaningfulURL(x.SeoName.ToLower(), null) value instead or together with x.SeoName and use this stored value in queries. Also it becomes possible to add an index on this field.
//This works, but seems incorrect to me
Object selection = db.ExampleTable
.Where(s => s.Id == id)
.Select(s => new { s.Id, s.PropIWantToShow })
.SingleOrDefault();
//This seems correct, but does not work
Object selection = db.ExampleTable
.SingleOrDefault(s => s.Id == id)
.Select(s => new { s.Id, s.PropIWantToShow });
db is our Entity Framework data context.
My goal is to select a single entry matching the provided id in ExampleTable. If an entry is not found, this is to return null. However, EF doesn't seem to let me select a single object and then only return specific properties. How do I accomplish this or is the first example I provided correct?
I did check this question:
select properties of entity ef linq:
Unfortunately you cannot conditionally load properties of related entity - you either load whole door entity, or don't include that entity.
But the answer just doesn't seem right, but obviously "seems" is a very weak statement.
Your first method is correct:
//This works, but seems incorrect to me
Object selection = db.ExampleTable
.Where(s => s.Id == id)
.Select(s => new { s.Id, s.PropIWantToShow })
.SingleOrDefault();
Your second method gets you a single object, not an IQueryable<T> object that LINQ would work with. If you want to convert from one type of object to another, that isn't a LINQ thing. You can still, but it'll be more convoluted. Something like:
var selection =...;
var newselection=new { Id=selection.Id, PropIWantToShow=selection.PropIWantToShow };
but this is very bad because you DID retrieve the entire object from the DB, and then just threw away most of it. Your first method only returns 2 fields from the DB.
If you want your function to return null if condition doesn't match then use FirstorDefault() instead of SingleorDefalut(). So if you want to match an id and return an object then do it like this :
return db.ExampleTable.FirstorDefault(c=>c.Id == id);
I am trying to fetch an option using the SingleOrDefault Linq to SQL method.
var po = repository.Context.AsQueryable<Option>().SingleOrDefault(o => o.Option.Id == sp.Options // sp.Options is a collection);
The problem is that inside the SingleOrDefault method I am comparing p.Option.Id == a collection. What I want is to select the option from sp.Options that matches the o.Option.Id. How can I do that?
UPDATE:
One thing I should have mentioned that the sp.Options is a different class than the Option class. sp.Options is SPOptions class so I cannot pass it inside the contains method.
Take a look at Contains.
repository.Context.AsQueryable<Option>().SingleOrDefault(o => sp.Options.Contains(o.Option.Id));
If Options is not a collection of the class of Option.Id, you can use the Any method with your comparison logic in it as follow :
repository.Context.AsQueryable<Option>().SingleOrDefault(o => sp.Options.Any(opts => opts.Something == o.Option.Id));
Search using Contains (sp.Options.Contains(o.Option.Id)) like:
var po = repository.Context.AsQueryable<Option>()
.SingleOrDefault(o => sp.Options.Contains(o.Option.Id));
If members of sp.Options are different from Id then you can do:
var po = repository.Context.AsQueryable<Option>()
.SingleOrDefault(o => sp.Options.Any(r=> r.Id == o.Option.Id));
or
var po = repository.Context.AsQueryable<Option>()
.SingleOrDefault(o => sp.Options.Select(r=> r.Id).Contains(o.Option.Id));
Assuming Id is the field in sp.Options elements that you want to compare with.
Based on your question it seems you're expecting to have a single match between those two option sets, correct ?
If so, I'd suggest you to write it as:
var po = repository.Context.AsQueryable().Where(o => sp.Options.Any(item=>item.id == o.Option.Id)).SingleOrDefault();
I have got a bit of an issue and was wondering if there is a way to have my cake and eat it.
Currently I have a Repository and Query style pattern for how I am using Linq2Sql, however I have got one issue and I cannot see a nice way to solve it. Here is an example of the problem:
var someDataMapper = new SomeDataMapper();
var someDataQuery = new GetSomeDataQuery();
var results = SomeRepository.HybridQuery(someDataQuery)
.Where(x => x.SomeColumn == 1 || x.SomeColumn == 2)
.OrderByDescending(x => x.SomeOtherColumn)
.Select(x => someDataMapper.Map(x));
return results.Where(x => x.SomeMappedColumn == "SomeType");
The main bits to pay attention to here are Mapper, Query, Repository and then the final where clause. I am doing this as part of a larger refactor, and we found that there were ALOT of similar queries which were getting slightly different result sets back but then mapping them the same way to a domain specific model. So take for example getting back a tbl_car and then mapping it to a Car object. So a mapper basically takes one type and spits out another, so exactly the same as what would normally happen in the select:
// Non mapped version
select(x => new Car
{
Id = x.Id,
Name = x.Name,
Owner = x.FirstName + x.Surname
});
// Mapped version
select(x => carMapper.Map(x));
So the car mapper is more re-usable on all areas which do similar queries returning same end results but doing different bits along the way. However I keep getting the error saying that Map is not able to be converted to SQL, which is fine as I dont want it to be, however I understand that as it is in an expression tree it would try to convert it.
{"Method 'SomeData Map(SomeTable)' has no supported translation to SQL."}
Finally the object that is returned and mapped is passed further up the stack for other objects to use, which make use of Linq to SQL's composition abilities to add additional criteria to the query then finally ToList() or itterate on the data returned, however they filter based on the mapped model, not the original table model, which I believe is perfectly fine as answered in a previous question:
Linq2Sql point of retrieving data
So to sum it up, can I use my mapping pattern as shown without it trying to convert that single part to SQL?
Yes, you can. Put AsEnumerable() before the last Select:
var results = SomeRepository.HybridQuery(someDataQuery)
.Where(x => x.SomeColumn == 1 || x.SomeColumn == 2)
.OrderByDescending(x => x.SomeOtherColumn)
.AsEnumerable()
.Select(x => someDataMapper.Map(x));
Please note, however, that the second Where - the one that operates on SomeMappedColumn - will now be executed in memory and not by the database. If this last where clause significantly reduces the result set this could be a problem.
An alternate approach would be to create a method that returns the expression tree of that mapping. Something like the following should work, as long as everything happening in the mapping is convertible to SQL.
Expression<Func<EntityType, Car>> GetCarMappingExpression()
{
return new Expression<Func<EntityType, Car>>(x => new Car
{
Id = x.Id,
Name = x.Name,
Owner = x.FirstName + x.Surname
});
}
Usage would be like this:
var results = SomeRepository.HybridQuery(someDataQuery)
.Where(x => x.SomeColumn == 1 || x.SomeColumn == 2)
.OrderByDescending(x => x.SomeOtherColumn)
.Select(GetCarMappingExpression());
Consider the following Query :
var profilelst =
(
from i in dbContext.ProspectProfiles
where i.CreateId == currentUser
select new ProspectProfile
{
ProspectId = i.ProspectId,
Live = i.Live,
Name = i.Name,
ServiceETA = i.Opportunities.OrderByDescending(t => t.FollowUpDate)
.FirstOrDefault()
.ServiceETA.ToString(),
FollowUpDate = i.Opportunities.OrderByDescending(t => t.FollowUpDate)
.FirstOrDefault()
.FollowUpDate
}
)
.ToList();
return profilelst.OrderByDescending(c=>c.FollowUpDate)
.Skip(0).Take(endIndex)
.ToList();
Here in this query please take a look at FollowUpDate and ServiceType, these both i have fetched from Opportunity table, is there any other work around to get these both..
One to Many Relationship in tables is like: ProspectProfile -> Opportunities
Whether the query i have written is ok or is there any another work around that can be done in easier way.
The only thing you can improve is to avoid ordering twice by changing your code to this:
var profilelst
= dbContext.ProspectProfiles
.Where(i => i.CreateId == currentUser)
.Select(i =>
{
var opportunity
= i.Opportunities
.OrderByDescending(t => t.FollowUpDate)
.First();
return new ProspectProfile
{
ProspectId = i.ProspectId,
Live = i.Live,
Name = i.Name,
ServiceETA = opportunity.ServiceETA.ToString(),
FollowUpDate = opportunity.FollowUpDate
}
}).ToList();
return profilelst.OrderByDescending(c => c.FollowUpDate).Take(endIndex).ToList();
I made several changes to your original query:
I changed it to use method chains syntax. It is just so much easier to read in my opinion.
I removed the unnecessary Skip(0).
The biggest change is in the Select part:
I changed FirstOrDefault to First, because you are accessing the properties of the return value anyway. This will throw a descriptive exception if no opportunity exists. That's better than what you had: In your case it would throw a NullReferenceException. That's bad, NullReferenceExceptions always indicate a bug in your program and are not descriptive at all.
I moved the part that selects the opportunity out of the initializer, so we need to do the sorting only once instead of twice.
There are quite a few problems in your query:
You cannot project into an entity (select new ProspectProfile). LINQ to Entities only supports projections into anonymous types (select new) or other types which are not part of your entity data model (select new MySpecialType)
ToString() for a numeric or DateTime type is not supported in LINQ to Entities (ServiceETA.ToString())
FirstOrDefault().ServiceETA (or FollowUpdate) will throw an exception if the Opportunities collection is empty and ServiceETA is a non-nullable value type (such as DateTime) because EF cannot materialize any value into such a variable.
Using .ToList() after your first query will execute the query in the database and load the full result. Your later Take happens in memory on the full list, not in the database. (You effectively load the whole result list from the database into memory and then throw away all objects except the first you have Takeen.
To resolve all four problems you can try the following:
var profilelst = dbContext.ProspectProfiles
.Where(p => p.CreateId == currentUser)
.Select(p => new
{
ProspectId = p.ProspectId,
Live = p.Live,
Name = p.Name,
LastOpportunity = p.Opportunities
.OrderByDescending(o => o.FollowUpDate)
.Select(o => new
{
ServiceETA = o.ServiceETA,
FollowUpDate = o.FollowUpDate
})
.FirstOrDefault()
})
.OrderByDescending(x => x.LastOpportunity.FollowUpDate)
.Skip(startIndex) // can be removed if startIndex is 0
.Take(endIndex)
.ToList();
This will give you a list of anonymous objects. If you need the result in a list of your entity ProspectProfile you must copy the values after this query. Note that LastOpportunity can be null in the result if a ProspectProfile has no Opportunities.