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);
Related
I need some assistance in learning how DTO's and Extension methods work with Many to Many relationships in EF5 Code First MVC4.
I have the following DTO's
using System;
using System.Collections.Generic;
namespace Mirtol.Web.Models
{
public class TaskDetail
{
public DateTime DueDate {get;set;}
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Status { get; set; }
public string Phase { get; set; }
public string Project { get; set; }
public DateTime StartDate { get; set; }
public string overdue { get; set; }
public List<GroupDetail> Groups { get; set; }
}
}
using System;
namespace Mirtol.Web.Models
{
public class GroupDetail
{
public string GroupName {get;set;}
public string GroupDescription { get; set; }
}
}
On top of these I have The following extensions;
using Mirtol.Web.Models;
using System.Collections;
using System.Collections.Generic;
namespace Mirtol.Web.Extensions
{
public static class TaskDetailExtensions
{
public static TaskDetail ToTaskDetail(this Task task)
{
var success = "success";
var warning = "warning";
var error = "error";
return new TaskDetail
{
Id = task.Id,
Name = task.Name,
Description = task.Description,
Status = task.Status,
Phase = task.Phase != null ? task.Phase.Name : string.Empty,
Project = task.Project != null ? task.Project.ShortName : string.Empty,
StartDate = task.StartDateTime,
DueDate = task.DueDate,
overdue = task.DueDate > System.DateTime.Now ? success : task.DueDate < System.DateTime.Now.AddDays(-7) ? error : warning,
};
}
}
}
using Mirtol.Entities.Mir;
using Mirtol.Web.Models;
namespace Mirtol.Web.Extensions
{
public static class GroupDetailExtensions
{
public static GroupDetail ToGroupDetail(this Group group)
{
return new GroupDetail
{
GroupName = group.Name != null ? group.Name: string.Empty,
GroupDescription = group.Description !=null ? group.Description: string.Empty,
};
}
}
}
So my tasks controller has an action;
public IEnumerable<TaskDetail> GetUserProjectTasks(int id, string ustr, int uid)
{
var projectUTasks = taskRepository.GetUserProjectTasks(id, ustr, mirtolSecurity.CurrentUserId).Distinct();
return projectUTasks.OrderByDescending(x => x.DueDate).Select(x => x.ToTaskDetail());
}
which returns toTaskDetail. and includes "Groups"
What I am struggling with is in TaskDetailExtension and TaskDetaail, how do I reflect the fact that a task can belong to many groups and groups can have many tasks? CF takes care of this on my entity classes and the relationships seem to be set up fine there.
My thought was something like
Groups = task.Groups(x => x.Groups.ToGroupDetail()),
in the TaskDetailExtension ?
Any help appreciated.
John
I am going to assume that your Task has a IEnumerable called Groups.
You need two extension methods in these kind of situations. One that will convert your child model and another that will convert the list of your child models.
public static GroupDetail ToGroupDetail(this Group group)
{
return new GroupDetail { .. your mappings };
}
public static IEnumerable<GroupDetail> ToGroupDetailList(this IEnumerable<Group> groups)
{
return groups.Select(g => g.ToGroupDetail());
}
Then include following line in your ToTaskDetail after other properties.
Groups = task.Groups.ToGroupDetailList()
If the task.Groups is a IQueriable you need to change the ToGroupDetailList extension method to take a IQueriable instead of IEnumerable. Hope this helps
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.
I am having an issue with using Fluent NHibernate automapping with Inheritance. Below is my entity setup (abbreviated for simplicity). I have configured Fluent NHibernate to create 1 class for the hierarchy with a discriminator column. The automapping appears to be working correctly as when I generate a database, one table is created named "AddressBase" with a discriminator column that signals what type of address each row is.
The problem lies in the face that when I call the method "GetPrimaryBillingAddress()" on the UserAccount class, instead of just querying Billing Addresses, NHibernate is creating a query that looks at both Billing and Shipping Addresses. It doesn't take into account the discriminator at all. I am assuming there is some sort of configuration I can set but have not been able to find anything.
public abstract class AddressBase : ActiveRecord<AddressBase>
{
public virtual long Id { get; set; }
public virtual string Address1 { get; set; }
}
public class AddressBilling : AddressBase
{
public class TypedQuery : ActiveRecordQuery<AddressBilling> { }
public virtual bool IsPrimary { get; set; }
}
public class AddressShipping : AddressBase
{
public class TypedQuery : ActiveRecordQuery<AddressShipping> { }
[Display(Name = "Is Primary")]
public virtual bool IsPrimary { get; set; }
}
public class UserAccount : ActiveRecord<UserAccount>
{
public virtual long Id { get; set; }
public virtual IList<AddressBilling> BillingAddresses { get; set; }
public virtual IList<AddressShipping> ShippingAddresses { get; set; }
public UserAccount()
{
BillingAddresses = new List<AddressBilling>();
ShippingAddresses = new List<AddressShipping>();
}
public virtual AddressBilling GetPrimaryBillingAddress()
{
if (BillingAddresses.Any(x => x.IsPrimary))
{
return BillingAddresses.Single(x => x.IsPrimary);
}
return BillingAddresses.FirstOrDefault();
}
public virtual AddressShipping GetPrimaryShippingAddress()
{
if (ShippingAddresses.Any(x => x.IsPrimary)) {
return ShippingAddresses.Single(x => x.IsPrimary);
}
return ShippingAddresses.FirstOrDefault();
}
}
UPDATE:
Here is the Mapping override functions used in the automapping:
private static FluentConfiguration GetFluentConfiguration(string connectionStringName = "CS")
{
var autoMapping = AutoMap
.AssemblyOf<Product>(new Mapping.AutoMappingConfiguration())
.Conventions.Setup(c =>
{
c.Add<Mapping.ForeignKeyConvention>();
c.Add<Mapping.DiscriminatorConvention>();
})
.IgnoreBase<AddressBilling.TypedQuery>()
.IgnoreBase<AddressShipping.TypedQuery>()
.IncludeBase<AddressBase>();
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005.ConnectionString(c => c.FromConnectionStringWithKey(connectionStringName)))
.Mappings(m => m.AutoMappings.Add(autoMapping));
}
public class AutoMappingConfiguration : DefaultAutomappingConfiguration
{
public override bool ShouldMap(Type type)
{
var isStatic = type.IsAbstract && type.IsSealed;
return type.Namespace == typeof(Entities.Product).Namespace && !isStatic;
}
public override bool IsDiscriminated(Type type)
{
if (type == (typeof(Entities.AddressBase))) {
return true;
}
return false;
}
public override string GetDiscriminatorColumn(Type type)
{
return "Type";
}
public class DiscriminatorConvention : ISubclassConvention
{
public void Apply(ISubclassInstance instance)
{
//Address
if (instance.Name == typeof(AddressBilling).AssemblyQualifiedName)
{
instance.DiscriminatorValue(Enums.AddressType.BillingAddress);
}
else if (instance.Name == typeof(AddressShipping).AssemblyQualifiedName)
{
instance.DiscriminatorValue(Enums.AddressType.ShippingAddress);
}
}
}
Thanks!
Please, try to change your class UserAccount like this:
public class UserAccount : ActiveRecord<UserAccount>
{
public virtual IList<AddressBase> Addresses { get; set; }
public virtual IList<AddressBilling> BillingAddresses { get {return this.Addresses.OfType<AddressBilling>();} }
public virtual IList<AddressShipping> ShippingAddresses { get {return this.Addresses.OfType<AddressShipping>();} }
// ...
}
Of course, only Addresses property should be mapped here.
I am using the Specification pattern, and have a working implementation (taken from the WhoCanHelpMe Codeplex project) for getting data via NLinq, generic repositories and all that goodness.
The root method is:
public IList<Case> GetCasesByUsername(string username)
{
CaseByUserNameSpecification spc = new CaseByUserNameSpecification(username);
return this.caseRepository.FindAll(spc).ToList();
}
The FindAll() method does the following:
public IQueryable<T> FindAll(ILinqSpecification<T, T> specification)
{
return specification.SatisfyingElementsFrom(this.Session.Linq<T>());
}
And, SatisfyingElementsFrom() does this:
public virtual IQueryable<TResult> SatisfyingElementsFrom(IQueryable<T> candidates)
{
if (this.MatchingCriteria != null)
{
return candidates.Where(this.MatchingCriteria).ToList().ConvertAll(this.ResultMap).AsQueryable();
}
return candidates.ToList().ConvertAll(this.ResultMap).AsQueryable();
}
So, for querying cases by CaseNb property of a Case, it's pretty straight-forward. A Specification like the one below works for me and gets the cases I'd want.
public class CaseByCaseNbSpecification : QuerySpecification<User>
{
private string caseNb;
public CaseByCaseNbSpecification(string caseNb)
{
this.caseNb = caseNb;
}
public string UserName
{
get { return this.caseNb; }
}
public override Expression<Func<Case, bool>> MatchingCriteria
{
get { return u => u.CaseNb.Equals(this.caseNb, StringComparison.CurrentCultureIgnoreCase); }
}
}
However, I am at a loss to understand how to do this when crossing multiple entities. What I'd like to have is a Specification that allows me to get Cases by UserName. Basically, in the database, there are three tables and these have been carried into entities. Here's are entities:
Here's the Case class:
public class Case : Entity
{
private ICollection<CaseUser> caseUsers = new HashSet<CaseUser>();
public virtual Patient Patient { get; set; }
public virtual string CaseNb { get; set; }
...
public virtual IEnumerable<CaseUser> CaseUsers { get { return caseUsers; } }
}
Here's the CaseUser:
public class CaseUser : Entity
{
public virtual Case Case { get; set; }
public virtual User User { get; set; }
...
}
And, User:
public class User : Entity
{
private ICollection<CaseUser> caseUsers = new HashSet<CaseUser>();
public virtual Account Account { get; set; }
public virtual string UserName { get; set; }
...
public virtual IEnumerable<CaseUser> CaseUsers { get { return caseUsers; } }
}
How would I write the Expression to get the data across the association table?
I believe your specification implementation should look something like this:
public class CaseByUsernameSpecification : QuerySpecification<Case>
{
private string userName;
public CaseByUsernameSpecification(string userName)
{
this.userName = userName;
}
public string UserName
{
get { return this.userName; }
}
public override Expression<Func<Case, bool>> MatchingCriteria
{
get { return c => c.CaseUsers.Any(cu => cu.User.Username == this.userName); }
}
}
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.