Nhibernate project into class property - c#

How can I project into class property using NHibernate? For example:
[Test]
public void Test()
{
MyClass dto = null;
var test = CurrentSession.CreateCriteria<Contact>()
.Add(Restrictions.Eq("ContactName", "John Smith"))
.SetProjection(Projections.ProjectionList()
.Add(Projections.Property("ContactName").WithAlias(() => dto.SubClass.Name))
.Add(Projections.Property("EmailAddress").WithAlias(() => dto.Email))
)
.SetResultTransformer(Transformers.AliasToBean<MyClass>())
.List<MyClass>();
Assert.That(test[0].SubClass.Name, Is.EqualTo("John Smith"));
}
class MyClass
{
public string Email { get; set; }
public MySubClass SubClass { get; set; }
}
class MySubClass
{
public string Name { get; set; }
}
As you see I have a simple query and want to transform 1 row into 1 object - without lists but with a subclass. Unfortunately, it fails:
NHibernate.PropertyNotFoundException : Could not find a setter for property 'Name' in class 'MyTest+MyClass'
Is it possible to achieve this behaviour without custom transformer?

The default result transformer will be able to fill the root entity properties. But we can introduce our custom result transformer. There is one I do use:
DeepTransformer<TEntity> : IResultTransformer
Which is ready to convert . notation into chain of inner objects (excluding collections)
So, if you'll take it, and reuse it, this syntax would work:
...
.SetProjection(Projections.ProjectionList()
.Add(Projections.Property("ContactName").As("SubClass.Name"))
.Add(Projections.Property("EmailAddress").As("Email"))
)
.SetResultTransformer(DeepTransformer<MyClass>())
You can even improve it, but the idea of custom transformer should be clear now

Related

EF Core, include derived type property

I have a small problem with a specific include statement.
My datastructure is as follows:
[Table("item")]
public class Item
{
public int Id { get; set; }
public string ItemCode { get; set; }
...
}
public abstract class DerivedItemAbstractBase : Item
{
[ForeignKey("ItemId")]
public List<Assignment> Assignments { get; set; }
...
}
[Table("item")]
public class DerivedItemA : DerivedItemAbstractBase
{
...
}
[Table("item")]
public class DerivedItemB : DerivedItemAbstractBase
{
...
}
public class ItemContext : DbContext
{
public DbSet<Item> Items { get; set; }
...
}
Now I want to get a list of all DerivedItemA and include properties of it.
I have the following method:
public List<DerivedItemA> GetDerivedItemsA()
{
var list = _context.Items
.Include(x => (x as DerivedItemA).Assignments)
.ToList();
}
This code compiles just fine and is something I have found on stackoverflow.
However executing this results in an exception with the short message Invalid include.
I dont know how to solve this problem.
The project is a database-first approach so I have no control over the database.
All items are stored in the same table item.
I cannot create multiple DbSets because there is no discriminator column in the table and I
cannot configure a custom discriminator in code because it would need to discriminate based on multiple properties and not a single property.
Is there any other way of doing this?
Currently I am solving it by iterating through all ItemContext.Items then doing a .Select() on each and creating a new DerivedItemA. After that I manually set every Assignment by iterating from the Assignment table.
However this approach takes far too long and it would be a lot quicker if I could just include it in the initial query.

How to solve "Expected element name to be '_t', not 'number'."

