My fluent mappings are:
HasOne(x => x.User).ForeignKey("fOM_User_Id").Cascade.None();
My class is:
public class OrganizationMember
{
public virtual int MemberId
{
get;
set;
}
public virtual int OrganizationId
{
get;
set;
}
public virtual int UserId
{
get;
set;
}
public virtual User User { get; set; }
public virtual string Permissions
{
get;
set;
}
public virtual System.DateTime DateAdded
{
get;
set;
}
}
I run the query:
var assignableusers = _session.Query<OrganizationMember>().Where(x => x.OrganizationId == CurrentProject.OwnerID)
.Select(x => x.User.Login);
which generated:
select user1_.fU_Login as col_0_0_ from tOrganization_Members organizati0_
left outer join tUsers user1_ on **organizati0_.fOM_Id**=user1_.fU_UserID
where organizati0_.fOM_Organization_Id=7
but it should have been:
select user1_.fU_Login as col_0_0_ from tOrganization_Members organizati0_
left outer join tUsers user1_ on **organizati0_.fOM_User_Id** = user1_.fU_UserID
where organizati0_.fOM_Organization_Id = 7
How can I fix this in my mappings?
from organizati0_.fOM_Id to organizati0_.fOM_User_Id
I don't think your relationship is a true one to one.
http://www.jagregory.com/writings/i-think-you-mean-a-many-to-one-sir/
You should probably use a References (many-to-one) instead.
References(x => x.User).Cascade.None();
Related
I have a join table UserSystem which is:
public class UserSystem
{
public int UserSystemId { get; set; }
public int SystemId { get; set; }
public virtual System System { get; set; }
public int UserId { get; set; }
public virtual User User { get; set; }
public DateTime AddedDate { get; set; }
public int AddedByUserId { get; set; }
public bool Deleted { get; set; }
public DateTime? DeletedDate { get; set; }
public int? DeletedByUserId { get; set; }
}
And a configuration
public void Configure(EntityTypeBuilder<UserSystem> builder)
{
builder.ToTable("UserSystem", DbSchemas.Dbo);
builder.HasKey(e => e.UserSystemId);
builder.HasOne(bc => bc.User).WithMany(b => b.UserSystems).HasForeignKey(bc => bc.UserId);
builder.HasOne(bc => bc.System).WithMany().HasForeignKey(bc => bc.SystemId);
}
Now I want to be able to add new UserSystem with same UserId and SystemId, but only when same data not exists with Deleted == 0. If for example UserId = 1 and SystemId = 1 are in database, but it has Deleted = 1 then I want to be able to add same record again with Deleted = 0, but not being able to add same data when any record with that data has Deleted = 0. How can I achieve it in FluentApi?
I have a SQL statement below that needs to convert it to NHibrnate QueryOver. I have searched the web but can't find a concrete solution. Anyone can help me on this?
SELECT
TABEL1.Id,
CASE WHEN EXISTS (SELECT Id FROM TABLE2 WHERE TABLE2.ID = TABLE1.ID)
THEN 'TRUE'
ELSE 'FALSE'
END AS NewFiled
FROM TABLE1
--Here is the real POCO
public class UserRole
{
[Required]
public virtual User User { get; set; }
[Required]
public virtual Role Role { get; set; }
}
public class UserTenant
{
[Required]
public virtual Tenant Tenant { get; set; }
[Required]
public virtual User User { get; set; }
}
public class Role
{
public int Id {get;set}
[StringLength(255), Required]
public virtual string RoleLabel { get; set; }
[StringLength(4000), Required]
public virtual string RoleDescription { get; set; }
}
public class User
{
public int Id {get;set}
[StringLength(255), Required]
public virtual string Firstname { get; set; }
[StringLength(255), Required]
public virtual string Lastname { get; set; }
}
public class Tenant
{
public int Id {get;set}
[StringLength(255), Required]
public virtual string Name { get; set; }
[StringLength(4000), Required]
public virtual string Description { get; set; }
}
public class AssignRoleUsersModel
{
public virtual int UserId { get; set; }
public virtual string LastName { get; set; }
public virtual string FirstName { get; set; }
public virtual bool IsAssigned { get; set; }
}
--This is the method to get the users assigned or not for that particular role.
public RoleUsers GetRoleUsers(int Id)
{
UserRole userRolesAlias = null;
UserTenant userTenantsAlias = null;
-- This query will get the role depending the Id that have passed. Take note that I don't want to use this query that is why I am converting it to queryOver.
var role = (from r in RoleRepository.Queryable()
where r.Id == Id
select r).FirstOrDefault();
--This query will get all the users having the role result above.Take note that I don't want to use this query that is why I am converting it to queryOver.
var assignedUsers = UserRoleRepository.Queryable().Where(x => x.Role.Id == role.Id).Select(a => a.User.Id).ToArray();
--This is the condition to know if the user was assigned to the role
var projection = Projections.Conditional(Restrictions.Where(() => userRolesAlias.User.Id.IsIn(assignedUsers))
, Projections.Constant(true)
, Projections.Constant(false)
);
var users =
new List<AssignRoleUsersModel>(UnitOfWorkLocalData.CurrentUnitOfWork.Session.QueryOver(() => userTenantsAlias)
.Select(x => x.User.Id)
.Select(x => x.User.LastName)
.Select(x => x.User.FirstName)
.Select(x => x.User.UserName)
.Select(projection)
.TransformUsing(Transformers.AliasToBean<AssignRoleUsersModel>())
.List<AssignRoleUsersModel>());
}
EXTENDED based on the extended question, the way how to do CASE WHEN:
// the ID of searched role, coming as parameter id
int searchedRolId = ...
UserRole userRolesAlias = null;
// this is the SUBQUERY we need, the inner select
var subquery = QueryOver
.Of<UserRole>(() => userRolesAlias)
.Where(() => userRolesAlias.Role.Id == searchedRolId)
.Select(x => userRolesAlias.User.Id);
// here we use NHibernate built in Subqueries tools
var projection = Projections.Conditional(
Subqueries.Exists(subquery.DetachedCriteria) // this is the SUB-SELECT
, Projections.Constant(true)
, Projections.Constant(false)
);
ORIGINAL - This could look like this:
var projection = Projections.Conditional(
NHibernate.Criterion.Expression
.Sql("EXISTS (SELECT Id FROM TABLE2 WHERE TABLE2.ID = {alieas}.ID)")
, Projections.Constant(true)
, Projections.Constant(false)
);
var query = session.QueryOver<Table1>();
ResultDto result = null;
var list = query.SelectList(l => l
.Select(x => x.ID).WithAlias(() => result.ID)
.Select(projection).WithAlias(() => result.DoesExist)
)
.TransformUsing(Transformers.AliasToBean<ResultDto>())
.List<ResultDto>();
The list here contains the set of ResultDtos, with ID and decision if the subtabel TABLE2 record exists. The {alias} will be replaced by NHibernate with the alias of the outert table...
Where this is our DTO for projections:
public class ResultDto
{
public virtual int ID { get; set; }
public virtual bool DoesExist { get; set; }
}
How would I be able to Map an object inside of another with two columns that arent keys?
public class Car
{
public virtual int Id { get; set; }
public virtual int AccountId { get; set; }
}
public class UserAccount
{
public virtual int Id { get; set; }
public virtual int UserId{ get; set; }
public virtual int AccountId { get; set; }
}
public class User
{
public virtual int Id { get; set; }
public virtual int Name { get; set; }
}
Lets say I wanted to get all Cars with a User.Name of "joe". How would I map / query these with fluent nhibernate?
public Car()
{
Table("Car");
Id(x => x.Id).Column("ID").GeneratedBy.Native();
Map(x => x.AccountId);
References(x => x.Account); // ?? needs to map accountid with the Account.Id...
}
If you want to map a reference, you need the type, not the key.
public class Car
{
public virtual int Id { get; set; }
public virtual UserAccount Account { get; set; }
}
Then you would map it like this
public Car()
{
Table("Car");
Id(x => x.Id).Column("ID").GeneratedBy.Native();
References(x => x.Account, "AccountId");
}
I have the following Domain Model(s):
public class WriteOffApprovalUser
{
public virtual string UserName { get; set; }
public virtual Employee Employee { get; set; }
}
public class 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 UserId { get; set; }
public virtual string SupervisorID { get; set; }
}
These are my Fluent Mappings
public class WriteOffApprovalUserMap : ClassMap<WriteOffApprovalUser>
{
public WriteOffApprovalUserMap()
{
//Schema("LEGAL");
Table("WRITEOFF_APPROVAL_USER");
Id(x => x.UserName).Column("USER_NAME");
HasOne(x => x.Employee).PropertyRef("UserId");
}
}
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
// Table Name
//Schema("ADP_FEED_OWNER");
Table("ADP_EMPLOYEE");
// Primary Key
Id(x => x.EmployeeID).Column("EMPLID");
// Mappings
Map(x => x.UserId).Column("USER_ID");
Map(x => x.FirstName).Column("FIRST_NAME");
Map(x => x.LastName).Column("LAST_NAME");
Map(x => x.PreferredName).Column("PREFERRED_NAME");
}
}
Here is my Query:
var results = new Repository<WriteOffApprovalUser>(session)
.Query()
.ToList();
This is the SQL it is generating, and I was expecting a JOIN instead.
select writeoffap0_.USER_NAME as USER1_1_ from WRITEOFF_APPROVAL_USER writeoffap0_
SELECT employee0_.EMPLID as EMPLID0_0_, employee0_.USER_ID as USER2_0_0_, employee0_.FIRST_NAME as FIRST3_0_0_, employee0_.LAST_NAME as LAST4_0_0_, employee0_.PREFERRED_NAME as PREFERRED5_0_0_ FROM ADP_EMPLOYEE employee0_ WHERE employee0_.EMPLID=:p0;
SELECT employee0_.EMPLID as EMPLID0_0_, employee0_.USER_ID as USER2_0_0_, employee0_.FIRST_NAME as FIRST3_0_0_, employee0_.LAST_NAME as LAST4_0_0_, employee0_.PREFERRED_NAME as PREFERRED5_0_0_ FROM ADP_EMPLOYEE employee0_ WHERE employee0_.EMPLID=:p0;
SELECT employee0_.EMPLID as EMPLID0_0_, employee0_.USER_ID as USER2_0_0_, employee0_.FIRST_NAME as FIRST3_0_0_, employee0_.LAST_NAME as LAST4_0_0_, employee0_.PREFERRED_NAME as PREFERRED5_0_0_ FROM ADP_EMPLOYEE employee0_ WHERE employee0_.EMPLID=:p0;
SELECT employee0_.EMPLID as EMPLID0_0_, employee0_.USER_ID as USER2_0_0_, employee0_.FIRST_NAME as FIRST3_0_0_, employee0_.LAST_NAME as LAST4_0_0_, employee0_.PREFERRED_NAME as PREFERRED5_0_0_ FROM ADP_EMPLOYEE employee0_ WHERE employee0_.EMPLID=:p0;
Now there are four rows in the database, and the right data is coming back, but I would not expect five separate SQL statements to do this.
You need to eager load/fetch the Employee entity to avoid the behavior that you are seeing which is often referred to as the SELECT N+1 problem. In order to do this you have two options:
Option 1. Eager load in the mapping meaning when you query the WriteOffApprovalUser entity, it will always perform a JOIN to the Employee table. Note: This may sound like what you want but be cautious as you will force all developers who ever work with this entity to be stuck with this design decision until the end of time. You'll have to ask yourself, would I ever want to query the WriteOffApprovalUser table and not perform a JOIN to the Employee table. If the answer is yes, then don't force the eager loading in the mapping file.
To have the Employee automatically fetched, change your HasOne code in the mapping to look something like this:
HasOne(x => x.Employee).PropertyRef("UserId").Not.LazyLoad().Fetch.Join();
Option 2. Perform the eager loading in the query. I noticed that you are using some kind of Repository of T pattern so you may have to modify it to handle eager loading. Typical eager loading using NHibernate's built in LINQ Query<T> class in the NHibernate.Linq namespace looks something like this:
var results = new session.Query<WriteOffApprovalUser>()
.Fetch( x => x.Employee ) // This will tell NHibernate to perform a JOIN to the Employee table
.ToList();
I have the following classes:
public class Item
{
public int Id { get; set; }
public IDictionary<int, ItemLocal> { get; protected set; }
public ICollection<string> Tags { get; set; }
public int DefaultLanguageId { get; set; }
public DateTime Start { get; set; }
}
public class ItemLocal
{
public virtual Item Parent { get; set; }
public virtual int LanguageId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
}
These map to the tables Item, ItemTag and ItemLocal. I want to make the following query via the criteria api:
select
i.Id,
i.Start,
l.Title
from
Item i
left join ItemLocal l on i.Id = l.ParentId and i.DefaultLangaugeId = l.LanguageId
order by
l.Title,
i.Id
But I dont know how to perform the left join with the nhibernate criteria api. Especially with the usage of the default language selection.
Any help is very appreciated.
I have found a solution:
Session
.CreateCriteria<Item>("i")
.CreateCriteria("Localization", "l", NHibernate.SqlCommand.JoinType.LeftOuterJoin)
.Add(Restrictions.Disjunction()
.Add(Restrictions.EqProperty("i.DefaultLanguageId", "l.LanguageId"))
.Add(Restrictions.IsNull("l.LanguageId"))
)
.AddOrder(Order.Asc("l.Title"))
.AddOrder(Order.Asc("w.Id"));
This seems to work but results in a query with a workaround WHERE clause. Would rather see that it could render a SQL query where the restrictions defined in the disjunction could be part of the OUTER LEFT JOIN ... ON ... statement.