more than one column from select - nhibernate

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.

Related

How to get grandchild in Entity Framework Core

How to display children and grandchildren when 'parentItemNo' is given.
Below is my model: Any record (itemNo) can become a parent, child, or grandchild.
public partial class item
{
public string ItemNo { get; set; }
public string ParentItemNo { get; set; }
}
Below query returns children: Would like to display grandchildren as well.
var result = Context.Items.Where(p => p.ParentItemNo == parentItemNo).Select(p => p.ItemNo).ToListAsync(); ;
Navigation properties.
Given an Item class which can have multiple levels where each item can have an optional Parent:
public class Item
{
[Key]
public string ItemNo { get; set; }
[ForeignKey("Parent")]
public string ParentItemNo { get; set; }
public virtual Item Parent { get; set; }
public virtual ICollection<Item> Children { get; set; } = new List<Item>();
}
From here I would recommend setting up the mapping explicitly, either in the DbContext OnModelCreating event or using an EntityTypeConfiguration implementation:
modelBuilder.Entity<Item>()
.HasOptional(x => x.Parent)
.WithMany(x => x.Children);
When you want to get the children and grand-children for an item:
var children = await context.Items
.Include(x => x.Children) // eager load children
.ThenInclude(x => x.Children) // eager load grandchildren
.Where(x => x.ItemNo == parentItemNo)
.SelectMany(x => x.Children)
.ToListAsync();
Normally though you'd load the parent with it's children and grandchildren eager loaded:
var parent = await context.Items
.Include(x => x.Children)
.ThenInclude(x => x.Children)
.SingleAsync(x => x.ItemNo == parentItemNo);
For eager loading you need to plan ahead with what data you expect to need, otherwise you should ensure lazy loading is enabled and available if it's possible that a grandchild could itself have children items. Iterating down to this level would either attempt a lazy load (possibly resulting in errors if the object is orphaned from it's DbContext) or return no data or a partial representation of data if the DbContext was already tracking some of the great grandchildren that it could fill in.
Edit: if you just want to get the Item Numbers of the children and grandchildren then you don't need to use the eager loading with Include as that would be to return entities and their related entities. Projection with Select / SelectMany does not need this.
To select the ItemNos of any Children and Grandchildren:
var childDetails = await context.Items
.Where(x => x.ItemNo == parentItemNo)
.SelectMany(x => x.Children.Select(c => new
{
c.itemNo,
GrandChildItemNos = c.Children.Select(gc => gc.itemNo).ToList()
}).ToListAsync();
What this will give you is a list of anonymous types containing each child's Item #, and the list of that child's grandchild item #s. From there you can combine all of the item #s:
List<string> itemNumbers = childDetails.Select(x => x.itemNo).ToList();
foreach(var child in childDetails)
itemNumbers.AddRange(child.GrandChildItemNos);
This would combine all of the item numbers for the children and their grandchildren into a single list. From there if relationships to a particular child could be doubled up (2 children share the same grandchild) you can add a Distinct() to the end to remove duplicates.
It may be possible to select both child and grand child in the EF Linq operation using something like a Union but if possible I'd expect it to be a fair bit more complex and might be difficult to modify and understand later on.

How can I successfully map a many to many table in nhibernate with a composedID and still save to it directly?

I have a bunch of GUID constants in my code for certain tag categories that are important in my application. This is mapped in a simple two column many to many table. I often want to avoid fetching the nhibernate object because all I really need is the GUID and it's already hardcoded. I also noticed it's much quicker and easier to do certain queries with direct table access. So, the goal is to to map those nhibernate many to many tables as a class so they can be read and written to without disrupting nhibernates usage of them in the regular sense; while at the same time using GUIDs for identifiers.
Anyway, I have settled on using a composedID across the two columns nhibernate generates. But there is a problem. If I use composed ID and make a Category tag object and try to save my TagID and CategoryTag ID directly, TagID gets saved to the CategoryTagID column and CategoryTagID gets saved to the TagID column!
public class CategoryTagMapping : ClassMapping<CategoryTag>
{
public CategoryTagMapping ()
{
Table("CategoryTag");
/*Id(x => x.ID, map => map.Generator(Generators.Guid));*/
Property(x => x.CategoryTagID, map => { map.Column("CategoryTagID");});
Property(x => x.TagID, map => { map.Column("TagID"); });
ComposedId(p =>
{
p.Property(p1 => p1.CategoryTagID, a => a.Column("TagID"));
p.Property(p1 => p1.TagID, a => a.Column("CategoryTagID"));
});
}
}
public class CategoryTag
{
/*public virtual Guid ID {get;set;}*/
public virtual Guid CategoryTagID { get; set; }
public virtual Guid TagID { get; set; }
public override bool Equals(object obj)
{
if (obj == null)
return false;
var t = obj as CategoryTag;
if (t == null)
return false;
if (this.CategoryTagID == t.CategoryTagID && this.TagID == t.TagID)
return true;
return false;
}
public override int GetHashCode()
{
return (this.CategoryTagID + "|" + this.TagID).GetHashCode();
}
}
Trying to do this:
CategoryTag A = new CategoryTag { CategoryTagID = Constants.GUID1, TagID = Constants.GUID2 };
If I add the ID column by uncommenting the two lines, the saving works properly. But then that breaks the regular usage of the table because mysql can't auto increment the guid field and nhibernate won't generate an ID to go in the ID column.
Anyhow, maybe it's a bug, but maybe there's a workaround. Is there something wrong with the mapping, or the equals/gethashcode?
Thanks!
I am stupid. The columns are mismatched in the composedID mapping part.

Nhibernate query for items that have a Dictionary Property containing value

I need a way to query in Nhibernate for items that have a Dictionary Property containing value.
Assume:
public class Item
{
public virtual IDictionary<int, string> DictionaryProperty {get; set;}
}
and mapping:
public ItemMap()
{
HasMany(x => x.DictionaryProperty)
.Access.ReadOnlyPropertyThroughCamelCaseField(Prefix.Underscore)
.AsMap<string>(
index => index.Column("IDNumber").Type<int>(),
element => element.Column("TextField").Type<string>().Length(666)
)
.Cascade.AllDeleteOrphan()
.Fetch.Join();
}
I want to query all Items that have a dictionary value of "SomeText". The following example in Linq fails:
session.Query<Item>().Where(r => r.DictionaryProperty.Any(g => g.Value == "SomeText"))
with error
cannot dereference scalar collection element: Value
So is there any way to achieve that in NHibernate? Linq is not an exclusive requirement but its preffered. Not that I'm not interested to query over dictionary keys that can be achieved using .ContainsKey . Φορ this is similar but not the same
Handling IDictionary<TValueType, TValueType> would usually bring more issues than advantages. One way, workaround, is to introduce a new object (I will call it MyWrapper) with properties Key and Value (just an example naming).
This way we have to 1) create new object (MyWrapper), 2) adjust the mapping and that's it. No other changes... so the original stuff (mapping, properties) will work, because we would use different (readonly) property for querying
public class MyWrapper
{
public virtual int Key { get; set; }
public virtual string Value { get; set; }
}
The Item now has
public class Item
{
// keep the existing for Insert/Updae
public virtual IDictionary<int, string> DictionaryProperty {get; set;}
// map it
private IList<MyWrapper> _properties = new List<MyWrapper>();
// publish it as readonly
public virtual IEnumerable<MyWrapper> Properties
{
get { return new ReadOnlyCollection<MyWrapper>(_properties); }
}
}
Now we will extend the mapping:
HasMany(x => x.Properties)
.Access.ReadOnlyPropertyThroughCamelCaseField(Prefix.Underscore)
.Component(c =>
{
c.Map(x => x.Key).Column("IDNumber")
c.Map(x => x.Value).Column("TextField")
})
...
;
And the Query, which will work as expected:
session
.Query<Item>()
.Where(r =>
r.Properties.Any(g => g.Value == "SomeText")
)
NOTE: From my experience, this workaround should not be workaround. It is preferred way. NHibernate supports lot of features, but working with Objects brings more profit