I have a mongo model like this:
class ObjectA {
[BsonId(IdGenerator = typeof(BsonObjectIdGenerator))]
public BsonObjectId Id;
[BsonElement("number")]
public int Number { get; set; }
[BsonElement("b")]
public List<ObjectB> objectB { get; set; }
}
class ObjectB {
[BsonElement("someProperty")]
public string SomeProperty { get; set; }
}
My problem is when I aggregate the collection with {$unwind:objectB}. The result documencts have a unique object on the property objectB (not a list).
So the cast failes with the exception:
An error occurred while deserializing the ObjectB property of class
ObjectA: Expected element name to be '_t', not
'number'.
Do I have to create a new model for this or is there a easier way to solve it?
You could also choose to work with BsonDocument directly (but that is not strongly typed and more cumbersome to work with), e.g. (I'm using the simple Posts/Tags example here)
var aggregationResults = db.GetCollection("Posts").Aggregate().ResultDocuments;
foreach (var document in aggregationResults)
{
var tag = document.GetValue("Tags").AsString;
}
Unlike the normal query and projection operators, the aggregation framework may change the structure of your document. As you already pointed out, $unwind transforms a document that contains an array into a number of documents that each have a single value of the same name.
Another approach this is to indeed create a new type for this, so
class Post {
public List<string> Tags { get; set; }
...
would become
class PostAggregationResult {
public string Tags { get; set; }
...
That is very easy to work with, but if you have very various aggregation queries, you need a large number of classes which can be annoying.

c# LINQ to XML test data loading

I'm trying to set up a Test Harness and I have some data I want to include in my project in XML format and load that into the business objects in the test pre-setup method.
The class structure is
public class DbUserChoice
{
public int Id { get; set; }
public string Description { get; set; }
}
public class DbUserAmbition : DbUserChoice { }
public class DbUserDiet : DbUserChoice { }
public class DbUserEthnicity : DbUserChoice { }
etc...
So there is an abstract base class DbUserChoice that is then extended by all the different types of choices a user has (15 in total). All of these except one add nothing to the class, just extending it verbatim.
The XML structure is (partial)
<UserChoiceOptions>
<UserChoice ChoiceType="DbUserAmbition">
<Choice>I'm content to just sit back and enjoy life</Choice>
<Choice>I have a few ambitions and dreams but keep my feet on the ground</Choice>
<Choice>I'm quite ambitious and driven in my career and personal life</Choice>
<Choice>I'm extremely driven to succeed and want the very best from life</Choice>
</UserChoice>
<UserChoice ChoiceType="DbUserBodyType">
<Choice Gender="M">Slim</Choice>
<Choice Gender="M">Athletic and toned</Choice>
<Choice Gender="M">A healthy medium</Choice>
<Choice Gender="M">Muscular</Choice> etc...
I want some kind of generic method that I can pass a DataType to, and it returns me an IQueryable of the choices that map to that datatype in the XML, chosen by the "ChoiceType" discriminator attribute on the UserChoice node above.
Eg
var ambitions = TestUtil.ReadXMLObjects<DbUserAmbition>(xmlFilePath);
would return me an IQueryable<DbUserAmbition> with the above 4 options
14 of the 15 user choice types would behave in this way. The only one different is the DbUserBodyType one also mentioned above, as you can see this has an additional attribute on each record of Gender. This is a new property on the DbUserBodyType class, it is the only derived class that adds a new record to the base class, and this woudl also need to be popualted from that XML attribute.
I've been trying to achieve this using Linq to XML but I can't seem to quite get it right. The following code gives me a list of XElements but I can't see how to easily and cleanly translate that to a list of C# DbUserAmbition objects for example without using messy reflection.
var element = XElement.Load(_xmlPath);
var typeName = typeof(T).Name;
var nodes = from n in element.Elements("UserChoiceOptions/UserChoice/Choice")
where n.Parent.Attribute("ChoiceType").Value.Equals(typeName, StringComparison.CurrentCultureIgnoreCase)
select n;
Any advice would be welcome
Well, you can achive this by declaring a method which would parse data from xml:
public class DbUserChoice
{
public int Id { get; set; }
public string Description { get; set; }
public virtual void Parse(XElement node)
{
//assign properties from XElement here
//override this method in DbUserBodyType to add additional logic
}
}
Then your query will end with:
return nodes.Select(n => { var x = new T(); x.Parse(n); return x; }).AsQueryable();
This will add a restriction T : DbUserChoice, new() to your generic method, but that should not be a problem

How to create the correct inherited class based on what is stored in the database

What Utility or Pattern can be used to solve this Issue? I don't know what can be used to assist with this. Can we use some type of pattern?
If you have the following abstract class:
abstract class Foo
{
function void Something()
{
// Get the media type
}
}
And the following classes that derive from that class:
class Foo1 : Foo
{
public string MyId {get;set}
public string MyFile {get;set}
public TxtFile MyTextFile {get;set}
function void myFooFunction()
{
// Save File to Txt
}
}
class Foo2 : Foo
{
public string MyId {get;set}
public string MyFile {get;set}
public XMLFile MyXMLFile {get;set}
function MyOtherFunction()
{
// Save to XML
}
}
Then in Linq (or Similar) within the repository you do something like this:
var a = (from e in db.myTable
where e.myFileType == "XML"
Select e);
Then we have to map this to the correct object. Like this:
Foo newFoo = FooFactory.CreateFooFor(a.myFileType.ToString())
newFoo.MyId = a.id;
newFoo.MyFile = a.myfile;
newFoo.MyXMLFile = a.xml;
The Factory certainly helps, but how do you do this for multiple "FileTypes" like txt for example? The Fields wouldn't match up!
Do I have to write more code that does the same thing??
I feel like there has to be something that can do this.
First, if myFooFunction and MyOtherFunction are both used to save, you can use the strategy pattern and just define and abstract Save() method to implement in derived classes. You might also look at the Template Method pattern.
Although this isn't exactly a pattern, you might also want to apply the "Pull Up Field" refactoring here and put the MyId and MyFile properties in the parent class.
For creation...
The Builder pattern is similar to the factory, but allows for more complex object creation. I don't know how well it fits this simplified example, but it might fit what you are actually trying to do in your real code. Probably not. I just mention it first because it is the closest to factory in my mind.
There are also the Mapper Pattern and the Data Mapper Pattern. You might encapsulate the mapping in an object and have the factory return a mapper:
FooMapper mapper = FooMapperFactory.CreateFooMapperFor(a.myFileType);
Foo newFoo = mapper.CreateFoo(a);
I believe you could solve your problem using generics. I took the liberty of changing around some code. Would this work?
public abstract class Foo
{
public abstract void Save();
public void Something()
{
// Get the media type
}
}
public class FooText : Foo
{
public string MyId { get; set; }
public string MyFile { get; set; }
public string MyTextFile { get; set; }
public override void Save()
{
// Save File to Txt
}
}
public class FooXml : Foo
{
public string MyId { get; set; }
public string MyFile { get; set; }
public string MyXMLFile { get; set; }
public override void Save()
{
// Save to XML
}
}
public class FooFactory<T> where T : Foo, new()
{
public static T CreateFoo()
{
return new T();
}
}
If you consider using reflection on the data that's returned from the database, or perhaps the Adapter pattern you can set up a dynamic way to map fields to each other. Using reflection (the following is pseudo logic as reflection is kind of messy code to provide):
Get a list of PropertyInfo objects for all public properties from the target type
Get a list of PropertyInfo objects for all public properties from the database type
Compare their names/types to create the mapping
Assign values from the database type, using reflection, to the target type
Something like this will do the trick:
public void AssignAndMapTypes<DatabaseType, TargetType>(DatabaseType db, ref TargetType target)
{
var dbType = db.GetType();
var dbTypeProperties = dbType.GetProperties(System.Reflection.BindingFlags.Public);
var targetType = target.GetType();
var targetTypeProperties = targetType.GetProperties(System.Reflection.BindingFlags.Public);
foreach (var prop in targetTypeProperties)
{
var matchingProp = dbTypeProperties.Where(e => { return (string.Compare(e.Name, prop.Name, true) == 0) && (e.PropertyType == prop.PropertyType) }).FirstOrDefault();
if(matchingProp != null)
{
prop.SetValue(target, matchingProp.GetValue(db, null), null);
}
}
}

Get collection property of a specific type

I have a class MyDatabaseContext that has a series of DbSet collection properties:
public DbSet<EntityA> EntitiesA { get; set; }
public DbSet<EntityB> EntitiesB { get; set; }
public DbSet<EntityC> EntitiesC { get; set; }
I need to get the name of the collection given the type of the entity.
For example, I have "EntityB" and want to get as a result "EntitiesB".
I really wanted to avoid switch-case statements, since MyDatabaseContext is generated automatically (T4 templates).
if you just want the name of the property here you go. I would just refine the answer given by hunter. You can use the same method with string as return type.
public string GetEntitiName<T>() where T : class
{
PropertyInfo propInfo = typeof(MyDatabaseContext).GetProperties().Where(p => p.PropertyType == typeof(DbSet<T>)).FirstOrDefault();
string propertyName = propInfo.Name; //The string has the property name ..
return propertyName;
}
I tried a sample similar to your situation. Try replacing List with DbSet.
class Program
{
public static void GetEntities<T>() where T : class
{
var info = typeof(TestClass1).GetProperties().Where(p => p.PropertyType == typeof(List<T>));
Console.WriteLine(info.FirstOrDefault().Name);
}
static void Main(string[] args)
{
GetEntities<int>();
Console.ReadLine();
}
}
public class TestClass1
{
public List<int> IntTest { get; set; }
public List<double> DoubleTest { get; set; }
public List<string> IStringTest { get; set; }
}
This sample works.
I know this is old page, But my answer maybe useful for other guys referring here. (like me)
I think you want to accessing EntitiesB to run a query on it, like EntitiesB.Where(a=>a.bla=="blabla"). If I'm right or another visitor of this page needs something like this, just easily use the following code:
using System.Data.Entity.Infrastructure;
using System.Data.Objects;
((IObjectContextAdapter)_dbContext).ObjectContext.CreateObjectSet<EntityB>()
Description:
_dbContext is Context class inherting from DbContext.
EntitiesB is DbSet<EntityB> defined in Context class.
Example:
Ilist result = ((IObjectContextAdapter)_dbContext).ObjectContext.CreateObjectSet<EntityB>().Where(b=>b.bla=="blabla").ToList();
Your generated file is a partial class, you could create a new file and declare a class with same name using the keyword partial, then make a method which will return the desired Collection...
I haven't actually done this myself, but it sounds like what you want to do is to use reflection to locate the property of type "DbSet" that has the appropriate generic type parameter. The following pseudo-C# should get you started:
foreach ( FieldInfo field in this.GetType() )
{
if ( field.FieldType.IsGenericType )
{
foreach ( Type param in field.FieldType.GetGenericArguments() )
{
if ( param.Name == soughtType )
{
return field.Name;
}
}
}
}

Categories