Entity Framework shows inconsistent behaviour when used with Asp.net Identity - asp.net

I have 3 tables Violation,Comment and and auto generated AspNetUsers respectively.The relationship between them as follows.
I am using code-first approach and my models are as follows.Some properties are removed for brevity.
Violation Model
public class Violation
{
public Violation()
{
this.Comments = new HashSet<Comment>();
}
public int Id { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
public virtual ApplicationUser CreatorUser { get; set; }
}
Comment Model
public class Comment
{
public int Id { get; set; }
[Required]
public string Content { get; set; }
[Required]
public DateTime PostedDateTime { get; set; }
public ApplicationUser ApplicationUser { get; set; }
public Violation Violation { get; set; }
}
ApplicationUser(AspNetUsers Table)
public class ApplicationUser : IdentityUser
{
public ApplicationUser()
{
this.Comments = new List<Comment>();
this.Violations = new List<Violation>();
}
public virtual List<Comment> Comments { get; set; }
public virtual List<Violation> Violations { get; set; }
}
The problem is that when I try to retrieve Comment's ApplicationUser navigation property , I see many of them pointing to a null property even database has proper record for each of them.
Shortly,EF doesn't retrieve database records properly.I stuck with it,can't find the reason.

In fact, it's not being lazy-loaded. You didn't add the virtual keyword to your Comment.ApplicationUser property, so Entity Framework cannot override it to add the lazy-loading logic. As a result, it's always going to be null unless you explicitly load it. Add the virtual keyword, and you'll be fine.

If you want the navigation properties populated you need to include them in the query:
var comments = context.Comments
.Include(c => c.Violation)
.Include(c => c.ApplicationUser)
.Where(x => x.Violation.Id == violationId);
https://msdn.microsoft.com/en-us/data/jj574232.aspx#eager

Related

Establishing one to one relations with Entity Framework 7

Having the following parent class:
[Table("smart_recharge_registro")]
public class SmartRechargeRegistro
{
[Key]
public int id { get; set; }
public SmartRechargeRequest request { get; set; }
public SmartRechargeProceso proceso { get; set; }
public SmartRechargeResponse response { get; set; }
}
Which in turn references the following child classes:
[Table("smart_recharge_request")]
public class SmartRechargeRequest
{
public String nombreDeUsuario { get; set; }
public String passwordDeUsuario { get; set; }
public String msisdnSuscriptor { get; set; }
}
and:
[Table("smart_recharge_proceso")]
public class SmartRechargeProceso
{
[Key]
public int id { get; set; }
public String carrierId { get; set; }
public String cliente { get; set; }
public String network { get; set; }
}
and lastly:
[Table("smart_recharge_response")]
public class SmartRechargeResponse
{
public Boolean responseSuccess { get; set; }
public int responseCode { get; set; }
public String? responseDetails { get; set; }
}
The Add-Migration and Update-Database command execute without problems. However, when I try to save
await _repository.RegistroColeccion.AddAsync(registro);
await _repositorio.SaveChangesAsync();
I get the following error:
Microsoft.EntityFrameworkCore.DbUpdateException: Could not save changes. Please configure your entity type accordingly.
---> MySql.Data.MySqlClient.MySqlException (0x80004005): Cannot add
or update a child row: a foreign key constraint fails
(beservicebroker_dev.registro_eventos_srdotnet, CONSTRAINT
FK_registro_eventos_srdotnet_SmartRechargeProceso_procesoid FOREIGN
KEY (procesoid) REFERENCES smartrechargeproceso (id) O)
To solve the problem, I tried to create one-to-one relationships following this tutorial
modelBuilder.Entity<SmartRechargeRegistro>()
.HasOne(s => s.request)
.WithOne(r => r.SmartRechargeRegistro)
.HasForeignKey<SmartRechargeRequest>(r => r.id);
modelBuilder.Entity<SmartRechargeRegistro>()
.HasOne(s => s.proceso)
.WithOne(p => p.SmartRechargeRegistro)
.HasForeignKey<SmartRechargeProceso>(p => p.id);
modelBuilder.Entity<SmartRechargeRegistro>()
.HasOne(s => s.response)
.WithOne(r => r.SmartRechargeRegistro)
.HasForeignKey<SmartRechargeResponse>(r => r.id);
Inside SmartRechargeRequest, SmartRechargeProceso and SmartRechargeResponse, added the following:
[JsonIgnore]
public SmartRechargeRegistro SmartRechargeRegistro { get; set; }
Also added inside SmartRechargeRequest and SmartRechargeResponse an id
[Key]
public int id { get; set; }
I'm still unable to test the endpoint because the SmartRechargeRequest and SmartRechargeResponse are completely disfigured in the swagger (even if the [JsonIgnore] or [IgnoreDataMember] annotations are set) due to the presence of that SmartRechargeRegistro object.
I'm pretty sure my solution is misguided and I'm getting the process completely wrong.
What would be the proper way to map one-to-one relationships for this case? Any help will be appreciated.
Please note that in reality, these classes are huge (dozens of properties), so it's not possible to merge all of them on a single table.

EF Core one-to-many relationship with multiple contexts (databases)

I have contexts with entities like this:
public class CompanyContext : DbContext
{
public DbSet<StoreModel> Stores { get; set; }
// Other entities
}
public class DepartmentContext : DbContext
{
public DbSet<OrderModel> Orders { get; set; }
// Other entities
}
public class StoreModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public virtual ICollection<OrderModel> ReceivedOrders { get; set; }
public virtual ICollection<OrderModel> PreparedOrders { get; set; }
public virtual ICollection<OrderModel> IssuedOrders { get; set; }
}
public class OrderModel
{
public Guid Id { get; set; }
public string Details { get; set; }
public StoreModel GettingStore { get; set; }
public StoreModel PreparingStore { get; set; }
public StoreModel IssuanceStore { get; set; }
}
For example a user makes an order in storeA, but wants to receive it in storeC, and it order will preparing in storeB. And I needs a statiscics about store received/prepared/issued orders.
When I try to create a migrations, EF throws exceptions "Unable to determine the relationship represented by navigation 'OrderModel.GettingStore' of type 'StoreModel'" and "Unable to determine the relationship represented by navigation 'StoreModel.IssuedOrders' of type 'ICollection<OrderModel>'". If I understand correctly, this happens because entities are defined in different contexts.
Now I just use next model:
public class OrderModel
{
public Guid Id { get; set; }
public string Details { get; set; }
public Guid GettingStoreId { get; set; }
public Guid PreparingStoreId { get; set; }
public Guid IssuanceStoreId { get; set; }
}
This works fine, but perhaps there are options that allow to create such a structure using navigation properties, with correct relationships between these entities from different contexts(databases).
First, the map of a different database was not placed in tables of different application formats, so think that you have a domain that should be well defined in your application, that way you would have the mapping of your application like this:
public class DomainNameContext: DbContext
{
public DomainNameContext(): base()
{
}
public DbSet<StoreModel> Stores { get; set; }
public DbSet<OrderModel> Orders { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// config mapping methods
}
}
another thing, the relation you are using doesn't work so you can't have a repetition of Orders within the same class because this is not one -> many, this statement means that a StoreModel line can have many lines in the OrderModel this way would be like this
public class OrderModel
{
public Guid Id { get; set; }
public string Details { get; set; }
public Guid StoreModeId { get; set; } // this part will show the entity framework that this is the fk it will correlate
public StoreModel StoreModel { get; set; }
}
public class StoreModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public virtual ICollection<OrderModel> OrderModels { get; set; }
}
see that if you are wanting to have many StoreModel related to many OrderModel then you need to use many -> many which microsoft documentation foresees to use as well
good to map this within its context it is necessary in OnModelCreating to use its mapping like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// config mapping methods
modelBuilder.Entity<StoreModel>()
.HasMany<OrderModel>(g => g.OrderModels )
.HasForeignkey<Guid>(s => s.StoreModeId )
}
you can have a look at the microsoft documentation enter link description here, enter link description here
now if you need to map between contexts you will have to use dapper to make separate queries in separate bases the entity has support for that in this link enter link description here
and then you can make the necessary inner joins so that you can use it but natively this does not exist, I advise you to rethink your database so that it can make more sense to a relational model, perhaps putting types for your StoreModel and OrderModel so you can use the way I wanted the types GettingStore, PreparingStore, IssuanceStore using an enum for this to make it explicit