RavenDb static Index: query on child collection objects

I have a document structure like the following:
Employer => Positions => RequiredSkills
Employer has a collection of Position
Positions have a collection of RequiredSkill.
Required Skill consists of a skill (string) and a proficiency (enum).
If I use a dynamic index it seems to return company fine, however I want to use an index to populate MVC view models to return to the UI.
I'm really new to Raven so my apologies for doing anything stupid/unnecessary!
I've got the following mapping:
public class PositionSearch : AbstractIndexCreationTask<Employer>
{
public PositionSearch()
{
Map = employers =>
from employer in employers
from position in employer.Positions
select new
{
EmployerId = employer.Id,
EmployerName = employer.Name,
PositionId = position.Id,
PositionTitle = position.Title,
position.Location,
position.Description,
RequiredSkills = position.RequiredSkills
};
StoreAllFields(FieldStorage.Yes);
Index("RequiredSkills_Skill", FieldIndexing.Analyzed);
}
}
However when I try to execute the following query:
var results = session.Query<PositionSearchResultModel, PositionSearch>()
.Customize(x => x.WaitForNonStaleResults())
.Where(x=>x.RequiredSkills.Any(y=>y.Skill == "SkillName"))
.ProjectFromIndexFieldsInto<PositionSearchResultModel>()
.ToList();
I get the following error:
System.ArgumentException:
The field 'RequiredSkills_Skill' is not indexed,
cannot query on fields that are not indexed
Can anyone see what I'm doing wrong or suggest another approach for me please?
Thanks,
James
UPDATE my view model - Thanks :
public class PositionSearchResultModel
{
public PositionSearchResultModel()
{
RequiredSkills = new HashSet<SkillProficiency>();
}
public string EmployerId { get; set; }
public string EmployerName { get; set; }
public string PositionId { get; set; }
public string PositionTitle { get; set; }
public string Location { get; set; }
public string Description { get; set; }
public ICollection<SkillProficiency> RequiredSkills { get; set; }
}
Because you want to do an analyzed search against the skill name, you need to isolate it as a separate index entry.
public class PositionSearch
: AbstractIndexCreationTask<Employer, PositionSearchResultModel>
{
public PositionSearch()
{
Map = employers =>
from employer in employers
from position in employer.Positions
select new
{
EmployerId = employer.Id,
EmployerName = employer.Name,
PositionId = position.Id,
PositionTitle = position.Title,
position.Location,
position.Description,
position.RequiredSkills,
// Isolate the search property into it's own value
SkillsSearch = position.RequiredSkills.Select(x => x.Skill)
};
// you could store all fields if you wanted, but the search field
// doesn't need to be stored so that would be wasteful.
Store(x => x.PositionId, FieldStorage.Yes);
Store(x => x.PositionTitle, FieldStorage.Yes);
Store(x => x.Location, FieldStorage.Yes);
Store(x => x.Description, FieldStorage.Yes);
Store(x => x.RequiredSkills, FieldStorage.Yes);
// Any field you are going to use .Search() on should be analyzed.
Index(x => x.SkillsSearch, FieldIndexing.Analyzed);
}
}
Note that I specified the projection as the result of the index. This is syntactic sugar. It's not wrong to leave it off, but then you have to specify your search field using a string.
You'll also need to add the search field to your results class
public string[] SkillsSearch { get; set; }
It really doesn't matter what type it is. A string array or collection will do just fine. You could also use just a string or an object, because it's only the name that's relevant.
When you query against this index, use the .Search() method, like this:
var results = session.Query<PositionSearchResultModel, PositionSearch>()
.Customize(x => x.WaitForNonStaleResults()) // only use this in testing
.Search(x=> x.SkillsSearch, "SkillName")
.ProjectFromIndexFieldsInto<PositionSearchResultModel>() // AsProjection would also work
.ToList();
Note that the only reason you have to store so many fields is because you want to project them. If you separated the positions into their own documents, you would have much smaller indexes and much less to project. Keep in mind that when you project, all of the fields in the original document are already there and come directly from the document store, rather than having to be copied into the index. So if your original documents more closely match your desired results, then there's less work to do.

Fluent Nhibernate will not save

I have the following classes:
public class PhoneModel
{
public virtual ModelIdentifier SupportModels
}
public class ModelIdentifier
{
public virtual string Name
public virtual IList<string> Values
}
This is how i mapped it:
mapping.Component(x => x.SuppoertedModel, y =>
{
y.Map(x => x.Name, "FAMILY_ID");
y.HasMany(x => x.Values).Element("VALUE").Table("SUPPORTEDMODULS")
}
2 tables were created:
PhoneModel
column "FAMILY_ID"
SUPPORTEDMODELS
column "VALUE", "PHONE_MODEL_ID"
The problem is that when I am adding values, it will not save it to the SUPPORTEDMODELS table:
var pm = new PhoneModel();
pm.SupportedModels.Name = "11"
pm.SupportedModels.Values.Add("34");
you are missing HasMany(x => x.Values).Cascade.AllDeleteOrphan()
maybe session.Flush() or transaction.Commit() is missing in the test code
btw: if the order of the Values is not important then change it to ICollection<string> and HasMany().AsSet() since this allows NH to optimise some things and code can not rely on the order which is not relyable when using sql.