Filter expression cannot be specified for an entity type because it may only be applied to the root entity type in a hierarchy - asp.net-core

I am trying to change the way asp.net generates its identity tables, trying to base the generation on an Id of int instead of a Guid(string), also adding a different schema(instead of dbo -> Security) and a QueryFilter for all my entities, in that case I created for each class a Mapping but will illustrate the idea with just one that is giving me the error.
public class AspNetRole : IdentityRole<int>, IEntityBase
{
public bool IsDeleted { get; set; }
public string CreatedBy { get; set; }
public string UpdatedBy { get; set; }
public DateTime? CreatedOn { get; set; }
public DateTime? UpdatedOn { get; set; }
}
public interface IEntityBase
{
int Id { get; set; }
bool IsDeleted { get; set; }
string CreatedBy { get; set; }
string UpdatedBy { get; set; }
DateTime? CreatedOn { get; set; }
DateTime? UpdatedOn { get; set; }
}
The mapping class with the QueryFilter:
public class AspNetRoleMap : IEntityTypeConfiguration<AspNetRole>
{
public void Configure(EntityTypeBuilder<AspNetRole> builder)
{
builder.ToTable(name: "AspNetRole", schema: "Security");
builder.HasQueryFilter(app => !app.IsDeleted);
}
}
The DbContext:
public class AspNetSecurityDbContext : IdentityDbContext<AspNetUser, IdentityRole<int>, int>
{
public AspNetSecurityDbContext(DbContextOptions<AspNetSecurityDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.ApplyConfiguration(new AspNetRoleMap());
}
}
Once I run the migration I am getting the following error:
The filter expression 'app => Not(app.IsDeleted)' cannot be specified
for entity type 'AspNetRole'. A filter may only be applied to the root
entity type in a hierarchy.
I tried this approach https://github.com/aspnet/EntityFrameworkCore/issues/10259 but still getting more errors
builder.HasQueryFilter(app => !((IEntityBase)app).IsDeleted);

The problem has nothing in common with EF Core query filter, but the incorrect base generic IdentityDbContext argument. Here
: IdentityDbContext<AspNetUser, IdentityRole<int>, int>
you are passing IdentityRole<int>, which in the base OnModelCreating will be configured as entity, hence EF Core will map your AspNetRole entity using TPH inheritance strategy, which along with the additional discriminator column introduces additional constraints, like the query filter exception you are getting.
To fix that, pass the correct generic type argument which in this case is the custom AspNetRole class:
: IdentityDbContext<AspNetUser, AspNetRole, int>
In case you create other custom entities inheriting the generic IndentityXyz<> classes, take a look at the other base IdentityDbContext classes having more generic type arguments, and select the one that allows you the pass all your custom identity derived types.

Related

HasKey: error while using Fluent API to configure which property should be used as the foreign key

I have two classes (Parent, Child) in ASP.Net Core and I'm using code first approach, my real project is more complex than that, so i have to use this method to migrate to database.
The issue here is when I'm defining the relations in Db Context class i face this error noting that I'm following this Microsoft document https://learn.microsoft.com/en-us/ef/core/modeling/relationships, and you can find the main class below:
Error: Cannot implicitly convert type 'System.Collections.Generic.List<logintest.Models.Chlid>' to 'System.Collections.Generic.IEnumerable<logintest.Models.Child>'. An explicit conversion exists (are you missing a cast?)
public class Parent
{
public int Id { get; set; }
public List<Chlid> Childs { get; set; }
}
public class Child
{
public int Id { get; set; }
public int ParentId { get; set; }
public Parent Parent { get; set; }
}
public class AppDbContext : DbContext
{
public DbSet<Parent> Parents { get; set; }
public DbSet<Child> Childs { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Child>()
.HasOne(p => p.Parent)
.WithMany(b => b.Childs)
.HasForeignKey(b => b.ParentId);
}
}

Automapper profile maps its props to my dto

Hi.
I want just map entity to dto but profile props map to the dto.
Now what can i do for the following problem?
public abstract class DtoProfile<TEntity, TDto> : Profile, IProfile
{
public DtoProfile()
{
var profile = CreateMap<TEntity, TDto>();
CustomMapping(profile);
}
public virtual void CustomMapping(IMappingExpression<TEntity, TDto> mapping)
{
}
}
public class UserDto : DtoProfile<Users,UserDto>
{
public int Id { get; set; }
public string FullName { get; set; }
public string NationalCode { get; set; }
public DateTime CreatedDate { get; set; }
}
You're doing this in a wrong way. Mapping Profiles should be for mapping, and not to be inherited from the Dto itself.
You're inherting from Profile in DtoProfile, and these properties are defined in Profile, so they will be there. And as the name of class, it's for mapping profile, and shouldn't be used for Dto.
You should have two classes, one is UserDto where you add all the properties you need to map, and the other is UserDtoProfile which inherits from DtoProfile<User, UserDto> and apply your mappings.

