Return properties from subclass left joins when querying superclass in NHibernate - nhibernate

I have an ICriteria that returns properties from a superclass Animal. Now I want to include in the results several properties from a subclass, Bird. For other subclasses, these properties should return null. I am using table-per-subclass inheritance. Is there any way to do this without adding lots of DetachedCriteria? NHibernate already left joins on the subclass tables; is there a way to project values from those joins?
Update: I need to be able to sort and filter on the subclass properties, and the entire query needs to support paging.
Here is my model:
public abstract class Animal
{
public long Id { get; set; }
public string Name { get; set; }
}
public class Cat : Animal
{
public int WhiskerCount { get; set; }
}
public class Bird : Animal
{
public long WingSpan { get; set; }
}
Given the following tables:
Animal:
Id | Name
----+--------------
1 | Sylvester
2 | Tweety
3 | Maru
4 | Big Bird
Cat:
Id | WhiskerCount
----+--------------
1 | 6
3 | 12
Bird:
Id | Wingspan
----+--------------
2 | 0.5
4 | 10
I want the following result set:
Id | Name | Wingspan
----+------------+-------------
1 | Sylvester | <null>
2 | Tweety | 0.5
3 | Maru | <null>
4 | Big Bird | 10

var results = session.CreateCriteria<Animal>()
.List<Animal>()
.Select(a => new
{
Id = a.Id,
Name = a.Name,
Wingspan = (a is Bird) ? ((Bird)a).Wingspan : (int)null
});

Related

Issue wih Laravel MorphOne

I am facing an issue with Polymorphic Relationships in morphOne
I have 3 tables User, Employee, Security contains
User:
id | userable_id | userable_type
1 | 1 | App\Models\Employee
2 | 1 | App\Models\Security
public function userable() { return $this->morphTo(); }
Employee
id | Name | Email | Password
1 | John | john#gmail.com | xxxxxxxxxxx
public function user() { return $this->morphOne(User::class, 'userable'); }
Security
id | Name | Email | Password
1 | Luke | luke#gmail.com | xxxxxxxxxx
public function user() { return $this->morphOne(User::class, 'userable'); }
Now using User::find(1)->userable returns employee data
but using Employee::find(1)->user returns empty

Is adding this hierarchically class an antipattern

I had some classes like this:
+---+ +---+
| L | ---uses---> | D |
+---+ +---+
| +---+
inherit ----<---- | V |
| +---+ +---+
+---<--- | C |
+---+
Let's say; class L is an abstract class, and V and C inherits from it. And L have a property of class D. - sorry for my bad drawing and also English-
Now, I need to add a new class -RA- that has a property of class D and class V should have a property of class RA, But I also need to get property from objects of class L So V. In my mind something like this:
+---+ +---+ +----+
| L | --uses--> | D | <--uses-- | RA |
+---+ +---+ +----+
| +---+ |
inherit ----<----| V | --uses-->--+
| +---+ +---+
+---<---| C |
+---+
I'm not sure!; But it seems to me like an anti-pattern or breaker of LSP -of SOLID principle- somehow!, But I can't figure it out.
Is my new diagram an anti-pattern? or is there a better way to design this situation?
Note that classes like D, V, C and RA can instantiate.
And I meant that usage of property of class D is now hierarchically in class V.
Edit:
Imagine that I use an interface -IGFP- that returned a string value by using D property in L so in V and C, now in V I need to override it by using RA instead of D.
In C# classes are:
public abstract class L : VOBase<L>, IGFP {
public virtual D D { get; protected set; }
public virtual string Name { get; protected set; }
public virtual string GFP => $"{D.GFP}/{Name}"; // Here I use D property that I think breaks LSP
}
public class C : L {
public C (D d, string name) {
D = d;
Name = name;
}
}
public class V : L {
public V (RA ra, string name) {
RA = ra;
Name = name;
D = ra.D;
}
public RA RA { get; private set; }
public override string GFP => $"{RA.GFP}/{Name}"; // Here I should override GFP to use RA instead of D
}
public class D : VOBase<D>, IGFP {
public D (U u, string name) {
U = u;
Name = name;
}
public U U { get; private set; }
public string Name { get; private set; }
public string GFP => $"{U.GFP}/{Name}";
}
public class RA : VOBase<RA>, IGFP {
public RA (D d, string name) {
D = d;
Name = name;
}
public D D { get; private set; }
public string Name { get; private set; }
public string GFP => $"{D.GFP}/{Name}";
}
I'm sure whatever you are smelling isn't really described by this question.
But let me attempt to break it down.
First D is only used by two other classes. Nothing wrong with that. What makes D any different from both classes referencing a class like String? So let's remove it from the diagram:
+---+ +----+
| L | | RA |
+---+ +----+
| +---+ |
inherit ----<----| V | --uses-->--+
| +---+ +---+
+---<---| C |
+---+
Now, RA is only used, why is that any different from using a class like String or D, let's remove it from the diagram:
+---+
| L |
+---+
| +---+
inherit ----<----| V |
| +---+ +---+
+---<---| C |
+---+
So now we have a very simple diagram that doesn't demonstrate any issues.
So, no, your diagram doesn't indicate any anti pattern. But that's not to say it's not one, just that the diagram doesn't contain enough information. I know you have tried to explain in English, but English is ambiguous. Only seeing an actual code example of how these classes interact would allow people to identify anti-patterns or improvements.

