I have flat view which I should map to 2 separate C# models
class Container
{
public virtual int Id {get;set;}
public virtual string Title{get;set;}
public virtual List<Item> Items{get;set;}
}
class Item
{
public virtual string Code {get;set;}
public virtual string Name {get;set;}
}
View contains next columns:
ContainerId | Title | Code | Name
1 | 'title1' | 'code1' | 'name1'
1 | 'title1' | 'code2' | 'name2'
2 | 'title2' | 'code1' | 'name1'
...
How can I do this, to have an ability to make following queries:
session.Get<Container>(1)
=> {Id = 1, Title = "title1", Items = [{Code="code1", Name = "name1"}, {Code="code2", Name= "name2"} ]}
I should somehow map ContainerId as Id, but inside view it is not unique because it is denormalized to contain multiple . Also I should Map some Id inside 'Item' mapping - Item.Code can be used for this.
Thanks!
If the two entities you describe are represented in the database as two separate tables and you also have a view that is basically a combination of these two tables I would map them as such. So you have your entity maps for both Container and Item and you have an additional "entity" (I call them view objects in my DAL) for the view itself.
Typically I will map database views like this:
public class ContainerItemViewMap : ClassMap<ContainerItem>
{
public ContainerItemViewMap()
{
Table("V_CONTAINER_ITEM");
ReadOnly();
Not.LazyLoad();
SchemaAction.None();
//Location mappings
Id(x => x.ContainerId, "CONTAINER_ID");
Map(x => x.Title);
Map(x => x.Code);
Map(x => x.Name);
}
}
Also with the retrieval of my view objects I will always use a Stateless session because to me it doesn't make sense to use ISession here since you aren't going to be doing any CUD (create, update, delete) operations on the data.
Related
The issue is when selecting directly on the base model the generated SQL performs an left outer select on the subclasses.
Basemodel.
public class Node
{
public virtual int ID {get;set;}
public virtual string Url {get;set;}
}
public class CMSPage : Node
{
public virtual string FieldA {get;set;}
public virtual string FieldB {get;set;}
}
public class Article : Node
{
public virtual string FieldC {get;set;}
public virtual string FieldD {get;set;}
}
My Mapping is
public class NodeMap : ClassMap<Node>
{
Table("Nodes");
Id(x => x.ID, "Node_ID");
Map(x => x.Url);
}
public class CMSPageMap: SubclassMap<CMSPage>
{
Table("CMSPages");
Map(x => x.FieldA);
Map(x => x.FieldB);
}
public class ArticleMap: SubclassMap<Article>
{
Table("Articles");
Map(x => x.FieldC);
Map(x => x.FieldD);
}
When querying direct on Nodes using ICriteria
var store = session.CreateCriteria(typeof(Node));
store.Add(Restrictions.Eq("Url", filter.Url));
store.SetProjection(Projections.ProjectionList()
.Add(Projections.Property("ID"), "ID")
.Add(Projections.Property("Url"), "Url"));
store.SetResultTransformer(new
AliasToBeanResultTransformer(typeof(Node)));
Node result = store.UniqueResult<Node>();
The sql generated is
SELECT this_.Node_ID as y0_, this_.Url as y7_ FROM Nodes this_
left outer join CMSPages this_1_
on this_.Node_ID = this_1_.Node_ID
left outer join Articles this_2_
on this_.Node_ID = this_2_.Node_ID
WHERE this_.Url = '/' /* #p0 - Url */
How do i prevent the join selects
Ive tried using both Abstract and KeyColumn according to https://www.codeproject.com/Articles/232034/Inheritance-mapping-strategies-in-Fluent-Nhibernat
Subclass mapping purpose is to have a Table per class hierarchy. Means you have one table for any child class. You can find examples on the documentation about all kind of supported inheritance by NHibernate.
I think you need to try "Table per subclass" strategy, using a JoinedSubclassMap<T>. The two subclass tables have primary key associations to the superclass table so the relational model is actually a one-to-one association. Documentation 9.1.2. Table per subclass
If you want to keep the SubclassMap<T> you must use a discriminator. 9.1.3. Table per subclass, using a discriminator.
I have an entity:
public class Foo
{
public virtual int Id;
public virtual IEnumberable<string> Bars;
}
And its mapping:
public class FooMapping : ClassMap<Foo>
{
public FooMapping()
{
Table("foo_table_in_database");
Id(x => x.Id, "Id");
HasMany(x => x.Bars)
.AsList()
.Table("bars_table_in_db")
.Element("BarId", m =>
{
m.Type<string>();
});
}
}
And an exception returned inside the entity insteaf of the expected result :(
base = {"could not initialize a collection: [Loya.Services.CouponsWeb.Promotion.LoyCouponCustomerGroups#2][SQL: SELECT loycouponc0_.Promotion_id as Promotion3_0_, loycouponc0_.LoyCustomerGroupId as LoyCusto1_0_, loycouponc0_.Index as Index0_ FROM loy_promotion__cu...
Database tables :
foo_table : *Id, other properties
bar_table : *FooId, *BarId
My aim is to get a List of BarId's (strings) in my Foo.
How do I map it properly?
I think you might need to specify the KeyColumn. I do something similar in one of my solutions and I would map the entities above as follows...
mapping.HasMany(x => x.Bars)
.Schema("Schema")
.Table("FooBars")
.Element("Bar")
.KeyColumn("FooID")
.ForeignKeyConstraintName("FK_FooBar_Foo")
.Not.Inverse()
.Cascade.All()
.Fetch.Select();
This will give a table called Schema.FooBars. It will have a FooID column (which is a foreign key back to the Foo table) and a Bar column which contains the value in your Bars collection.
I'm trying to figure out the best way to accomplish a design that's persisted with NHibernate. Here's what the data would ideally look like:
Person
===============
Id (int)
Name (nvarchar)
Privileges
==============
Person_Id
Right (int)
AccessLevel (int)
Ideally, I'd be able to create classes like this (pretend I have all the NHibernate virtual modifiers):
public enum Rights
{
Read = 0,
Save = 1,
Delete = 2
}
public enum AccessLevels
{
LevelOne = 0,
LevelTwo = 1,
LevelThree = 2
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public Dictionary<Rights, AccessLevels> Privileges { get; set; }
}
Originally I considered the design described in this blog post, but I don't want to make the Rights enum a class because I'd like to be able to test for it without having to keep track of the Id of a given right. A pair of enumerations would be easiest I think.
Anyway, I've been striking out figuring out how to accomplish this in NHibernate, much less Fluent NHibernate. I'm hoping some NH gurus might be able to help.
I'm not sure if it's possible... but try something like this (I did not test):
HasMany(x => x.Privileges)
.WithTableName("Privileges")
.KeyColumnNames.Add("Person_Id")
.Cascade.All()
.AsMap<string>(
index => index.WithColumn("Right").WithType<Rights>(),
element => element.WithColumn("AccessLevel").WithType<AccessLevels>()
);
}
Or something like this:
References(x => x.Privileges).AsMap<Rights>("Right").Element("AccessLevel", c => c.Type<AccessLevels>());.
Take a look at this thread: How do I map a dictionary using Fluent NHibernate automapping?
I'm new to Hibernate and I'm struggling with problem:
I have 3 tables and I need to present some data from these tables on DataGridView.
Normally - without hibernate, I have to make select and bind columns.
But I don't need all columns from these tables.
I have made sample project, where I select data from one table. And I store data as collection of hibernate poco object. Then I bind it as datasource to DataGridView and it is fine.
How to bind if I need columns from more than one table?
Should I have one class with columns. Or 3 poco classes? Or I totally wrong and I need to make this on mapping level?
If you just need to display data (and not change it), you could define a class for the objects you want to have listed in the DataGridView, e.g.
CombinedClassForGrid
{
public Id { get; set; }
public PropertyA1 { get; set; }
public PropertyB1 { get; set; }
public PropertyB2 { get; set; }
}
With NHibernate you can query the tables like this, assuming that ClassA has a property ClassBRef:
ClassB bAlias = null;
CombinedClassForGrid cForGrid = null;
IList<CombinedClassForGrid> result = session.QueryOver<ClassA>()
.JoinAlias(a => a.ClassBRef, () => bAlias)
.SelectList(list => list
.Select(a => a.Id).WithAlias(() => cForGrid.Id)
.Select(a => a.Property1).WithAlias(() => cForGrid.PropertyA1)
.Select(a => bAlias.Property1).WithAlias(() => cForGrid.PropertyB1)
.Select(a => bAlias.Property2).WithAlias(() => cForGrid.PropertyB2))
.TransformUsing(Transformers.AliasToBean<CombinedClassForGrid>())
.List<CombinedClassForGrid>();
Then you should be able to bind result to your DataGridView.
I have 2 tables like this: (Please note the non-standard db schema naming)
table T_Pen
TP_ID
TP_PrimaryColorID
TP_SecondaryColorID
...
table E_Color
EC_ID
EC_ColorName
...
And I want to create a mapping of the 2 tables to a domain object Pen using Fluent NHibernate.
class Pen
{
PenID;
PrimaryColorName;
SecondaryColorName;
...
}
How can I do that?
I don't think you would be able to Insert/Update anymore if you were to only reference the Name.
You could create a view of PenColour or hide the actual reference in your pen class and only expose the Name property.
class Pen
{
int PenID;
Color PrimaryColor;
Color SecondaryColor;
}
class Color
{
int ColorID;
string ColorName;
}
class ColorMap
{
Id(x => x.ColorID);
Map(x => x.ColorName);
}
class PenMap
{
Id(x => x.PenID);
References(x => x.PrimaryColor).Column("TP_PrimaryColorID");
References(x => x.SecondaryColor).Column("TP_SecondaryColorID");
}