Ouerying an object inside ravendb document - ravendb

There is a doubt as of how to query or retrieve a value from an object stored in an document as follows in raven db.
class User
{
public String Id { get; set; }
public AccountType AccountType { get; set; }Servicetax
public String MainAccountMobileNo { get; set; }
public UserStatus Status { get; set; }
public String EmailId { get; set; }
public String DisplayName { get; set; }
public Object User { get; set; }
}
Here i am storing three different types of classes into the object User.
Say Customer,Retailer and Trader.
Customer
{
public String Name{ get; set; }
public String Address { get; set; }
public String MobileNo { get; set; }
public String EmailId { get; set; }
}
Retailer
{
public String Name{ get; set; }
public String Address { get; set; }
public String MobileNo { get; set; }
public String EmailId { get; set; }
}
Trader
{
public String Name{ get; set; }
public String Address { get; set; }
public String MobileNo { get; set; }
public String EmailId { get; set; }
}
Now is it possible to retrieve results based on the Customer's class detail?
That is now i want to retrieve All the Customers based on Address in the customer class, So how will i do it? How to typecast the object user in the query to type customer.
Thanks.
The user object in the document can store any type of class's object like account info trader in the above image. So how can i query from the object type that cannot is not definite and changing.
var Result = sess.Query<UserAccountInfo>().Where(x => x.AccountType == usertype && ((AccountInfoCustomer)x.User).Customerstatus == CustomerStatus.Pending);
This is the query that's been tried and this is the exception that's been caught
{"Url:
\"/indexes/dynamic/UserAccountInfos?query=AccountType%253ADistributor%2520AND%2520User).Customerstatus%253APending&start=0&pageSize=128&aggregation=None\"\r\n\r\nSystem.ArgumentException:
The field ')_Customerstatus' is not indexed, cannot query on fields
that are not indexed\r\n at
Raven.Database.Indexing.Index.IndexQueryOperation.AssertQueryDoesNotContainFieldsThatAreNotIndexes()

The problem here was the build of raven db. i was using the older build after changing it to newer version the query
var Result = sess.Query<UserAccountInfo>().Where(x => x.AccountType == usertype && ((AccountInfoCustomer)x.User).Customerstatus == CustomerStatus.Pending);
works fine.

Your classes are not very DRY. Consider this instead:
public abstract class Person
{
public string Name{ get; set; }
public string Address { get; set; }
public string MobileNumber { get; set; }
public string EmailAddress { get; set; }
}
public class Customer : Person {}
public class Retailer : Person {}
public class Trader : Person {}
Then, in your User class replace
public Object User { get; set; }
With this:
public Person Person { get; set; }
That way, you can store an instance of any of the 3 derived types. I wouldn't call the property User given that the containing class is called User and User.User could get confusing to anyone having to understand your code.

Related

Should I inject another context into SaveChangesInterceptor?

I want to perform auditing, by logging entity changes. I have an Audit class:
public class Audit
{
public int Id { get; set; }
public string UserId { get; set; }
public string Type { get; set; }
public string TableName { get; set; }
public DateTime DateTime { get; set; }
public string OldValues { get; set; }
public string NewValues { get; set; }
public string AffectedColumns { get; set; }
public string PrimaryKey { get; set; }
}
I've created AuditingInterceptor which I will add to multiple contexts. The Audits table is not accessible through these contexts.
internal class AuditingInterceptor : SaveChangesInterceptor
{
public override ValueTask<InterceptionResult<int>> SavingChangesAsync...
}
In order to save to the Audits table should I inject AuditContext that has access to Audits table or should I use another aproach?

Is it possible to link one table to another with entity framework core without FKs?

I have 2 tables company and user. Company will have one created by user and one modified user - these will be admin users. User will belong to one company but one admin user could create or modify multiple companies.
I'm having a hard time using entity framework core in my .net core app to join company and user so when I get a company record I have the created by and modified user information.
My company and user classes look like this:
public class Company
{
[Key]
public Guid Id { get; set; }
public DateTime Created { get; set; }
public Guid Created_By { get; set; }
public virtual ApplicationUser CreatedByUser { get; set; }
public DateTime Modified { get; set; }
public Guid Modified_By { get; set; }
public virtual ApplicationUser ModifiedByUser { get; set; }
public string Company_Name { get; set; }
}
public class ApplicationUser: IdentityUser<Guid>
{
[Column("ID")]
public override Guid Id { get; set; }
[Column("CREATED")]
public DateTime Created { get; set; }
[Column("CREATED_BY")]
public Guid? CreatedBy { get; set; }
[Column("MODIFIED")]
public DateTime Modified { get; set; }
[Column("MODIFIED_BY")]
public Guid? ModifiedBy { get; set; }
[Column("FIRST_NAME")]
public string FirstName { get; set; }
[Column("LAST_NAME")]
public string LastName { get; set; }
[Column("EMAIL")]
public override string Email { get; set; }
[Column("NORMALIZED_EMAIL")]
public override string NormalizedEmail { get; set; }
[Column("EMAIL_CONFIRMED")]
public override bool EmailConfirmed { get; set; }
[Column("USER_NAME")]
public override string UserName { get; set; }
[Column("NORMALIZED_USER_NAME")]
public override string NormalizedUserName { get; set; }
[Column("COMPANY_ID")]
public Guid CompanyId { get; set; }
[Column("PHONE_NUMBER")]
public override string PhoneNumber { get; set; }
[Column("PHONE_NUMBER_CONFIRMED")]
public override bool PhoneNumberConfirmed { get; set; }
[Column("TITLE")]
public string Title { get; set; }
[Column("ACTIVE")]
public bool Active { get; set; }
[Column("ROLE_ID")]
public int UserRoleId { get; set; }
[Column("TYPE_ID")]
public int TypeId { get; set; }
[Column("PASSWORD_HASH")]
public override string PasswordHash { get; set; }
[Column("SECURITY_STAMP")]
public override string SecurityStamp { get; set; }
[Column("CONCURRENCY_STAMP")]
public override string ConcurrencyStamp { get; set; }
[Column("TWO_FACTOR_ENABLED")]
public override bool TwoFactorEnabled { get; set; }
[Column("LOCKOUT_END")]
public override DateTimeOffset? LockoutEnd { get; set; }
[Column("LOCKOUT_ENABLED")]
public override bool LockoutEnabled { get; set; }
[Column("ACCESS_FAILED_COUNT")]
public override int AccessFailedCount { get; set; }
}
My DbContext class looks like this:
public class DbContext : IdentityDbContext<ApplicationUser, ApplicationRole, Guid, ApplicationUserClaim, ApplicationUserRole, IdentityUserLogin<Guid>, IdentityRoleClaim<Guid>, IdentityUserToken<Guid>>
{
public DbContext(DbContextOptions<DbContext> options) : base(options)
{
}
public virtual DbSet<Company> Companies { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Company>().ToTable("COMPANY").Property<Guid>("Created_By");
builder.Entity<Company>().HasOne(x => x.CreatedByUser).WithOne().HasForeignKey("Created_By");
builder.Entity<ApplicationUser>().ToTable("USER");
builder.Entity<ApplicationUser>().HasKey(x => x.Id);
builder.Entity<ApplicationUserClaim>().ToTable("USER_CLAIMS");
builder.Entity<ApplicationRole>().ToTable("IDENTITY_ROLES");
builder.Entity<IdentityUserRole<Guid>>().HasKey(p => new { p.UserId, p.RoleId });
builder.Entity<ApplicationUserRole>().ToTable("IDENTITY_USER_ROLES");
}
}
And I was trying to get companies like this:
public async Task<List<Company>> GetAllCompanies()
{
return await _locationDbContext.Companies.ToListAsync();
}
Currently I am getting this error:
System.InvalidOperationException: 'You are configuring a relationship between 'Company' and 'ApplicationUser' but have specified a foreign key on 'Created_By'. The foreign key must be defined on a type that is part of the relationship.'
Is there an easier way to do this? Really all I want is the username of the user that created of modified the company record? If I was doing this with just sql I would just use a basic Join but Im not sure how to do that with entity framework. Worst case I would just get all the companies and then loop through doing a select on the user table where ID = Created_By
As #IvanStoev noticed without FKs it is not possible. But you can still use EF to join 2 tables.
In your case you have to unmap user from company and maybe it is a good idea to make user Guid nullable:
public class Company
{
[Key]
public Guid Id { get; set; }
public string Company_Name { get; set; }
public DateTime Created { get; set; }
public DateTime Modified { get; set; }
public Guid Created_By { get; set; }
public Guid Modified_By { get; set; }
[NotMapped]
public ApplicationUser CreatedByUser { get; set; }
// or better
[NotMapped]
public string CreatedByUser { get; set; }
[NotMapped]
public ApplicationUser ModifiedByUser { get; set; }
//or better
[NotMapped]
public string ModifiedByUser { get; set; }
}
and remove
builder.Entity<Company>().HasOne(x => x.CreatedByUser).WithOne().HasForeignKey("Created_By");
you still can join them like this
var companies= (
from c in _locationDbContext.Companies
join uc in _locationDbContext.ApplicatonUser on c.Created_By equals uc.Id
join um in _locationDbContext.ApplicatonUser on c.Modified_By equals um.Id
select new Company
{
....
CreatedByUser = uc,
ModifiedByUser = um
// or usually
CreatedByUser = uc.FirstName + " " + uc.LastName,
ModifiedByUser = um.FirstName + " " + um.LastName,
}).ToList();
This is one way of accomplishing this.
This is a simplified version of class Company:
public class Company
{
public int CompanyID { get; set; }
public string Name { get; set; }
//
// Relations
public string CreatorID { get; set; }
public ApplicationUser Creator { get; set; }
public string LastModifiedByID { get; set; }
public ApplicationUser LastModifiedBy { get; set; }
}
CreatorID and LastModifiedByID will be used by EF for determining the FK's. These are the fields you have to work with when updating the Db, not Creator and LastModfiedBy, yet you can also do it but with more lines of code.
Now, you can add companies the usual way, this code searches for the company, and creates one if not found, just sample code, not serious, really:
var companyName = "My company";
var company = context.Companies.Include(c => c.Creator).Include(c => c.Creator).FirstOrDefault(c => c.Name == companyName);
if (company == null)
{
company = new Company
{
Name = "My company",
CreatorID = user.Id,
LastModifiedByID = user.Id
};
context.Companies.Add(company);
context.SaveChanges();
}
And retrieving the companies with all relations filled is a matter or using calls to Include() and ThenInclude(), like here:
var companies = context.Companies.Include(c => c.Creator).Include(c => c.Creator).ToList();
I omitted all filtering logic for the sake of simplicity.

Doubts about EF Core 2.1 Relations

I am working on Entity Framework Core Code First approach and ASP.Net Core 2.1 making 3 tables:
Person class
public class Person
{
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public PeopleProfessions PeopleProfessions { get; set; }
}
Professions' class
public class Profession
{
public string Id { get; set; }
public string Name{ get; set; }
public PeopleProfessions PeopleProfessions { get; set; }
}
peopleprofessions' class
public class peopleprofessions
{
[ForeignKey("PersonId ")]
public string PersonId { get; set; }
public ICollection<Person> People { get; set; }
[ForeignKey("ProfessionId")]
public string ProfessionId{ get; set; }
public ICollection<Profession> Professions { get; set; }
}
On my Context:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<peopleprofessions>().HasKey(up => new { up.PersonId, up.ProfessionId });
}
Bearing this in mind:
People can have multiple professions.
The professions table is only for reading stored data like "Accountant".
I have doubts about how I can make table 3 only contain the foreigners and that it can meet the needs that I just mentioned.
I have tried to make the relationship appropriately but I also noticed that in tables 1 and 2 it requests both Id of the table people's professions.
I don't know if I am lost or if I am looking wrong or if there is an alternative to that situation. Thanks for any help you can give me.
You have the use of Collections on the navigation items a bit backwards. For your primary entities (Person and Profession), they should have collections, since it's one-to-many. But for the PeopleProfessions, each record is a single link to a specific entity, so no collection there just a direct object reference.
public class Person
{
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<PeopleProfessions> PeopleProfessions { get; set; }
}
public class Profession
{
public string Id { get; set; }
public string Name{ get; set; }
public ICollection<PeopleProfessions> PeopleProfessions { get; set; }
}
public class PeopleProfessions
{
public string PersonId { get; set; }
public Person Person { get; set; }
public string ProfessionId { get; set; }
public Profession Profession { get; set; }
}
You can, but don't need to specify a ForeignKey attribute because you are following EFs naming conventions(it will figure it out for you). Your OnModelCreating looks correct for the composite key.
You may want to consider removing the plural from PeopleProfessions (just call the class PeopleProfession) since one instance represents a single People-Profession relationship. I typically do this and but the navigation name in the entities remains plural, since it can represent more than one, i.e.
public ICollection<PeopleProfession> PeopleProfessions { get; set; }