RavenDB: How can I properly index a cartesian product in a map-reduce?

This question is a spin-off of RavenDB: Why do I get null-values for fields in this multi-map/reduce index?, but I realized, the problem was another.
Consider my extremely simplified domain, rewritten to a movie rental store scenario for abstraction:
public class User
{
public string Id { get; set; }
}
public class Movie
{
public string Id { get; set; }
}
public class MovieRental
{
public string Id { get; set; }
public string MovieId { get; set; }
public string UserId { get; set; }
}
It's a text-book many-to-many example.
The index I want to create is this:
For a given user, give me a list of every movie in the database (filtering/search left out for the moment) along with an integer describing how many times (or zero) the user has rented this movie.
Basically like this:
Users:
| Id |
|--------|
| John |
| Lizzie |
| Albert |
Movies:
| Id |
|--------------|
| Robocop |
| Notting Hill |
| Inception |
MovieRentals:
| Id | UserId | MovieId |
|-----------|--------|--------------|
| rental-00 | John | Robocop |
| rental-01 | John | Notting Hill |
| rental-02 | John | Notting Hill |
| rental-03 | Lizzie | Robocop |
| rental-04 | Lizzie | Robocop |
| rental-05 | Lizzie | Inception |
Ideally, I want an index to query, that would look like this:
| UserId | MovieId | RentalCount |
|--------|--------------|-------------|
| John | Robocop | 1 |
| John | Notting Hill | 2 |
| John | Inception | 0 |
| Lizzie | Robocop | 2 |
| Lizzie | Notting Hill | 0 |
| Lizzie | Inception | 1 |
| Albert | Robocop | 0 |
| Albert | Notting Hill | 0 |
| Albert | Inception | 0 |
Or declaratively:
I always want a full list of all the movies (eventually I will add filtering/searching) - even when providing a user that has never rented a single movie yet
I want a count of the rentals for each user, just the integer
I want to be able to sort by the rental-count - i.e. show the most-rented movies for a given user at the top of the list
However, I can't find a way to make the "cross-join" above and save it in the index. Instead, I initially thought I got it right with this maneuver below, but it does not allow me to sort (see failing test):
{"Not supported computation: x.UserRentalCounts.SingleOrDefault(rentalCount => (rentalCount.UserId == value(UnitTestProject2.MovieRentalTests+<>c__DisplayClass0_0).user_john.Id)).Count. You cannot use computation in RavenDB queries (only simple member expressions are allowed)."}
My question is basically: how can I - or can I at all - index so, that my requirements are fulfilled?
Below is my mentioned example, that does not fulfill my requirements, but that's where I am right now. It uses the following packages (VS2015):
packages.config
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Owin.Host.HttpListener" version="3.0.1" targetFramework="net461" />
<package id="NUnit" version="3.5.0" targetFramework="net461" />
<package id="RavenDB.Client" version="3.5.2" targetFramework="net461" />
<package id="RavenDB.Database" version="3.5.2" targetFramework="net461" />
<package id="RavenDB.Tests.Helpers" version="3.5.2" targetFramework="net461" />
</packages>
MovieRentalTests.cs
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Raven.Client.Indexes;
using Raven.Client.Linq;
using Raven.Tests.Helpers;
namespace UnitTestProject2
{
[TestFixture]
public class MovieRentalTests : RavenTestBase
{
[Test]
public void DoSomeTests()
{
using (var server = GetNewServer())
using (var store = NewRemoteDocumentStore(ravenDbServer: server))
{
//Test-data
var user_john = new User { Id = "John" };
var user_lizzie = new User { Id = "Lizzie" };
var user_albert = new User { Id = "Albert" };
var movie_robocop = new Movie { Id = "Robocop" };
var movie_nottingHill = new Movie { Id = "Notting Hill" };
var movie_inception = new Movie { Id = "Inception" };
var rentals = new List<MovieRental>
{
new MovieRental {Id = "rental-00", UserId = user_john.Id, MovieId = movie_robocop.Id},
new MovieRental {Id = "rental-01", UserId = user_john.Id, MovieId = movie_nottingHill.Id},
new MovieRental {Id = "rental-02", UserId = user_john.Id, MovieId = movie_nottingHill.Id},
new MovieRental {Id = "rental-03", UserId = user_lizzie.Id, MovieId = movie_robocop.Id},
new MovieRental {Id = "rental-04", UserId = user_lizzie.Id, MovieId = movie_robocop.Id},
new MovieRental {Id = "rental-05", UserId = user_lizzie.Id, MovieId = movie_inception.Id}
};
//Init index
new Movies_WithRentalsByUsersCount().Execute(store);
//Insert test-data in db
using (var session = store.OpenSession())
{
session.Store(user_john);
session.Store(user_lizzie);
session.Store(user_albert);
session.Store(movie_robocop);
session.Store(movie_nottingHill);
session.Store(movie_inception);
foreach (var rental in rentals)
{
session.Store(rental);
}
session.SaveChanges();
WaitForAllRequestsToComplete(server);
WaitForIndexing(store);
}
//Test of correct rental-counts for users
using (var session = store.OpenSession())
{
var allMoviesWithRentalCounts =
session.Query<Movies_WithRentalsByUsersCount.ReducedResult, Movies_WithRentalsByUsersCount>()
.ToList();
var robocopWithRentalsCounts = allMoviesWithRentalCounts.Single(m => m.MovieId == movie_robocop.Id);
Assert.AreEqual(1, robocopWithRentalsCounts.UserRentalCounts.FirstOrDefault(x => x.UserId == user_john.Id)?.Count ?? 0);
Assert.AreEqual(2, robocopWithRentalsCounts.UserRentalCounts.FirstOrDefault(x => x.UserId == user_lizzie.Id)?.Count ?? 0);
Assert.AreEqual(0, robocopWithRentalsCounts.UserRentalCounts.FirstOrDefault(x => x.UserId == user_albert.Id)?.Count ?? 0);
var nottingHillWithRentalsCounts = allMoviesWithRentalCounts.Single(m => m.MovieId == movie_nottingHill.Id);
Assert.AreEqual(2, nottingHillWithRentalsCounts.UserRentalCounts.FirstOrDefault(x => x.UserId == user_john.Id)?.Count ?? 0);
Assert.AreEqual(0, nottingHillWithRentalsCounts.UserRentalCounts.FirstOrDefault(x => x.UserId == user_lizzie.Id)?.Count ?? 0);
Assert.AreEqual(0, nottingHillWithRentalsCounts.UserRentalCounts.FirstOrDefault(x => x.UserId == user_albert.Id)?.Count ?? 0);
}
// Test that you for a given user can sort the movies by view-count
using (var session = store.OpenSession())
{
var allMoviesWithRentalCounts =
session.Query<Movies_WithRentalsByUsersCount.ReducedResult, Movies_WithRentalsByUsersCount>()
.OrderByDescending(x => x.UserRentalCounts.SingleOrDefault(rentalCount => rentalCount.UserId == user_john.Id).Count)
.ToList();
Assert.AreEqual(movie_nottingHill.Id, allMoviesWithRentalCounts[0].MovieId);
Assert.AreEqual(movie_robocop.Id, allMoviesWithRentalCounts[1].MovieId);
Assert.AreEqual(movie_inception.Id, allMoviesWithRentalCounts[2].MovieId);
}
}
}
public class Movies_WithRentalsByUsersCount :
AbstractMultiMapIndexCreationTask<Movies_WithRentalsByUsersCount.ReducedResult>
{
public Movies_WithRentalsByUsersCount()
{
AddMap<MovieRental>(rentals =>
from r in rentals
select new ReducedResult
{
MovieId = r.MovieId,
UserRentalCounts = new[] { new UserRentalCount { UserId = r.UserId, Count = 1 } }
});
AddMap<Movie>(movies =>
from m in movies
select new ReducedResult
{
MovieId = m.Id,
UserRentalCounts = new[] { new UserRentalCount { UserId = null, Count = 0 } }
});
Reduce = results =>
from result in results
group result by result.MovieId
into g
select new
{
MovieId = g.Key,
UserRentalCounts = (
from userRentalCount in g.SelectMany(x => x.UserRentalCounts)
group userRentalCount by userRentalCount.UserId
into subGroup
select new UserRentalCount { UserId = subGroup.Key, Count = subGroup.Sum(b => b.Count) })
.ToArray()
};
}
public class ReducedResult
{
public string MovieId { get; set; }
public UserRentalCount[] UserRentalCounts { get; set; }
}
public class UserRentalCount
{
public string UserId { get; set; }
public int Count { get; set; }
}
}
public class User
{
public string Id { get; set; }
}
public class Movie
{
public string Id { get; set; }
}
public class MovieRental
{
public string Id { get; set; }
public string MovieId { get; set; }
public string UserId { get; set; }
}
}
}
Since your requirement says "for a given user", if you really are looking only for a single user, you can do this with a Multi-Map index. Use the Movies table itself to produce the baseline zero-count records and then map in the actual MovieRentals records for the user on top of that.
If you really need it for all users crossed with all movies, I don't believe there is a way to do this cleanly with RavenDB as this would be considered reporting which is noted as one of the sour spots for RavenDB.
Here are some options if you really want to try to do this with RavenDB:
1) Create dummy records in the DB for every user and every movie and use those in your index with a 0 count. Whenever a movie or user is added/updated/deleted, update the dummy records accordingly.
2) Generate the zero-count records yourself in memory on request and merge that data with the data that RavenDB gives you back for the non-zero counts. Query for all users, query for all movies, create the baseline zero-count records, then do the actual query for non-zero counts and layer that on top. Finally, apply paging/filtering/sorting logic.
3) Use the SQL replication bundle to replicate the Users, Movies, and MovieRental tables out to SQL and use SQL for this "reporting" query.

