I want to map a class that result in a left outer join and not in an innner join.
My composite user entity is made by one table ("aspnet_users") and an some optional properties in a second table (like FullName in "users").
public class UserMap : ClassMap<User> {
public UserMap() {
Table("aspnet_Users");
Id(x => x.Id, "UserId").GeneratedBy.Guid();
Map(x => x.UserName, "UserName");
Map(x => x.LoweredUserName, "LoweredUserName");
Join("Users",mm=>
{
mm.Map(xx => xx.FullName);
});
}
}
this mapping result in an inner join select so no result come out is second table as no data. I'd like to generate an left join.
Is this possible only at query level?
Try the Optional() method.
Join("Users", m =>
{
m.Optional();
m.Map(x => x.FullName);
});
Only this did work for me (NH 3.3):
Join("OuterJoinTable",
m =>
{
m.Fetch.Join().Optional();
m.KeyColumn("ForeignKeyColumn");
m.Map(t => t.Field, "FieldName");
});
Related
I have a One to One relation between a TimeRecord and the Location.
This implementation is exactly the same es described in documentation:
https://github.com/jagregory/fluent-nhibernate/wiki/Fluent-mapping
public class TimeRecordMap : ClassMap<TimeRecord>
{
public TimeRecordMap()
{
Id(x => x.Id);
Map(x => x.Description);
Map(x => x.StartTime);
Map(x => x.EndTime);
HasOne(x => x.Location).Cascade.All();
}
}
public class LocationMap : ClassMap<Location>
{
public LocationMap()
{
Id(x => x.Id);
Map(x => x.Longitude);
Map(x => x.Latitude);
Map(x => x.Adress);
References(x => x.TimeRecord).Unique();
}
}
Now I query my TimeRecords with the following method:
public IList<TimeRecord> GetTimeRecords(string userid)
{
var query = Session.Query<TimeRecord>().Where(tr => tr.User.Id == userid);
return query.ToList();
}
Unfortunalelty my Location object is always null even if there is a coresponding entry in Location table but when I query for the coresponding Location with the desired TimeRecordId it is returned correctly.
See code here (code is inside a loop -> trCurrent is the current object in list received from "GetTimeRecords")
Location location = _locationRepo.getLocationByTimeRecordId(trCurrent.Id);
//trCurrent.Location = location; <- don't want to do it that way
if (trCurrent.Location != null)<- always null
{
... do stuff here
}
Implementation of my LocationRepository method:
public Location getLocationByTimeRecordId(int timeId)
{
var query = Session.Query<Location>()
.Where(tr => tr.TimeRecord.Id == timeId && tr.IsDeleted == false);
List<Location> lstReturn = query.ToList();
if (lstReturn.Count() == 0)
{
return null;
}
else
{
return lstReturn.First();
}
}
Can someone tell me why my Location is not resolved corretly?
Cheers,
Stefan
People claim that
HasOne / one-to-one is usually reserved for a special case. Generally, you'd use a References / many-to-one relationship in most situations (see: I think you mean a many-to-one). If you really do want a one-to-one, then you can use the HasOne method.
If you really do want a one-to-one and use it, you should remember that entities are joined by their ids by default.
If you check generated SQL you'll see something like JOIN Location ON Location.Id = TimeRecord.Id.
In order to get SQL like JOIN Location ON Location.TimeRecordId = TimeRecord.Id you should specify the foreign key via PropertyRef() method. So your mapping could be the folloving:
public class TimeRecordMap : ClassMap<TimeRecord>
{
public TimeRecordMap()
{
Id(x => x.Id);
Map(x => x.Description);
Map(x => x.StartTime);
Map(x => x.EndTime);
HasOne(x => x.Location).Cascade.All().PropertyRef(it => it.TimeRecord);
}
}
public class LocationMap : ClassMap<Location>
{
public LocationMap()
{
Id(x => x.Id);
Map(x => x.Longitude);
Map(x => x.Latitude);
Map(x => x.Adress);
References(x => x.TimeRecord/*, "TimeRecordId"*/).Unique().Not.Nullable();
}
}
In order to make sure that any location has TimeRecord you can add .Not.Nullable() into your LocationMap class.
I currently have this code.
tableB = new TableB
{
TableA = tableA,
};
List<TableC> tableC = locations.Select(location => new TableC
{
TableB = tableB
}).ToList();
tableB.TableC = tableC;
tableA.TableB.Add(tableB);
nhibernate.Create(a);
nhibernate.Commit();
The above code works but I find it kinda weird that I have do it like this.
I would like to do something like
tableB = new TableB
{
TableA = tableA,
TableC = MakeAllTableCs()
};
tableA.TableB.Add(tableB);
nhibernate.Create(a);
nhibernate.Commit();
The collection of tableC's is being made in memory and and when I try to do a create I get
not-null property references a null or transient value
It seems that it want's a reference to TableB for each one in the collection of TableC. It seems kinda odd to do this seeing that I am sticking in the TableB object. I would have hoped that it would have figured that out and used it as a reference.
Is there anyway that I can do it so I don't need to have a reference of TableB in each of my TableC objects?
Edit Mapping
public class TableAMap : ClassMap<TableA>
{
public TableAMap()
{
Id(x => x.Id).GeneratedBy.GuidComb();
HasMany(x => x.).Cascade.All().Inverse();
}
}
public class TableBMap : ClassMap<TableB>
{
public TableBMap ()
{
Id(x => x.Id).GeneratedBy.GuidComb();
References(x => x.TableA).Not.Nullable();
HasMany(x => x.TableC).Cascade.All().Inverse();
}
}
public class TableCMap : ClassMap<TableC>
{
public TableCMap ()
{
Id(x => x.Id).GeneratedBy.GuidComb();
References(x => x.TableB).Not.Nullable();
}
}
Hard to say without your mappings and real NHibernate code (where you call session.Save()) but most likely you are not saving the new TableB objects and also don't have any cascade setting on TableA.b (you need one of those).
I have an entity that is defined in two tables with fluent nhibernate
Table one:
Employee
---------
Id,
Name
Table Two:
Salaries
--------
Employee_Id,
Salary
On Fluent NHibernate I defined it like this:
EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Table("Employee");
Map(x => x.Id);
Map (x => x.Name);
Join ("Salaried", m =>
{
m.Map (x => map.Salary);
m.KeyColumn("EmployeeId");
});
}
}
When I do a Session.Get like the following:
Employee e = session.Get<Employee>(employeeId);
Then I got all the details of Employee, except the columns coming from the "Salaries" table
Any idea?
I'd suggest that you use the HasOne method like so:
EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Table("Employee");
Map(x => x.Id);
Map (x => x.Name);
HasOne(x=>x.Salary).PropertyRef(r=>r.EmployeeId);
}
}
The Employee is being lazy loaded so NHibernate will only call out to the database if you access the Salary property.
The table and KeyColumn names are wrong in the join mapping. Should be:
Join ("Salaries", m =>
{
m.Map (x => map.Salary);
m.KeyColumn("Employee_Id");
});
i have the following model:
public class FlatMap : ClassMap<Flat>
{
public FlatMap()
{
Id(m => m.FlatID).GeneratedBy.Identity();
Map(m => m.Name);
Map(m => m.Notes);
Map(m => m.Released);
}
}
public class BuildingMap : ClassMap<Building>
{
public BuildingMap()
{
Id(i => i.BuildingID).GeneratedBy.Identity();
Map(m => m.Name);
HasMany<Flat>(m => m.Flats).Cascade.All().KeyColumn("BuildingID").Not.LazyLoad();
}
}
public class ContractMap : ClassMap<Contract>
{
public ContractMap()
{
Id(m => m.ContractID).GeneratedBy.Identity();
Map(m => m.Amount);
Map(m => m.BeginIn);
Map(m => m.EndIn);
References(m => m.RentedFlat);
}
}
how can i make the following query using fluent nhibernate ?
Select * From Contract
Inner Join Flat On Contract.RentedFlatID = Flat.ID
Inner Join Building On Building.BuildingID = Flat.BuildingID
Where Building.BuildingID = #p0
especially there no reference from Flat to Building?? and i don't want it to be !
of course the reference i am talking about in order to be able to do something like this
var criteria = session.CreateCriteria<Contract>().CreateCriteria ("RentedFlat").CreateCriteria ("Building"/*there is no such property in Flat class*/);
i solved a problem, but not the way as i think is good.
but i will make this as an answer until someone provide me a better solution.
i add a property BuildingID to the Flat class, and modified the mapping class to :
Map(m => m.BuildingID);
now i can do the following query:
criteria.CreateCriteria("RentedFlat")
.Add(Restrictions.Eq("BuildingID", selectedBuilding.BuildingID));
I am trying to create a map to get results as like from below query.
I am having hard time to get set Product mapping to set References to Product_Line on 3 columns as in where condition. How can I achieve this using fluent?
Product table: cId, ProjID, Line, etc., columns
Product_Line table: cId, ProjID, Line, etc., columns
select f.* from Product f
join Product_Line v on f.cId = v.CId and f.ProjID = v.ProjID and f.line = v.line
Thanks in Advance.
RajeshC
First, thank you for looking into it and Here with more info:
//Req: I want to query product such that if there is NO ProductLine, then I want to create a ProductLine, if there is one, then I'll update it.
public class ProductMap : ClassMap<Product>
{
Id(x => x.Id);
Map(x => x.CustomerId, "CustId");
Map(x => x.ProjId, "PROJId");
Map(x => x.LineNumber, "LineNumber");
Map(x => x.ReportType, "ReportType");
// Reference to Product_Line? - this reference should be based on three columns (custId, ProjId, LineNumber)
References(x => x.Line);
}
public class ProductLineMap : ClassMap<ProductLine>
{
Table("Product_Line");
Map(x => x.CustomerId, "CustId"); //same column as Product.CustId
Map(x => x.ProjId, "PROJId"); //Same as Product.ProjId
Map(x => x.LineNumber, "LINENUMBER"); //Same as Product.LineNumber
//etc.,
//for me, this reference is not needed as I need from Product to ProductLine - one way.
//References(x => x.Product).Column("ProjId") //
}
We could give you a much better answer if you showed us your C# code and wrapped the SQL in < code > tags... Here's my guess at what I think you want:
public class ProductMap : ClassMap<Product>
{
Id(x => x.Id);
References(x => x.Line); // Reference to Product_Line?
// etc.
}
public class ProductLineMap : ClassMap<ProductLine>
{
Table("Product_Line");
Id(x => x.Id).Column("cId");
References(x => x.Product).Column("ProjId")
}