automapper unflatten excludes value from parent object

I've a situation where entity framework core (2.0) is performing additional work to a parent table when I update a row in a child table. I've pin-pointed the cause to a value not being set in the unflattened object tree produced by AutoMapper (I'm not saying it is an error in AutoMapper; it's probably more to do with my code).
I'm using ASP.NET Core 2.0, C#, EF Core 2.0 and AutoMapper for the API development side. The database already exists and the EF classes scaffolded from it.
To keep it short, the child table is Note and the parent table is NoteType. The EF classes (extraneous columns removed) are as follows :
//Entity classes
public partial class Note
{
public int NoteBookId { get; set; }
public short NoteSeqNo { get; set; }
public short NoteTypeId { get; set; }
public string NoteText { get; set; }
public NoteBook NoteBook { get; set; }
public NoteType NoteType { get; set; }
}
public partial class NoteType
{
public NoteType() { Note = new HashSet<Note>(); }
public short NoteTypeId { get; set; }
public string NoteTypeDesc { get; set; }
public ICollection<Note> Note { get; set; }
}
//DTO class
public class NoteDto
{
public int NoteBookId { get; set; }
public short NoteSeqNo { get; set; }
public short NoteTypeId { get; set; }
public string NoteTypeNoteTypeDesc { get; set; }
public string NoteText { get; set; }
}
public class NoteTypeDto
{
public short NoteTypeId { get; set; }
public string NoteTypeDesc { get; set; }
}
(NoteBookId + NoteSeqNo) is Note's primary key.
NoteTypeId is the NoteType's primary key.
Configuration
This is the AutoMapper configuration:
// config in startup.cs
config.CreateMap<Note,NoteDto>().ReverseMap();
config.CreateMap<NoteType,NoteTypeDto>().ReverseMap();
Read the data
As a result of data retrieval I get the expected result and the parent note type description is populated.
// EF get note in repository
return await _dbcontext.Note
.Where(n => n.NoteId == noteId && n.NoteSeqNo == noteSeqNo)
.Include(n => n.NoteType)
.FirstOrDefaultAsync();
// Get note function in API controller
Note note = await _repository.GetNoteAsync(id, seq);
NoteDto noteDto = Mapper.Map<NoteDto>(note);
Example JSON result:
{
"noteBookId": 29,
"noteSeqNo": 19,
"noteTypeId": 18,
"noteTypenoteTypeDesc": "ABCDE",
"noteText": "My notes here."
}
Update the data
When the process is reversed during an update, the API controller maps the dto to the entity
Mapper.Map<Note>(noteDto)
Then when it is passed to EF by the repository code, EF tries to add a NoteType row with id 0. The unflattened object tree looks like this:
Note
NoteBookId = 29
NoteSeqNo = 19
NoteTypeId = 18
NoteTypeNoteTypeDesc = "ABCDE"
NoteText = "My notes updated."
NoteType.NoteTypeDesc = "ABCDE"
NoteType.NoteTypeId = 0
The parent id column (NoteType.NoteTypeId) value is 0 and is not assigned the value of 18 which is what I expected.
(During debugging I manually set NoteType.NoteTypeId to 18 to ensure EF did nothing with it).
To work around this at the moment I nullify the NoteType in the Note in the repository code.
Should I expected AutoMapper to populate all the parent properties with setters or have I missed some configuration? Perhaps there is a glaring flaw in my approach?
When AutoMapper reverses the mapping, it has to collect all information for nested objects from the flat object. Your DTO only carries a value for the mapping NoteType -> NoteTypeDesc. Not for NoteType -> NoteTypeId, so AM really doesn't have any idea where to get that value from.
If you want to rely on flattening only, the only way to change that is to add a flattened NoteTypeId to the DTO besides the unflattened one:
public class NoteDto
{
public int NoteBookId { get; set; }
public short NoteSeqNo { get; set; }
public short NoteTypeId { get; set; } // Not flattened
public short NoteTypeNoteTypeId { get; set; } // Flattened
public string NoteTypeNoteTypeDesc { get; set; }
public string NoteText { get; set; }
}
The alternative is to add this to your mapping:
config.CreateMap<Note, NoteDto>()
.ForMember(dest => dest.NoteTypeId,
e => e.MapFrom(src => src.NoteType.NoteTypeId))
.ReverseMap();
MapFrom-s (including the default unflattening) are reversed now. You can drop ReverseMap and create the maps, ignore Note.NoteType or ignore the offending path, Note.NoteType.NoteTypeDesc.