How to get data with #ManyToMany annotated tables?

My related tables are below.
Person Table
id
name
surname
Location Table
id
title
point
Person_Location Table
person_id
location_id
I want to get person and location values like this..
id | name | surname | title | point
1 john | adas | my home | 44,45
1 | John | adas | brother's home | 55,33
How can I get the users and their locations in hibernate?
try this out :
you can get an array of object and you can manipulat them as you want
String stringQuery="select p.id,p.name,p.surname,l.title,l.point from person p, location l,person_location pl where "
+ "p.id=pl.person_id and l.id=pl.location_id";
Query query=entityManager.createNativeQuery(stringQuery);
List<Object[]> result=query.getResultList();
later you can get the person Id by result.get(i)[0]
or you can create a custom class which will not be a managed entity:
public class customPerson{
id | name | surname | title | point
private int id;
private String name;
private String surname;
private String title;
private String doube point;
//getters&setters
//constructors (required) one default ant one with all your attributes
public CustomPerson(){}
public customPerson(int id,...){
...
}
}
later in your Dao you can get the result you want through the custom object:
String stringQuery="select p.id,p.name,p.surname,l.title,l.point from person p, location l,person_location pl where "
+ "p.id=pl.person_id and l.id=pl.location_id";
Query query=entityManager.createNativeQuery(stringQuery,CustomPerson.class);
List<CustomPerson> result=query.getResultList();
// In Person class:
#ManyToMany
#JoinTable(name="Person_Location",
joinColumns=
#JoinColumn(name="person_id", referencedColumnName="ID"),
inverseJoinColumns=
#JoinColumn(name="location_id", referencedColumnName="ID")
)
public Set<Locations> getLocations() { return locations; }
// In Location:
#ManyToMany(mappedBy="locations")
public Set<Person> getPersons() { return persons; }
In Person class
#OneToMany(mappedBy="person")
public Set<personLocations> getPersonLocation { return personLocations; }
In Location class
#OneToMany(mappedBy="location")
public Set<personLocations> getPersonLocation { return personLocations; }
In personLocations
#ManyToOne
#JoinColumn(name="person_ID")
public Person getPerson() { return person; }
#ManyToOne
#JoinColumn(name="location_ID")
public Location getlocation() { return location; }

