NHibernate Relationships in Oracle - nhibernate

After countless hours of trying to make Entity Framework work with Oracle, I have given up and started down the path of NHibernate.
I am struggling with the lingo a bit, and have a question. Given the following classes, how do I make NHibernate (Fluent Mappings) output something similar to the SQL below using the WriteOffApprovalUser.UserName as the key to the Employee.MailID field.
C# Classes
public class WriteOffApprovalUser : EntityBase<WriteOffApprovalUser>
{
public virtual string UserName { get; set; }
public virtual Employee.Employee Employee { get; set; }
}
public class Employee : EntityBase<Employee>
{
public virtual string EmployeeID { get; set; }
public virtual string EmployeeStatusCode { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual string PreferredName { get; set; }
public virtual string JobTitle { get; set; }
public virtual string Division { get; set; }
public virtual string Department { get; set; }
public virtual string Location { get; set; }
public virtual string City { get; set; }
public virtual string DeskLocation { get; set; }
public virtual string MailID { get; set; }
public virtual string Phone { get; set; }
public virtual string Fax { get; set; }
public virtual string SecCode { get; set; }
public virtual string SupervisorID { get; set; }
}
SQL
SELECT c.user_name,
a.LAST_NAME
|| ', '
|| DECODE (a.PREFERRED_NAME, ' ', a.FIRST_NAME, a.preferred_name)
writeoff_approval_name
FROM writeoff_approval_user c, adp_employee a
WHERE c.USER_NAME = a.USER_ID AND c.exp_date IS NULL
ORDER BY 2

In NHibernate all is about mapping the right way. If you're using fluent you should have defined a reference in WriteOffApprovalUser to an Employee entity. Like in this basic tutorial
public class WriteOffApprovalUserMap : ClassMap<WriteOffApprovalUser>
{
public WriteOffApprovalUserMap()
{
Id(x => x.UserName);
References(x => x.Employee);
}
}
Then all you need to do is a simple query like
List<Employee> employees = session.Where(e => e.exp_date == null);
I haven't seen mapped any Date but I this easy to fix.
Then to have the DECODE feature I will suggest you to do some DDD so instead of simply doing an anemic class for Employee create a property that returns the composed user name.
class Employee
{
public string ComposedName
{
get {
return this.LastName + string.IsNullOrEmpty(this.preferedName) ?
this.FirstName : this.PreferedName;
}
}
}
To me that should be treat as a calculation and there is no need to do it in the SQL query. As a bonus this code can be Unit tested.

Related

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; }

Ouerying an object inside ravendb document

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.

Fluent Nhibernate - One to Many Mapping - Child of same type as Parent

I have a class defined as:
public class ReportClient
{
public virtual int? Id { get; set; }
public virtual long? ClientId { get; set; }
public virtual string Name { get; set; }
public virtual string EmailAddress { get; set; }
public virtual string AdditionalEmailAddress { get; set; }
public virtual List<ReportClient> ChildClients { get; set; }
}
As you can see ChildClients are of same type as Parent.
Please guide me how can I map 'ChildClients' so for each ChildClient in List<ReportClient> ChildClients there is a new table record with a column 'ParentId' being set for this record ( having ParentId = Id)
Please guide.
Thank you!
I don't have the enviroment to test, but this should work, try swapping the column names if it doesn't.
HasManyToMany(x => x.ChildClients)
.ParentKeyColumn("ParentId")
.ChildKeyColumn("Id")

Fluent NHibernate mappings for localization

I am trying to build a Database from NHibernate mappings and have run into a problem.
I have many classes with localized string values:
public class MyClass1 {
public virtual int Id { get; set; }
public virtual ShortString Name { get; set; }
public virtual LongString Description { get; set; }
}
public class MyClass2 {
public virtual int Id { get; set; }
public virtual ShortString Name { get; set; }
public virtual LongString Description { get; set; }
}
and Languages like
public class Language {
public virtual string Code { get; set }
public virtual string Name { get; set }
}
My ShortString and LongString classes both look the same:
public class ShortString {
public virtual int Id { get; set; }
public virtual IDictionary<Language, string> Values { get; set; }
}
What I want to achieve are two tables (ShortString and LongString) looking like this:
TABLE ShortString
-----------------
Id (int)
LanguageCode (nvarchar(8))
Value (nvarchar(256)) (or ntext for the LongString Table)
...with Id AND LanguageCode as primary keys and a ForeignKey to the Language Table.
And in the MyClass1 and MyClass2 tables, I want to have NameId (int) and DescriptionId (int) columns mapped to ShortString and LongString tables respectively.
I am totally stuck. How can I achieve this?
Maybe you could ditch short and long string altogether
public class MyClass1 {
public virtual int Id { get; set; }
public virtual IDictionary<Language, string> Name { get; set; }
public virtual IDictionary<Language, string> Description { get; set; }
}
public class MyClass2 {
public virtual int Id { get; set; }
public virtual IDictionary<Language, string> Name { get; set; }
public virtual IDictionary<Language, string> Description { get; set; }
}
and use the folling Mapping
public class MyClass1Map : ClassMap<MyClass1>
{
public MyClass1Map()
{
[...]
HasMany(mc => mc.Name)
.Table("ShortString")
.KeyColumn("id")
.AsEntityMap("language_id")
.Element("value")
HasMany(mc => mc.Description)
.Table("LongString")
.KeyColumn("id")
.AsEntityMap("language_id")
.Element("value", e => e.Length(1000))
}
}
I cant test it right now so there might be tweaking nessesary