Entity Framework Core error occures when trying to include other entity

I am working with some entities which are connected by foreign keys using the Entity Framework code-first approach. When I try to include one from another I get an error that says:
The expression 'user.Organization' is invalid inside an 'Include' operation, since it does not represent a property access
These are my classes:
public class User : Person
{
public StaffRole? Role { get; set; } = null;
[ForeignKey(nameof(Organization))]
public Guid? OrganizationId { get; set; }
[NotMapped]
public virtual Organization Organization { get; set; }
}
public class Organization : Auditable
{
public Organization()
{
Staffs = new List<User>();
}
[Required]
public string Name { get; set; }
public virtual ICollection<User> Staffs { get; set; }
}
I have removed some properties in order to be clear.
This is how I am trying to include
var owner = userRepository.GetAll(user => user.Id == currentUser.Id &&
user.Role == StaffRole.Owner).Include(user => user.Organization).FirstOrDefault();
I have looked around the Web to find the answer but I think I need a little bit of individual help.

include user data in query

I would like to query a table, which holds workflow events with the appr. user id. And instead of the user id, I would like to show the user name.
I have this model:
public class WorkflowEvent
{
public long Id { get; set; }
public DateTime EventDate { get; set; }
public WorkflowStatus Status { get; set; }
public int UserId { get; set; }
public AppUser User { get; }
public string Note { get; set; }
public long WorkflowId { get; set; }
public long DocumentId { get; set; }
}
And this is the user model (it is the standard IdentityUser, I use Asp.Net Identity):
public class AppUser : IdentityUser<int>
{
public string RealName { get; set; }
public string AppTheme { get; set; }
}
And this is my query:
public IEnumerable<WorkflowEvent> WorkflowEvents(int DocumentId)
{
return DataContext.WorkflowEvents.Where(e => e.DocumentId == DocumentId).Include(e => e.User).OrderBy(e => e.EventDate);
}
Unfortunatelly, I get following error on this: System.InvalidOperationException: Lambda expression used inside Include is not valid. In the WorkflowEvents table, there is a foreign key on the UserId, of course.
Have you any idea, why do I get this error message? What goes wrong?
Both Sami's and Nik's suggestions helped. I added the setter to the AppUser. And then I got the error, that the AppUser object isn't exists. This due to the fact, that I have in my app two datacontexts: one for the domain models and one for Identity. So, I just add this line of code to the domain data context:
public DbSet<AppUser> AspNetUsers { get; set; }
And everything worked like a charm.