Linq join 2 datatables that share a column and put result in new datatable

I have the following datatables
table1:
+-----------+------+------+
| catalogid | name | snum |
+-----------+------+------+
| 353 | xx | 4 |
| 364 | yy | 3 |
| 882 | zz | 3 |
| 224 | mm | 71 |
| 999 | kk | 321 |
| 74 | kk | 4 |
| 54 | ii | 5 |
| 11 | u | 6 |
| 23 | yy | 6 |
+-----------+------+------+
table2:
+-----------+----------+--------------+
| catalogid | numitems | ignoreditems |
+-----------+----------+--------------+
| 353 | 4 | 0 |
| 364 | 10 | 0 |
| 882 | 2 | 0 |
| 224 | 0 | 7 |
+-----------+----------+--------------+
Using LINQ I want to join them and copy the result to a new datatable. They both share catalogid, and in the result it should only display the records that their catalogid exist in table2
result:
+-----------+------+------+-----------+---------------+
| catalogid | name | snum | numitems | ignoreditems |
+-----------+------+------+-----------+---------------+
| 353 | xx | 4 | 4 | 0 |
| 364 | yy | 3 | 10 | 0 |
| 882 | zz | 3 | 2 | 0 |
| 224 | mm | 71 | 0 | 7 |
+-----------+------+------+-----------+---------------+
Here is my attempt but it's not working:
Dim query = From a In oresult.AsEnumerable
Group Join b In products.AsEnumerable
On a.Field(Of Integer)("catalogid") Equals b.Field(Of Integer)("catalogid")
Into Group
query.copytodatatable
CopyToDatatable is not working, and I can't figure out why
CopyToDataTable() only works when your query returns an IEnumerable<'DataRow>. In your query, you are returning an anonymous type. Anonymous types don't carry the extension method for CopyToDataTable().
You can create a table using the ConvertToDataTable extension listed below. You'll have to convert it to VB.NET (there are converters out there if you google).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using Common;
namespace TestConsole
{
public class Linq_join_2_datatables_that_share_a_column_and_put_result_in_new_datatable
{
public class Table1
{
public int CatalogId { get; set; }
public string Name { get; set; }
public int SNum { get; set; }
}
public class Table2
{
public int CatalogId { get; set; }
public int NumItems { get; set; }
public int IgnoredItems { get; set; }
}
public static void Start()
{
DataTable table1 = new DataTable();
table1.Columns.Add("catalogid", typeof(int));
table1.Columns.Add("name", typeof(string));
table1.Columns.Add("snum", typeof(int));
DataRow row = table1.Rows.Add(353, "xx", 4);
DataTable table2 = new DataTable();
table2.Columns.Add("catalogid", typeof(int));
table2.Columns.Add("numitems", typeof(int));
table2.Columns.Add("ignoreditems", typeof(int));
table2.Rows.Add(353, 4, 0);
var query = (from t1 in table1.AsEnumerable()
join t2 in table2.AsEnumerable() on t1.Field<int>("catalogid") equals t2.Field<int>("catalogid")
select new
{
catalogid = t1.Field<int>("catalogid"),
name = t1.Field<string>("name"),
snum = t1.Field<int>("snum"),
numitems = t2.Field<int>("numitems"),
ignoreditems = t2.Field<int>("ignoreditems")
}).ToList();
DataTable table3 = query.ConvertToDataTable();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.ComponentModel;
using System.Reflection;
namespace Common
{
public static class DataTableExtensions
{
public static DataTable ConvertToDataTable<T>(this IList<T> data)
{
PropertyDescriptorCollection properties =
TypeDescriptor.GetProperties(typeof(T));
DataTable table = new DataTable();
foreach (PropertyDescriptor prop in properties)
table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
foreach (T item in data)
{
DataRow row = table.NewRow();
foreach (PropertyDescriptor prop in properties)
row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
table.Rows.Add(row);
}
table.AcceptChanges();
return table;
}
}
}
CopyToDataRow only works on IEnumerables of DataRow. See this article at MSDN for an implementation of CopyToDataRow for arbitrary IEnumerables.