Entity Framework Core: Global select filter - .net-core

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

Related

Use Extension to filter OneToMany relationship

I'm using API Platform 3 and Gedmo's Softdeleteable Extension. For my entities, I'm using Extensions to add $queryBuilder->andWhere('o.deletedAt IS NULL') to all queries.
This is working fine, but when using OneToMany relations, API Platform doesn't use these extensions and therefor shows 'softdeleted' entities.
Let's take a slightly modified example from their documentation:
#[ApiResource]
class User
{
#[ORM\OneToMany]
#[Serializer\Groups(['user'])]
public Collection $offers;
}
/api/offers/1 correctly shows a 404, because it has been softdeleted. But /api/users/1 shows something like this:
{
username: 'foo',
offers:
{
'#id': "/api/offers/1",
deletedAt: "2022-01-27T12:04:45+01:00"
}
}
How can I change the query that API Platform uses to fetch the relationships?
You have two methods of achieving this.
Filter hydrated objects:
Inside the existing (or completely new) getter:
return $this->offers->filter(function(Offer offer){
return $offer->getDeletedAt() !== NULL;
});
Apply criteria to query:
Again, inside the existing (or completely new) getter:
$criteria = Criteria::create()
->andWhere(Criteria::expr()->eq('deletedAt', NULL));
return $this->offers->matching($criteria);
The Criteria method is preferable if your user could have LOTS of deleted offers - the filtering would be performed on a database level.
Hope this helps.
Btw, both of these methods are well explained in SymfonyCasts tutorials:
https://symfonycasts.com/screencast/api-platform-security/filtered-collection
https://symfonycasts.com/screencast/symfony4-doctrine-relations/collection-criteria

Implementing an ?include=attributes querystring with Entity Framework and AutoMapper

A reasonably common pattern in REST APIs is to support conditional inclusion (or sometimes exclusions) of attributes in a response using a query parameter. For example:
GET /customers?include=notes
In this case, the Notes attribute is optional and won't be included in the response unless specifically requested.
The naive solution leaves a lot to be desired:
var query = dbContext.Query<Customer>();
if (include.Contains('notes'))
{
return query.Select(x => new Customer
{
Id = x.Id,
Name = x.Name,
Notes = x.Notes,
});
}
else
{
return query.Select(x => new Customer
{
Id = x.Id,
Name = x.Name
});
}
Is there a clean, elegant, DRY way to implement this feature with a combination of Entity Framework and AutoMapper in ASP.NET?

Entity Framework 6: Turn off Sequential GUIDs

Entity Framework 6 appears to default to sequential GUIDs instead of completely random ones. How do I turn this off?
See CodePlex work item: http://entityframework.codeplex.com/workitem/71
From the changeset linked to that work item, you'll see that the GuidColumnDefault isn't based on any settings, but just returns the default based on the provider type
Looking at this link, it would appear you can set it manually in your migrations though:
// Excerpt from migration in link above:
public override void Up()
{
CreateTable(
"dbo.Items",
c => new
{
Id = c.Guid(nullable: false,
identity: true,
// You would use newid() instead.
defaultValueSql: "newsequentialid()"),
})
.PrimaryKey(t => t.Id);
}

ASP.Net Roles and Membership check for Users in role for multiple roles

I have users in membership and one user can have one or more than one roles assigned. I want to check for specific roles of a page for example:
Role1, Role2, Role3, Role4, Role5
Users who have access of Role2 and Role3 can access Page1.aspx and Also if user who have access of Role1 and Role2 also can access because Role2 is there available.
I have implemented membership and have list of user's roles string array with the help of
string[] roles = Roles.GetRolesForUser(User.Identity.Name);
How can I check against multiple roles? May I need to do one by one check with using Roles.IsUserInRole function?
I have used; It returns 0 count because of Partner in capital. How can I do ignore case? And is below is right way to check array against array?
string[] userroles = { "Partner", "testsetsr" };
string[] requiredroles = { "contractor", "customer", "sims", "nonclientau", "partner" };
var countInRoles = userroles.Intersect(requiredroles).Count();
You would have to implement your own Authorization Filter Attribure. To do that you can create a class extending 'AuthorizationAttribute', then override OnAuthorization where you can specify your required role-checks.
A small example on how to do that would be here in Nimesh's .Net Blog
The example is not a solution to your personal problem, but should give you the idea about what you need to do.
Edit:
The same goes for your applications role-provider, extend it and override IsUserInRole (or maybe better even add a new method) to provide the checks you need in order to have Roles.IsUserInRole work the way you want.
To your 2nd question:
if you have 2 arrays and want to count how many members of 1st array are members of the 2nd while ignoring case you can do that using linq
var countInRoles = userroles.Select(u => u.ToLower()).Count(u => requiredroles.Select(r => r.ToLower()).Contains(u));
On the other hand, lets vice versa the lists if you have a list of required roles to access some action you can also check if a user has all required roles using linq (again with ignoring case)
string[] requiredroles = { "Partner", "testsetsr" };
string[] userroles = { "contractor", "testsetsr", "customer", "sims", "nonclientau", "partner" };
var userHasAllRequiredRoles = requiredroles.Select(r => r.ToLower()).All(r => userroles.Select(u => u.ToLower()).Contains(r));

Different RavenDB collections with documents of same type

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

Resources