EF6 automatically includes nested objects

Probably a simple question, but there is something I can't get my head around.
My structure Bundle -> BundleMembers -> InsuranceTypes
When I retrieve a single record form BundleMembers, and I include Bundle. EF6 automatically includes all BundleMembers in the Bundle
Example:
public async Task<List<BundleMember>> GetBundleMembers(string userId, bool includeBundle, bool includeInsuranceTypes)
{
var bundleMembers = db.BundleMembers.Where(m => string.Equals(m.UserId, userId, StringComparison.CurrentCultureIgnoreCase));
if (includeBundle)
{
bundleMembers = bundleMembers.Include(o => o.Bundle);
}
if (includeInsuranceTypes)
{
bundleMembers = bundleMembers.Include(m => m.BundleMemberInsuranceType);
}
return await bundleMembers.ToListAsync();
}
I call the function like this:
GetBundleMembers(_userManager.GetUserId(User), true, false)
Do I have to access the data from Bundle, to avoid this?
EDIT 1:
My data model looks like this:
public class Bundle
{
public int BundleId { get; set; }
public State State { get; set; }
public ICollection<BundleMember> Members { get; set; }
public ICollection<InviteLink> InviteLinks { get; set; }
public string BundleName { get; set; }
public string Description { get; set; }
public string ImagePath { get; set; }
}
public enum State
{
NotApproved,
Approved,
Disabled,
Rejected
}
public class BundleMember
{
public ApplicationUser User { get; set; }
public string UserId { get; set; }
public int BundleMemberId { get; set; }
public int BundleId { get; set; }
public Bundle Bundle { get; set; }
public bool Admin { get; set; }
public int Price { get; set; }
public int Coverage { get; set; }
public ICollection<BundleMemberInsuranceType> BundleMemberInsuranceType { get; set; }
}
I did not include BundleMemberInsuranceType and InviteLink as they are working fine.
Relevant part of ApplicationDbContext:
public DbSet<Bundle> Bundles { get; set; }
public DbSet<BundleMember> BundleMembers { get; set; }
As suggested in comments:
The described behavior is actually expected. Since includeBundle is set to true, both Bundles and referenced BundleMembers are in the context, and relationship fixup will set all navigation properties according to the FK relationships.
Obviously, this works both from BundleMembers to Bundles and from Bundles to BundleMembers since .Include does nothing more than create the SQL statements to load the related entries into the context as well and relationship fixup will do the rest.
To have the Bundles not have BundleMembers, you'll have to load them without the BundleMembers in the context and set the navigation properties yourself (EF will always set both direct and inverse navigation properties). In order to do this, there are two main ways:
Either load your bundles in a fresh context without the previous loaded BundleMembers (best practice is to load them into memory since EF navigation properties are loaded due to eager loading; you could have entries attached to two contexts and an exception will be thrown) or
Detach your BundleMembers from the context before loading the Bundles into it.

Resources