How do I extend IdentityUser class in DbFirst approach?

I have a table named User in my database. I also have a .net core project where authentication is built-in. I want to connect to my database and after scaffolding reveals my User class, I want it to inherit from IdentityUser.
After scaffolding went well, I tried to inherit from IdentityUser.
public partial class User :IdentityUser<int>
{
public int Id { get; set; }
public string Country { get; set; }
public string PersonalId { get; set; }
public string MobilePhone { get; set; }
public string Email { get; set; }
public DateTime BirthDate { get; set; }
public string Password { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public byte[] Idimage { get; set; }
public bool? EmailConfirmed { get; set; }
public bool? Smsconfirmed { get; set; }
}
I can not see Identity Fields like PasswordHash, PhoneNumberConfirmed and so on, in my database. Specifically, in User table.

How to model this correctly in Entity Framework?

I have a requirement that I am not sure how to accomplish, in my existing data I have a list of customers, each customer should be assigned a staffMember to work with them, so would this be a 1 to 1 relationship or a 1 to many relationship, having trouble wrapping my head around how to model the data, as I want to figure out how to model this correctly. Since a staff member can be assigned to many different customers How should I model this? Does this look correct?
What I would like is to have the form pull the list of staff members available from the staff table, when inputting a new customer, ideally by the name
which I figure I could probably do using linq..
public class Customer
{
public int CustomerId { get; set; }
public string Name { get; set; }
public string BusinessName { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public DateTime RequestDate { get; set; }
public Staff Staff { get; set; }
public List<CustomerJob> CustomerJobs { get; set; }
}
public class Staff
{
public int ID { get; set; }
public string Name { get; set; }
public string Phone { get; set; }
public string EMail { get; set; }
public int CustomerId { get; set; }
}
Customer have exactly 1 Staff while a single Staff maybe assigned to more than 1 Customer. So this is a one-to-many relation.
It is better that Customer be aware of its Staff. It could be called AssignedStaff. Staff itslef does not need to have a property to show all its Csutomers. Tough you can extract Customer list of a Staff using a simple query.
My recommended class structure is as follow:
public class Customer
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string BusinessName { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public DateTime RequestDate { get; set; }
public Staff AssignedStaff { get; set; }
public List<CustomerJob> CustomerJobs { get; set; }
}
public class Staff
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Phone { get; set; }
public string EMail { get; set; }
}
A query for extracting Customer list of a Staff:
var customers = _dbContext.Customers.Where(x => x.AssignedStaff.Id == staffId);