Entity framework code first, delete childs by updating parent

As entity framework states, "Code first", here we go with the code first...
public class BaseModel
{
[Key]
public Guid Id { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateChanged { get; set; }
public BaseModel()
{
this.Id = Guid.NewGuid();
this.DateCreated = DateTime.Now;
this.DateChanged = DateTime.Now;
}
}
public class Association: BaseModel
{
public string Name { get; set; }
public string Type { get; set; }
public virtual List<Rule> Rules { get; set; }
public Association()
: base()
{
}
}
public class Rule: BaseModel
{
[ForeignKey("Association")]
public Guid AssociationId { get; set; }
//[Required]
public virtual Association Association { get; set; }
//[Required]
public string Name { get; set; }
public string Expression { get; set; }
public virtual List<Action> Actions { get; set; }
public Rule()
: base()
{
}
}
public class Action: BaseModel
{
public string Name { get; set; }
public string ActionType { get; set; }
[ForeignKey("Rule")]
public Guid RuleId { get; set; }
public virtual Rule Rule { get; set; }
public int Order { get; set; }
public Action()
: base()
{
}
}
So these are my four model classes that are using entity framework code first.
Each inherit from the baseclass, so they all have an Id Guid as Primary Key.
An Association has a list of rules. (Rule has FK to Association)
A Rule as has a list of actions. (Action has FK to Rule)
What I would like to do is only change and save the most upwards class = Association.
For example when deleting a rule, I would like this code to work:
public ActionResult DeleteRule(Guid assId, Guid ruleId)
{
Association ass = this.DataContext.Associations.FirstOrDefault(a => a.Id == assId);
ass.Rules.RemoveAll(r => r.Id == ruleId);
this.DataContext.SaveChanges();
return RedirectToAction("Index");
}
On the context.savechanges this is giving me this error:
'The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.'
This error also occurs when deleting an action.
Is there a way to change the most upper (Association) object AND ONLY changing things to this Association.
I DO NOT want to say context.Rules.remove(...) or context.actions.remove(...)
here's the source: http://server.thomasgielissen.be/files/mvctesting.zip
you need VS2012, all nuget packages are included in zip and you should be able to build and run the project.
Thanks in advance for your feedback!
Greetz,
Thomas
I you want to fix this issue, you should store your relations through junction tables. I don't think that you can achieve what you need, with this model.
However if you put a junction table(or entity) between your entities, you can easily remove child objects and update parent object.
For example, put a junction entity between Association and Rule:
public class AssociationRule: BaseModel
{
public Guid AssociationId { get; set; }
public Guid RuleId { get; set; }
[ForeignKey("AssociationId")]
public virtual Association Association { get; set; }
[ForeignKey("RuleId")]
public virtual Rule Rule { get; set; }
public Association()
: base()
{
}
}
Now, you can easily remove any rule from any association:
public ActionResult DeleteRule(Guid assId, Guid ruleId)
{
AssociationRule assr = this.DataContext
.AssociationRuless
.FirstOrDefault(ar => ar.AssociationId == assId && ar.RuleId == ruleId);
this.DataContext.AssociationRules.Remove(assr);
this.DataContext.SaveChanges();
return RedirectToAction("Index");
}

Fluent hierarchy

I have the following situation with fluent nhibernate:
public class Stuff
{
public Stuff()
{
Entities = new List<Entity>();
}
public virtual int Id { get; set; }
public virtual IList<Entity> Entities { get; set; }
}
public abstract class Entity
{
public virtual int Id { get; set; }
public virtual string Type { get; set; }
public virtual Stuff Stuff { get; set; }
}
public class Person : Entity
{
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
}
public class Animal : Entity
{
public virtual string Species { get; set; }
}
And then, i have the following code to use automap and generate these mappings:
var sessionFactory =
Fluently.Configure().Database(persistenceConfigurer).Mappings(
m =>
m.AutoMappings.Add(
AutoMap.Source(new Types(typeof(Entity), typeof(Person), typeof(Animal), typeof(Stuff))))
.ExportTo(#"e:\")).ExposeConfiguration(BuildSchema).BuildSessionFactory();
however, what's happening is that i get the following exception:
---> NHibernate.MappingException: Association references unmapped class: ConsoleApplication1.Models.Entity
if i make the entity class non abstract this works, however, i'd like to avoid having that table in the database but still maintain the hierarchy concept with the re
You need to add your auto mappings like this
AutoMap.AssemblyOf<Entity>(yourConfiguration).IgnoreBase<Entity>();
Not only will this ignore your Entity base class but you don't need to add each entity manually so long as each model inherits from Entity.