Reputation: 174457
Short version:
Is it possible in NHibernate to create a mapping for a class that has no corresponding table in the database and specify for each property where the data should come from?
Long version:
I have the following class:
public class TaxAuditorSettings
{
private readonly IList<Month> _allowedMonths = new List<Month>();
private readonly IList<Company> _allowedVgs = new List<Company>();
public IList<Month> AllowedMonths
{
get { return _allowedMonths; }
}
public IList<Company> AllowedVgs
{
get { return _allowedVgs; }
}
}
The class Company
is a normal entity that is mapped to a table.
The class Month
is a simple class without ID or existing mapping (Constructor and error checking removed for brevity):
public class Month
{
public int MonthNumber { get; set; }
public int Year { get; set; }
}
My database has the following two tables:
Table TAX_AUDITOR_ALLOWED_COMPANIES
has only one column COMPANY_ID
that is a FK to the table COMPANY
and has a UNIQUE index.
Table TAX_AUDITOR_ALLOWED_MONTHS
has two columns MONTH_NUMBER
and YEAR
. There is a UNIQUE index spanning both columns.
I would like to map TaxAuditorSettings
such that I can ask my NHibernate session for an object of this type and NHibernate then should put the contents of TAX_AUDITOR_ALLOWED_MONTHS
into the list TaxAuditorSettings.AllowedMonths
and the companies referenced in TAX_AUDITOR_ALLOWED_COMPANIES
into the list TaxAuditorSettings.AllowedCompanies
.
Is this even possible? If so, how? If not, how would you do it instead?
Please note: I can change the database if necessary.
Upvotes: 2
Views: 205
Reputation: 30813
not quite what you requested for but here goes
public TaxAuditorSettings GetAuditorSettings(ISession session)
{
// assuming there is a ctor taking the enumerables as parameter
return new TaxAuditorSettings(
session.CreateSQLQuery("SELECT MONTH_NUMBER, YEAR FROM TAX_AUDITOR_ALLOWED_MONTHS")
.SetResultTransformer(new MonthResultTransformer())
.Future<Month>(),
session.CreateCriteria<Company>()
.Add(NHibernate.Criterion.Expression.Sql("Id IN (SELECT COMPANY_ID FROM TAX_AUDITOR_ALLOWED_COMPANIES)"))
.Future<Company>())
}
class MonthResultTransformer : IResultTransformer
{
public IList TransformList(IList collection)
{
return collection;
}
public object TransformTuple(object[] tuple, string[] aliases)
{
return new Month
{
MonthNumber = (int)tuple[0],
Year = (int)tuple[1],
}
}
}
Update: saving
public void SaveOrUpdate(ISession session, TaxAuditorSettings settings)
{
using (var tx = session.BeginTransaction())
{
// get whats in the database first because we dont have change tracking
var enabledIds = session
.CreateSqlQuery("SELECT * FROM TAX_AUDITOR_ALLOWED_COMPANIES")
.Future<int>();
var savedMonths = session
.CreateSQLQuery("SELECT MONTH_NUMBER, YEAR FROM TAX_AUDITOR_ALLOWED_MONTHS")
.SetResultTransformer(new MonthResultTransformer())
.Future<Month>();
foreach (var id in settings.AllowedVgs.Except(enabledIds))
{
session.CreateSqlQuery("INSERT INTO TAX_AUDITOR_ALLOWED_COMPANIES Values (:Id)")
.SetParameter("id", id).ExecuteUpdate();
}
foreach (var month in settings.AllowedMonths.Except(savedMonths))
{
session.CreateSqlQuery("INSERT INTO TAX_AUDITOR_ALLOWED_MONTHS Values (:number, :year)")
.SetParameter("number", month.Number)
.SetParameter("year", month.Year)
.ExecuteUpdate();
}
tx.Commit();
}
}
Note: if you can change the database it would be much easier and performant to sanitise the tables
Upvotes: 2
Reputation: 1735
I would do it this way.
public class MonthMap : ClassMap<Month>{
public MonthMap(){
CompositeId()
.KeyProperty(x=>x.MonthNumber,"MONTH_NUMBER")
.KeyProperty(x=>x.Year);
Table("TAX_AUDITOR_ALLOWED_MONTHS");
}
}
Add a column to the COMPANY
table called TaxAuditable
and map it to a bool property. Update the column to be 1 where a matching row is found in TAX_AUDITOR_ALLOWED_COMPANIES
. Then remove the table TAX_AUDITOR_ALLOWED_COMPANIES
as it serves no real purpose.
Now you have a Company with an appropriate property on it you can query Company's where TaxAuditable
is true
and pass them into a method along with the Months
to do your work/calculations etc... Something like this perhaps?
var taxAuditableCompanies = session.QueryOver<Company>()
.Where(x=>x.TaxAuditable==true).Future();
var months=session.QueryOver<Month>().Future();
var myService = new MyService();
myService.DoSomeWork(taxAuditableCompanies, months);
Upvotes: 2