I am using Nhibernate last version in my MVC project
I want to make a query on an object type names "Person" but person has many properties. In one of my queries I'd better not to have most of these properties.
Here is Person:
public partial class Person
{
virtual public User User { get; set; }
virtual public string FirstName { get; set; }
virtual public string LastName { get; set; }
virtual public int Gender { get; set; }
virtual public Contact Contact { get; set; }
virtual public BirthCertificate PersonBirthCertificate { get; set; }
}
In the query I just want first name and last name of the person.
So I decided to use ResultTransformer and implement it like this:
public class PersonResultTransformer : IResultTransformer
{
private Type result;
private PropertyInfo[] properties;
#region IResultTransformer Members
public IList TransformList(IList collection)
{
return collection;
}
public PersonResultTransformer (Type result, params string[] names)
{
this.result = result;
List<PropertyInfo> props = new List<PropertyInfo>();
foreach (string name in names)
{
props.Add(result.GetProperty(name));
}
properties = props.ToArray();
}
public object TransformTuple(object[] tuple, string[] aliases)
{
object instance = Activator.CreateInstance(result);
for (int i = 0; i < tuple.Length; i++)
{
var t = properties[i].Name;
var value = tuple[i].GetType().GetProperty(t).GetValue(tuple[i], null);
instance.GetType().GetProperty(t).SetValue(instance, value, null);
}
return instance;
}
#endregion
}
and for getting want I want with linq query :
var person = Repository<Person>.Find(p => p.LastName.Equals("Nadal")
, new PersonResultTransformer (typeof(Person), "FirstName", "LastName"));
////////////////
public IQueryable<T> Find(Expression<Func<T, bool>> expression, IResultTransformer transformer)
{
return Session.CreateCriteria<T>().SetResultTransformer(transformer).List<T>()
.AsQueryable().Where(expression);
}
Is it OK? can it be more customized? do I use it correctly?
It seems that it has a big problem: it gets all persons's first name and last name and then select from them, those having last name="nadal", and the performance is not good at all
first you have to delegate to a query which understands expressions
using NHibernate.Linq;
public IQueryable<T> Find(Expression<Func<T, bool>> expression)
{
return Session.Query<T>().Where(expression);
}
then you can
var personDTOs = Repository<Person>.Find(p => p.LastName.Equals("Nadal"))
.Select(person => new { person.FirstName, person.LastName });
you can shorten the resulttransformer a lot, but you cant use it with Linq2NHibernate.
public class PersonResultTransformer : IResultTransformer
{
private Type result;
private List<PropertyInfo> properties = new List<PropertyInfo>();
public IList TransformList(IList collection)
{
return collection;
}
public PersonResultTransformer (Type result, params string[] names)
{
this.result = result;
foreach (string name in names)
{
properties.Add(result.GetProperty(name));
}
}
public object TransformTuple(object[] tuple, string[] aliases)
{
object instance = Activator.CreateInstance(result);
for (int i = 0; i < tuple.Length; i++)
{
properties[i].SetValue(instance, tuple[i], null);
}
return instance;
}
}
You use Session.CreateCriteria<T>() but you do not add any criteria to it. So, the query is executed without criteria and then transformed and filtered.
You have to find a way to add your specifications to the Find method in a way that Nhibernate's ICriteria can handle, or use Linq to Nhibernate (beware of some issues though!).
I could be wrong - but I think you're overdoing this.
You could more easily utilize lazy loading to not fetch the properties you don't need.
Related
public class PersonBrief
{
public int Id { get; set; }
public string Picture { get; set; }
public PersonBrief(Person person)
{
Id = person.Id;
Picture = person.Picture;
}
}
public class Person : PersonBrief
{
public string FullName { get; set; }
}
var results = session.Query<Person>()
.Select(x => new PersonBrief(x))
.ToList();
Assert.IsNull(results[0] as Person); // Fails
Is this a bug? If not, what would be the correct way to select only the fields i'm interested in?
It would work if you move the .ToList before the .Select, but that would be doing the work on the client.
If you want to do it on the server, you need to use As in your query, and you need a static index that does a TransformResults. See these docs.
I have the following scenario:
Let's say that my "Product" table in this legacy database has a "Categories" column of type string. This column stores the category ID's separated by some sort of ascii character. For instance: "|1|" (for category 1), "|1|2|3|" (for categories 1, 2, and 3), etc.
Instead of exposing a string property for that, I want to expose an IEnumerable, so that users of my Product class don't have to worry about parsing those values.
I'm creating a SelectedCatories type that's simply an IEnumerable, and my Product class looks like this:
public class Product
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual bool Discontinued { get; set; }
public virtual SelectedCategories Categories { get; set; }
}
I then created a SelectedCategoriesUserType class like so:
public class SeletedCategoriesUserType : IUserType
{
static readonly SqlType[] _sqlTypes = {NHibernateUtil.String.SqlType};
public bool Equals(object x, object y)
{
// Fix this to check for Categories...
if (ReferenceEquals(x, y)) return true;
if (x == null || y == null) return false;
return x.Equals(y);
}
public int GetHashCode(object x)
{
return x.GetHashCode();
}
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
object obj = NHibernateUtil.String.NullSafeGet(rs, names[0]);
if (obj == null) return null;
string[] stringCategories = obj.ToString().Split(new[] {'|'}, StringSplitOptions.RemoveEmptyEntries);
var categories = new Categories();
return
new SelectedCategories(
stringCategories.Select(
stringCategory => categories.Single(cat => cat.Id == int.Parse(stringCategory)))
.ToList());
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
if (value == null)
{
((IDataParameter) cmd.Parameters[index]).Value = DBNull.Value;
}
else
{
var theCategories = (SelectedCategories) value;
var builder = new StringBuilder();
builder.Append("|");
theCategories.ForEach(i => builder.AppendFormat("{0}|", i.Id.ToString()));
((IDataParameter) cmd.Parameters[index]).Value = builder.ToString();
}
}
public object DeepCopy(object value)
{
return value;
}
public object Replace(object original, object target, object owner)
{
throw new NotImplementedException();
}
public object Assemble(object cached, object owner)
{
throw new NotImplementedException();
}
public object Disassemble(object value)
{
throw new NotImplementedException();
}
public SqlType[] SqlTypes
{
get { return _sqlTypes; }
}
public Type ReturnedType
{
get { return typeof (SelectedCategories); }
}
public bool IsMutable
{
get { return false; }
}
}
I then want to build a query that gives me back any product that belongs in a specific category (say, category 2), matching both "|2|", and "|1|2|3|".
Right now, my naive implementation that barely makes my test pass looks like this:
public IEnumerable<Product> GetByCategory(Category category)
{
using (ISession session = NHibernateHelper.OpenSession())
{
return session
.CreateSQLQuery("select * from product where categories LIKE :category")
.AddEntity(typeof(Product))
.SetString("category", string.Format("%|{0}|%", category.Id))
.List()
.Cast<Product>();
}
}
My question is: what's the proper way to right that query?
A different way to do that ICriteria query would be this...
return Session
.CreateCriteria(typeof(Product), "product")
.Add(Expression.Sql(
"{alias}.categories LIKE ?",
string.Format("%|{0}|%", category.Id),
NHibernateUtil.String))
.List<Product>();
However, you may want to think about setting up a many-to-many table between Product and Category and setting up a collection of Categories in the Product class. You can still keep your field of concatenated Category Ids (I assume it's needed for legacy purposes), but tie it to the collection with something like this.
public virtual ISet<Category> Categories { get; private set; }
public virtual string CategoriesString
{
get { return string.Join("|", Categories.Select(c => c.Id.ToString()).ToArray()); }
}
Doing something like this will let you set foreign keys on your tables, and make the queries a bit easier to construct.
return Session
.CreateCriteria(typeof(Product), "product")
.CreateCriteria("product.Categories", "category")
.Add(Restrictions.Eq("category.Id", category.Id))
.List<Product>();
I'm wondering how to persist a property that depends on both regular persisted properties (e.g. string, int) as well as some custom transforms of their own.
For instance suppose I have
class A
{
public int Id {get; set;}
public string SomeString {get; set;}
public object SpecialProperty {get; set;}
}
suppose that persisting SpecialProperty requires reading SomeString and depending on it's value, producing some byte[] which can then be stored in the database.
My first thought was to use an IUserType, but this the method NullSafeGet(IDataReader rs, string[] names, object owner) is invoked before (or actually, during) SomeString is persisted (or not), so it is not set.
My second thought was to use a ICompositeUserType and some rather convoluted setup using wrappers.
My third thought was maybe I could implement ILifecycle and hook the OnLoad() method. But if I wanted to do that I would need to create a seperate property for the byte[] payload which I don't really want to store. This certainly seems the easiest to implement but also somewhat inelegant.
e.g.
class A : ILifecycle
{
public int Id {get; set;}
public string SomeString {get; set;}
public object SpecialProperty {get; set;}
private byte[] payloadData { get; set; }
public void OnLoad(ISession s, object id)
{
SpecialProperty = customIn(SomeString, payloadData);
payloadData = null;
}
static object customIn(string a, byte[] payload)
{
// ...
}
}
Does anybody know of an an easier and possibly more concise way?
I'd do something like the following:
class A
{
public virtual int Id {get; set;}
protected virtual string _SomeString { get; set; }
protected virtual object _SpecialProperty { get; set; }
public virtual object SpecialProperty {get { return _SpecialProperty; } }
public virtual string SomeString {get { return _SomeString; } set { _SomeString = value; _SpecialProperty = SomeManipulatorMethod(value); } }
}
I'd only map Id, _SomeString and _SpecialProperty.
I was so far and yet so close. Of course, it turns out we can access the dependent property from the IDataReader in the NullSafeGet method.
A complete(-ish) solution is here:
Firstly let us define a fluent mapping:
public class AMap : ClassMap<A>
{
public AMap()
{
Id(x => x.Id);
Map(x => x.SomeString);
Map(x => x.SpecialProperty)
.CustomType(typeof (DependentProperty))
;
}
static object DeserialiseSpecialProperty(string SomeString, byte[] specialProperty)
{
// ...
}
static byte[] SerialiseSpecialProperty(object specialProperty)
{
// ...
}
}
Now, implement DependentProperty.
public class DependentProperty: IUserType
{
// change SqlTypes appropriatelly if your backing field is something else
public SqlType[] SqlTypes { get { return new[] {new SqlType(DbType.Binary)}; } }
public Type ReturnedType { get { return typeof (object); } }
public bool IsMutable { get { return false; } }
public int GetHashCode(object x)
{
if (x == null)
return 0;
return x.GetHashCode();
}
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
var SomeString = (string)rs.GetValue(1);
object obj = NHibernateUtil.Binary.NullSafeGet(rs, names[0]);
return AMap.DeserialiseSpecialProperty(SomeString, (byte[])obj);
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
if (value == null)
((IDataParameter) cmd.Parameters[index]).Value = DBNull.Value;
else
((IDataParameter)cmd.Parameters[index]).Value = AMap.DeserialiseSpecialProperty(value);
}
public object DeepCopy(object value) { return value; }
public object Replace(object original, object target, object owner) { return original; }
public object Assemble(object cached, object owner) { return cached; }
public object Disassemble(object value) { return value; }
bool IUserType.Equals(object x, object y) { return object.Equals(x, y); }
}
Notes:
The IUserType implementation is based on a template from a nhibernate.info tutorial. It should be fine for value types, but if you are doing anything complex with reference types or deep object graphs, you may have to provide different implementations for the various methods.
Due to directly accessing the IDataReader this is VERY BRITTLE. Changes to the order of mappings in AMap will most likely require you to update the index accessed. Quite possibly other changes to AMap (and maybe even A) could also break this implementation.
This is unpleasant, yes, but then so is having to store persistence fields (and custom serialiser/deserialiser baggage) directly in your business objects and having them called every time the external property is get/set'd. I would argue that a broken index should be picked up by your persistence validation and this implementation should lead to a generally cleaner separation of business/persistence concerns.
Combine this with (pimp my post?) sqlite's manifest typing capabilities and you can do some very cool dynamic typing stuff :)
How Do You Implement Specification Pattern for querying database using NHibernate?(without LINQ to NHibernate).I read a lot about Specification Pattern but most of them was about Validation and Querying Memory collection objects.
Best method as far as I know using DetachedCriteria in Specification Interface like this.
interface ISpecification<T> {
bool IsSatisfiedBy(T object);
DetachedCriteria CreateCriteria();
}
Is there any alternative or better way to do this?
This is not nessary better, but can be an alternative
interface ISpecification<T>
{
bool IsSatisfiedBy(T object);
Expression<Func<T, bool>> Predicate { get; }
}
Easy to use over linq (to nhibernate) and memory-collections.
I implemented this using a simple extension method and specification pattern, works for System.Linq.IQueryable lists.
public interface IFilter<in T>
{
bool MatchFilter(T o);
}
public static class FilterExtension
{
public static IQueryable<T> Filter<T>(this IQueryable<T> query, IFilter<T> filter)
{
return query.Where(x => filter.MatchFilter(x));
}
}
Simple example classes and IFilter implementation:
public class Organization
{
public string Name { get; set; }
public string Code { get; set; }
public Address Address { get; set; }
public Organization(string name, string code, string city, string country)
{
Name = name;
Code = code;
Address = new Address(city, country);
}
}
public class Address
{
public Address(string city, string country)
{
City = city;
Country = country;
}
public string City { get; set; }
public string Country { get; set; }
}
public class GenericOrganizationFilter : IFilter<Organization>
{
public string FilterString { get; set; }
public GenericOrganizationFilter(string filterString)
{
FilterString = filterString;
}
public bool MatchFilter(Organization o)
{
return
(o.Name != null && o.Name.Contains(FilterString)) ||
(o.Code != null && o.Code.Contains(FilterString)) ||
(o.Address != null && o.Address.City != null && o.Address.City.Contains(FilterString)) ||
(o.Address != null && o.Address.Country != null && o.Address.Country.Contains(FilterString));
}
}
Usage:
IFilter<Organization> filter = new GenericOrganizationFilter("search string");
//Assuming queryable is an instance of IQueryable<Organization>.
IQueryable<Organization> filtered = queryable.Filter(filter);
Say I have a class like this:
public class MyClass
{
public int Id { get; set; }
public DateTime Date { get; set; }
public string String1 { get; set; }
public string String2 { get; set; }
public string String3 { get; set; }
public string String4 { get; set; }
}
Is it possible to get NHibernate to store it in the following schema?
CREATE TABLE [dbo].[MyClass](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Xml] [varchar](max) NOT NULL,
)
Where the Id maps to Id, but then all other fields get serialized into XML (or otherwise)? I don't mind if these other fields have to go on a child object like the below, if that helps:
public class MyClass
{
public int Id { get; set; }
public AllOtherOptions Options { get; set; }
}
public class AllOtherOptions
{
public DateTime Date { get; set; }
public string String1 { get; set; }
public string String2 { get; set; }
public string String3 { get; set; }
public string String4 { get; set; }
}
I am thinking about doing something similar for an upcoming project. The project requires collecting a lot of data but only a few elements need to be stored in a relational database. I haven't started experimenting but these are my thoughts so far.
You can map an XML data type by creating a type that implements IUserType. If the child class (AllOtherOptions) is serializable, you should be able to map the XML field as a private member in MyClass and serialize/deserialize AllOtherOptions as needed. You could either dynamically maintain the XML field (sounds like a lot of work) or create an interceptor to do it. My thinking is that MyClass would implement an interface such as
public interface IXmlObjectContainer
{
void SerializeChildObjects();
void DeSerializeChildObjects();
}
and the interceptor would call those methods as needed. That's a proof of concept idea. I would probably refine that by exposing pairs of xml fields and serializable objects to remove the work of serializing from IXmlObjectContainer implementers. Or maybe handle serialization through the XML field's get/set accessors.
More info:
Working with XML Fields in NHibernate
Another XML implementation of IUserType
I had the same idea to save object in XML column. My idea was other. I took code from links and changed it to generic IUserType implementation. So any field/prop which is [Serializable] can be saved in XML column.
public class XmlUserType<T> : IUserType where T : class
{
public new bool Equals(object x, object y)
{
return x == y;
}
public int GetHashCode(object x)
{
return x.GetHashCode();
}
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
if (names.Length != 1)
throw new InvalidOperationException("names array has more than one element. can't handle this!");
var val = rs[names[0]] as string;
if (string.IsNullOrWhiteSpace(val) == false)
{
return KRD.Common.GenericXmlSerialization.Deserialize<T>(val);
}
return null;
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
var parameter = (DbParameter)cmd.Parameters[index];
T toSave = value as T;
if (toSave != null)
{
parameter.Value = KRD.Common.GenericXmlSerialization.Serialize(toSave);
}
else
{
parameter.Value = DBNull.Value;
}
}
public object DeepCopy(object value)
{
T toCopy = value as T;
if (toCopy == null)
return null;
string serialized = KRD.Common.GenericXmlSerialization.Serialize(toCopy);
return KRD.Common.GenericXmlSerialization.Deserialize<T>(serialized);
}
public object Replace(object original, object target, object owner)
{
throw new NotImplementedException();
}
public object Assemble(object cached, object owner)
{
var str = cached as string;
if (string.IsNullOrWhiteSpace(str) == false)
{
return null;
}
return KRD.Common.GenericXmlSerialization.Deserialize<T>(str);
}
public object Disassemble(object value)
{
var toCache = value as T;
if (toCache != null)
{
return KRD.Common.GenericXmlSerialization.Serialize(toCache);
}
return null;
}
public SqlType[] SqlTypes
{
get
{
return new SqlType[] { new SqlXmlType() };
}
}
public Type ReturnedType
{
get { return typeof(XmlDocument); }
}
public bool IsMutable
{
get { return true; }
}
}
public class SqlXmlType : SqlType
{
public SqlXmlType()
: base(DbType.Xml)
{
}
}
Usage with FluentNHibernate:
public class MainObject
{
public int Id { get; set; }
public ObjectAsXml Data { get; set; }
}
public class ObjectAsXml
{
public string Name { get; set; }
public int Date { get; set; }
public ObjectAsXml OtherObject { get; set; }
}
private class MainObjectMap : ClassMap<MainObject>
{
public MainObjectMap()
{
Id(id => id.Id);
Map(m => m.Data).CustomType<XmlUserType<ObjectAsXml>>().Nullable();
}
}
Maybe it will help somebody.