I'm using Entity Framework 4.3.
I have a user entity named Lender - a lender has many lending Tiers. There are many other entities linked to lender but for or the purpose of clarity, I'll keep the structure simple.
What I need to do is return a lender but only with a single matched Tier based on some criteria, in other words a flattened structure for Tier.
I'm unsure as to how to do this in a single statement - can anyone help?
The meaning of your "flattened structure for Tier" is a bit dark to me but perhaps you are looking for a projection like the following one:
var lenderWithTier = context.Lenders
.Where(l => l.Something == something) // lender filter
.Select(l => new
{
Lender = l,
Tier = l.Tiers
.Where(t => t.SomethingElse == somethingElse) // tier filter
.FirstOrDefault() // only one tier
})
.FirstOrDefault();
lenderWithTier is an anonymous object that has a lenderWithTier.Lender property and a lenderWithTier.Tier property which is a single Tier object (or null if none of the tiers related to the lender matches the tier filter).
Related
The bounty expires in 1 hour. Answers to this question are eligible for a +100 reputation bounty.
eia92 wants to draw more attention to this question.
I recently upgraded my project to .NET Core 6 and now removing records from my look up tables is not working. I have a Risk object that has a collection of Users. Removing users from the risk object no longer works. Any ideas what I'm doing wrong?
My lookup table is called RiskItemUser, and it has two columns, RiskItemId and UserId.
Code:
var postSavedRisk = _riskService.Queryable().Include(c => c.AssignedTo).Where(w => w.Id == riskitem.Id).FirstOrDefault();
List<User> usersToRemove = postSavedRisk.AssignedTo.Where(c => userNamesToRemove.Contains(c.UserName)).ToList();
using (var db = new ApplicationDbContext())
{
var postSavedAssginedTo = db.RiskItemUser
.Where(w => w.RiskItemId == riskitem.Id)
.ToList();
foreach (var userToRemove in usersToRemove)
{
foreach (var riskAssignedTo in postSavedAssginedTo)
{
if(userToRemove.Id == riskAssignedTo.UserId)
db.RiskItemUser.Remove(riskAssignedTo);
await db.SaveChangesAsync().ConfigureAwait(false);
}
}
}
The code, as you show it, looks like it should work, although some parts are hidden. Therefore, it's hard to tell how to make it work. But there's room for simplification, which should result in working code.
You want to remove users whose names are specified by userNamesToRemove from a risk that's specified by riskitem.Id. Assuming that there's a navigation property RiskItemUser.User, removing these data could be done by essentially one line of code:
db.RiskItemUser.RemoveRange(
db.RiskItemUser.Where(ru => ru.RiskItemId == riskitem.Id
&& userNamesToRemove.Contains(ru.User.Name)));
await db.SaveChangesAsync().ConfigureAwait(false);
You tagged EFC 6, but as of EFC 7.0, there's support for bulk delete (and update) functions, allowing for single-statement deletion of multiple database records:
db.RiskItemUser
.Where(db.RiskItemUser.Where(ru => ru.RiskItemId == riskitem.Id
&& userNamesToRemove.Contains(ru.User.Name)))
.ExecuteDelete();
This will execute one delete statement, whereas the previous method will execute one statement per row.
Note that this bulk method is like executing raw SQL. There's no communication with EF's change tracker and EF can't coordinate the correct order of statements. I think the general advice should be to not mix these bulk methods with regular SaveChanges calls.
In EFCore5, implicit tables are saved as Dictionary<TKey, object> sets, knows as Property Bag Entity Types. However, I cannot figure out how to create a LINQ query with a Where() clause that compiles for MySQL for such a property bag entity type.
For example, this code successfully retrieves the IQueryable reference to an intermediate table (generated by EFcore5's implicity many-to-many feature) given the ISkipNavigation:
ISkipNavigation nav = // some many-to-many relationship
IQueryable<Dictionary<string, object>> intermediateTable =
context.Set<Dictionary<string, object>>(nav.JoinEntityType.Name);
And this code successfully retrieves all the entries in the intermediate table:
List<Dictionary<string, object>> joins = await intermediateTable.ToListAsync();
In the resulting List, each Dictionary has just one key/value (representing the row).
However, I cannot figure out how to add a .Where() clause to the LINQ query which will compile:
joinTable.Where(d => d.Keys.First() == "foo").ToList();
joinTable.Where(d => d.Keys.Any(k => k == "foo")).ToList();
The error is:
Translation of member 'Keys' on entity type 'MachinePartMachineProfile (Dictionary<string, object>)' failed. This commonly occurs when the specified member is unmapped. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'
I do not wish to do client-side parsing for performance reasons (the join table will be to big).
I note that the type reported by the error is MachinePartMachineProfile (Dictionary<string, object>). Some investigation showed that these types are being generated based upon the static Model.DefaultPropertyBagType (which is a Dictionary<string, object>). But despite staring at the EFCore code base, I cannot discern how to correctly query such a default property bag type.
I am using MySQL as my database, if it is relevant.
You can index the dictionary directly, with knowledge of the column name.
Working example would be:
joinTable.Where(d => d[columnName] == "foo").ToList();
And for the sake of completeness, if you have an ISkipNavigation instance, you can infer these keys as follows:
string foreignKey = nav.ForeignKey.Properties.First().GetColumnBaseName();
string localKey = nav.Inverse.ForeignKey.Properties.First().GetColumnBaseName();
In EF Core you can add global query filters to exclude data that fulfill some condition. I was wondering if the "same" exists for globally selecting properties of entities, e.g. to globally exclude sensitive information from ApplicationUser from .NET Core Identity?
E.g. something like this:
builder.Entity<ApplicationUser>().HasSelectFilter(u => new ApplicationUser {
Id = u.Id,
Email = u.Email,
...
});
EDIT: I just found that you can use
builder.Entity<ApplicationUser>().Ignore(u => u.PasswordHash);
when configuring relations with the FluentAPI. However, it seems to create problems within Identity.
Ignore properties in data model while keeping them in EF Core migrations
No, by default nothing supports it in EF Core.
Disclaimer: I'm the owner of the project Entity Framework Plus
The EF+ Query IncludeFilter (free and open source) allows using the filter as a Global select filter as well.
Example:
QueryFilterManager.Filter<Customer>(c => c.Select(x => new Customer() { CustomerId = x.CustomerId, Name = x.Name }));
// ...code...
var customers = context.Customers.ToList();
Online Example: https://dotnetfiddle.net/wArKog
I am trying to debug this code to make sure that it queries a database for users that have zero applications for the week using LINQ. When I connect it with an HTML.ActionLink I am getting no proper filter results.
case "No_Activity":
dateCriteria = DateTime.Now.AddDays(-7);
students = students.Where(s => db.JPApplications.Where(a => a.ApplicationUserId == s.ApplicationUserId && a.JPApplicationDate >= dateCriteria).Count() == 0);
break;
You have to add ToList() because students for this is IQueryable try this :
students = students.Where(s => !db.JPApplications.Any(a => a.ApplicationUserId == s.ApplicationUserId && a.JPApplicationDate >= dateCriteria)).ToList()
and Init your date like this:
dateCriteria = DateTime.ToDay.AddDays(-7)
I replaced the count==0 by Any().
Apparently you have tables Students and Applications. There is a one-to-many relation between Students and Applications: Every Student has zero or more Applications, and every Application is owned (created by?) exactly one Student, using a foreign key.
Furthermore every Application has a (DateTime?) property ApplicationDate.
Requirement:
Given a (DateTime) variable dateCriterium, query all Students that didn't submit any Application on or after dateCriterium.
In baby steps: the Students and all applications that were submitted on or after dateCriterium:
DateTime dateCriterium = ...
IQueryable<Student> students = ...
IQueryable<Application> applicationsAfterDate = allApplications
.Where(application => application.ApplicationDate >= dateCriterium);
Extract the Ids of all Students that did submit any of these applications. Remove duplicates:
var idsOfStudentsWithApplicationsOnOrAfterDate = applicationsAfterDate
.Select(application => application.StudentId)
.Distinct();
Fetch all students that have an Id that is not in the list of student Ids of students that submitted an application on or after date = that does not contain the student Id
var studentsWithoutApplicationsOnOrAfterDate = students
.Where(student => !idsOfStudentsWithApplicationsOnOrAfterDate.Contains(student.Id));
Until now you only created the query, no the database is not queried yet:
var result = studentsWithoutApplicationsOnOrAfterDate.ToList();
TODO: if desired you can put this in one big LINQ statement. As you didn't query the database until the ToList, this won't improve performence. It surely will deteriorate the readability of the code.
In RavenDB I can store objects of type Products and Categories and they will automatically be located in different collections. This is fine.
But what if I have 2 logically completely different types of products but they use the same class? Or instead of 2 I could have a generic number of different types of products. Would it then be possible to tell Raven to split the product documents up in collections, lets say based on a string property available on the Product class?
Thankyou in advance.
EDIT:
I Have created and registered the following StoreListener that changes the collection for the documents to be stored on runtime. This results in the documents correctly being stored in different collections and thus making a nice, logically grouping of the documents.
public class DynamicCollectionDefinerStoreListener : IDocumentStoreListener
{
public bool BeforeStore(string key, object entityInstance, RavenJObject metadata)
{
var entity = entityInstance as EntityData;
if(entity == null)
throw new Exception("Cannot handle object of type " + EntityInstance.GetType());
metadata["Raven-Entity-Name"] = RavenJToken.FromObject(entity.TypeId);
return true;
}
public void AfterStore(string key, object entityInstance, RavenJObject metadata)
{
}
}
However, it seems I have to adjust my queries too in order to be able to get the objects back. My typical query of mine used to look like this:
session => session.Query<EntityData>().Where(e => e.TypeId == typeId)
With the 'typeId' being the name of the new raven collections (and the name of the entity type saved as a seperate field on the EntityData-object too).
How would I go about quering back my objects? I can't find the spot where I can define my collection at runtime prioring to executing my query.
Do I have to execute some raw lucene queries? Or can I maybe implement a query listener?
EDIT:
I found a way of storing, querying and deleting objects using dynamically defined collections, but I'm not sure this is the right way to do it:
Document store listener:
(I use the class defined above)
Method resolving index names:
private string GetIndexName(string typeId)
{
return "dynamic/" + typeId;
}
Store/Query/Delete:
// Storing
session.Store(entity);
// Query
var someResults = session.Query<EntityData>(GetIndexName(entity.TypeId)).Where(e => e.EntityId == entity.EntityId)
var someMoreResults = session.Advanced.LuceneQuery<EntityData>(GetIndexName(entityTypeId)).Where("TypeId:Colors AND Range.Basic.ColorCode:Yellow)
// Deleting
var loadedEntity = session.Query<EntityData>(GetIndexName(entity.TypeId)).Where(e =>
e.EntityId == entity.EntityId).SingleOrDefault();
if (loadedEntity != null)
{
session.Delete<EntityData>(loadedEntity);
}
I have the feeling its getting a little dirty, but is this the way to store/query/delete when specifying the collection names runtime? Or do I trap myself this way?
Stephan,
You can provide the logic for deciding on the collection name using:
store.Conventions.FindTypeTagName
This is handled statically, using the generic type.
If you want to make that decision at runtime, you can provide it using a DocumentStoreListner