I'm trying to decide how to handle domain-level classes and interfaces with respect to objects populated via ORM (NHibernate in this case). I've mocked up a very simple scenario that helps to illustrate my current situation.
public interface ICar
{
public bool PassesEmisionStandards(string state);
public int Horsepower { get; set; }
public string Model { get; set; }
}
public class CarFromDB
{
public int Horsepower { get; set; }
public string Model { get; set; }
}
public class ModelT : CarFromDB, ICar
{
public bool PassesEmissionStandards(string state)
{
return false;
}
public override string ToString()
{
return Model + " with " + Horsepower + " ponies";
}
}
In this case, CarFromDB is the class that's got the mapping via NHibernate to my database. ICar is the interface that my UI/Controller code is handling. ModelT is one of the classes that have instances passed to the UI.
In my actual domain, the PassesEmissionStandards is a complicated method that differs significantly among the different derived classes, and the CarFromDB class has a dozen simple properties along with references to other classes, both singly and in lists. This information is all used in the PassesEmissionStandards equivalent.
I'm confused about the best way to end up with my derived classes decorated with the interface when I start with a populated base class from the ORM. The ideas I've come up with to try to handle this are:
Decorate CarFromDB with ICar and try to come up with a clean way to implement the extensive PassesEmissionStandards method within it or by calling out to other classes for help
Use AutoMapper or the equivalent + a factory to transform my base class objects into derived class objects
Since the derived class type can be identified from a property in the base class, mapp my derived classes for NHibernate and find some way to hook into NHibernate to instruct it which mapped derived class to use.
I feel like this must be a very common issue, but I searched around SO and other places without finding any solid guidelines. Please note: I'm relatively new to ORM and domain modelling and very new to NHibernate. Any help is appreciated.
I don't think that I understand your problem, why canĀ“t you use:
public interface ICar
{
public bool PassesEmisionStandards(string state);
public int Horsepower { get; set; }
public string Model { get; set; }
}
public abstract class CarBase : ICar
{
public int Horsepower { get; set; }
public string Model { get; set; }
public abstract bool PassesEmisionStandards(string state);
}
Or if CarBase is used for all derived classes too, you might want to use strategy pattern
public interface IEmissionCalculator
{
void Calculate(IEmissionCalculatorContext context);
}
public CarBase : ICar
{
internal void Assign(IEmissionCalculator calculator){}
public bool PassesEmisionStandards(string state)
{
//assign all info needed for calculations
var ctx = new IEmissionCalculatorContext { };
return _calculator.Check(ctx);
}
}
You can use the same DB-class, but assign different emission caluclations depending of the type of car.
If that doesn't work either, I would use automapper.
Related
As you know, C# 9.0 (.Net 5) now allows Covariant Returns. I need help applying this to a set of classes having Auto-Implemented properties.
I have two abstract classes that represent financial bank accounts and transactions. I made them abstract since I will pull data from various data sources and while the main properties will be common across all sources, each source may have additional fields I want to keep. A 1 to Many relationship exists between both classes (1 account has many transactions AND 1 transaction belongs to only 1 account).
public abstract class BankAccount
{
public string Name { get; set; }
public IList<Transaction> Transactions { get; set; } = new List<Transaction>();
...
}
public abstract class Transaction
{
public string Name { get; set; }
public virtual BankAccount BankAccount { get; set; } // This doesn't work unless I remove set;
...
}
And here is an example of the concrete implementations
public class PlaidBankAccount : BankAccount
{
public string PlaidId { get; set; }
...
}
public class PlaidTransaction : Transaction
{
public string PlaidId { get; set; }
public override PlaidBankAccount BankAccount { get; set; } // This doesn't work unless I remove set;
...
}
What I want to do is to override the base class getters and setters so that they use derived classes. For example:
If I create an instance of the concrete transaction and call the BankAccount getter, I want to get an instance of the derived PlaidBankAccount not the base BankAccount.
What I've found is that when I only define virtual getter in the base class and override it in the derived class, it works. But just as I add both properties {get;set;}, I get the same error as in previous C# versions:
error CS1715: 'PlaidTransaction.BankAccount': type must be 'BankAccount' to match overridden member 'Transaction.BankAccount'
How could I fix this?
In C# 9 properties are only able to have co-variant returns when they are readonly, so unfortunately, no set; is possible.
An overriding property declaration must specify exactly the same access modifier, type, and name as the inherited property. Beginning with C# 9.0, read-only overriding properties support covariant return types. The overridden property must be virtual, abstract, or override.
From the Microsoft Docs - Override keyword
I should implement strategy design pattern and I was thinking about what the right way is. Let's say I have the following example below.
I know Discriminator is able to do that on EF Core level but I should take off the inheritance for that case (Mapping inheritance in EntityFramework Core). What is the best practice? If you look in that other thread I linked, there is a solution with HasDiscriminator but I don't know about the inheritance.
public abstract class Strategy
{
public int Id { get; set; }
public string Name { get; set; }
public abstract void SomeLogic();
}
public class FirstStrategy : Strategy
{
public string CustomField { get; set; }
public override void SomeLogic()
{
throw new NotImplementedException();
}
}
public class SecondStrategy : Strategy
{
public int CustomValue { get; set; }
public override void SomeLogic()
{
throw new NotImplementedException();
}
}
The strategy pattern is about behavior, while Entity Framework Core deals with data.
To persist an inheritance hierarchy with EF it should be fine sticking with the default pattern without customizing the discriminator (if that is what you want to avoid). See Inheritance for further information.
There is a NODES table with dozen of 'small' columns and a LOB column in a legacy DB. A NodeEntity class is mapped to the NODES table.
For performance purposes I do not want to load LOB column every time I access the DB. I know two approaches to achieve this:
Lazy loaded properties
Separate entity class (the idea is taken from here)
Lazy loaded properties are good when you only loading data from DB. But if you have to save entities then there is a risk to lose your data if you forget to fetch lazy loaded properties beforehand.
So I chose the second approach.
I created separate small NodeEntityLite class with properties mapped to non-LOB columns of NODES table. I modified NodeEntity class so it inherits from NodeEntityLite class. I changed the mappings for my classes and used union-subclass for inheritance.
public class NodeEntityLite {
public virtual long Id { get; set; }
public virtual string Code { get; set; }
}
public class NodeEntity : NodeEntityLite {
public virtual string NOTE { get; set; } // type:clob
}
FluentNHibernate mapping for NodeEntityLite class is
public void Override(AutoMapping<NodeEntityLite> mapping) {
mapping.Table("NODES");
mapping.UseUnionSubclassForInheritanceMapping();
}
FluentNHibernate mapping for NodeEntity class is
public void Override(AutoMapping<NodeEntity> mapping) {
mapping.Table("NODES");
mapping.Map(e => e.NOTE).CustomType("StringClob").CustomSqlType("NCLOB");
}
I expected that when I execute select n from NodeEntityLite n where n.Id = :p0 HQL then NHibernate generates SQL commands without NOTE column:
select nodeentity0_.ID as id1_87_,
nodeentity0_.CODE as code2_87_
from from NODES nodeentity0_
where nodeentity0_.ID=:p0;
But NHibernate generates absolutely different SQL command (NOTE column is not skipped as I expected):
select nodeentity0_.ID as id1_87_,
nodeentity0_.CODE as code2_87_,
nodeentity0_.NOTE as note14_87_,
nodeentity0_.clazz_ as clazz_
from ( select ID, CODE, NOTE, 1 as clazz_ from NODES ) nodeentity0_
where nodeentity0_.ID=:p0;
I tried to change inheritance and to use other mappings but without success.
The question is: Can I map several classes to the same table in NHibernate to get access to different columns?
If yes, please give an example.
The solution (based on the suggestions from David Osborne and mxmissile) is not to use inheritance. I use common interface implementation instead of class inheritance. The working code is below:
public interface INodeLite {
long Id { get; set; }
string Code { get; set; }
}
public class NodeEntityLite : INodeLite {
public virtual long Id { get; set; }
public virtual string Code { get; set; }
}
public class NodeEntity : INodeLite {
public virtual long Id { get; set; }
public virtual string Code { get; set; }
public virtual string NOTE { get; set; } // type:clob
}
...
public void Override(AutoMapping<NodeEntityLite> mapping) {
mapping.Table("NODES");
}
...
public void Override(AutoMapping<NodeEntity> mapping) {
mapping.Table("NODES");
mapping.Map(e => e.NOTE).CustomType("StringClob").CustomSqlType("NCLOB");
}
Regardless of the inheritance, NH can map different types to the same table. I have done it, albeit without inheritance.
You should be able to remove this line from the NodeEntityLite override and achieve it:
mapping.UseUnionSubclassForInheritanceMapping();
If this proves unsuccessful, you might need to tune the automapping further. It's definitely possible though.
I have designed my classes with this hierarchy to achieve
RepositoryBase to have some functionalities that can be used by derived classes.
The derived classes implement validation and saving.
Use the IRepository interface (rather than numerous customer interfaces) to register with a IoC container to get the instance of the repository, for example CustomerRepository in this example.
The problem that I have with this design is that the customer object is not known at the construction of the CustomerRepository. It has to be set before save.
How can I redesign this hierarchy to achieve the three that mentioned at the beginning of this question?
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public void string GetFullName();
}
public interface IRepository
{
void Save();
}
public abstract class RepositoryBase<T> : IRepository
{
public abstract bool IsValid();
}
public class CustomerRepository : RepositoryBase<Customer>
{
public void Save()
{
IsValid(); // Check for validation
customer.GetFullName(); // Generate the FullName before saving
// Save the data using some RepositorBase method.
}
}
Which entity FluentNHibernate uses as entity
I create some entity in Domain(or BLL), such as the following:
public class Role
{
public long ID { get; protected set; }
public string Name { get; set; }
public string Description { get; set; }
public List<User> Users { get; set; }
public Role()
{
Users = new List<User>();
}
}
And I want make use of FlunetNHibernate to map them, but get errors:
The following types may not be used as proxies:
Freeflying.Domain.Core.Profile: method get_ID should be 'public/protected virtual' or 'protected internal virtual'
Yes, I recall the programmer requirement when use FluentNHibernate, the entity should be like this:
public class Role
{
public virtual long ID { get; protected set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
}
But It looks wired. Do you think so? How do you do when using FluentNHibernate? I don't want go back to Domain layer and add virtual for every property.
This is a basic requirement for using NHibernate; It allows NHibernate to generate a proxy class that descends from your class for lazy loading and such.
I have not seen a method of removing this requirement, though if such a thing is possible it would mean that you could not use lazy loading of objects and/or properties.
Here's a blog post that explains this a bit more; It also offers a way to avoid marking your properties as virtual, although I would really recommend that you do not use this method (marking classes to avoid lazy loading) as the benefits of lazy loading in most circumstances far outweigh the cost of making your properties virtual.