I have these classes:
class Car {
int ID;
string Name;
}
class Truck : Car {
int MaximumLoad;
}
I have these tables
Car
- ID
- Name
Truck
- CarID
- MaximumLoad
How could I map my classes with my table using PetaPoco ?
If you store Car and Truck in the same table (TPH) you can inherit Truck from Car with minor changes to PetaPOCO source code,
table Vehicle (ID, Discriminator, Name, MaximumLoad)
in PetaPOCO.cs, add
[AttributeUsage(AttributeTargets.Class)]
public class DiscriminatorAttribute : Attribute
{
public int Value { get; private set; }
public DiscriminatorAttribute(object discriminator)
{
Value = (int)discriminator;
}
}
protected bool IsTPHTable<T>()
{
var t = typeof(T);
var a = t.GetCustomAttributes(typeof(DiscriminatorAttribute), true);
return a.Length > 0;
}
protected void AppendDiscriminator<T>(Sql sql)
{
var t = typeof(T);
var a = t.GetCustomAttributes(typeof(DiscriminatorAttribute), true);
sql.Append("Where Discriminator = #0", (a[0] as DiscriminatorAttribute).Value);
}
public IEnumerable<T> Query<T>(Sql sql)
{
if (IsTPHTable<T>())
AppendDiscriminator<T>(sql);
return Query<T>(default(T), sql);
}
// also similar AppendDiscriminator() for Update an Delete.
Then in your Car.cs and Truck.cs, you can write/generate code like this,
public enum VehicleType:int
{
Car,
Truck
}
[TableName("Vehicle")]
[Discriminator(VehicleType.Car)]
public class Car
{
[Column]
public int ID {get; set;}
[Column]
public string Name {get; set;}
public Car()
{
//this.Discriminator = VehicleType.Car;
}
public static new Car SingleOrDefault(object primaryKey) { return repo.SingleOrDefaultById<Car>(primaryKey); }
//...
}
[Discriminator(VehicleType.Truck)]
public class Truck:Car
{
[Column]
public double MaximumLoad {get;set;}
public Truck()
{
//this.Discriminator = VehicleType.Truck;
}
public static new Truck SingleOrDefault(object primaryKey) { return repo.SingleOrDefaultById<Truck>(primaryKey); }
//...
}
To read truck records I would create a Trucks view that combines the two tables. Or have a look at Schotime's muliple result sets :
http://schotime.net/blog/index.php/2011/11/20/petapoco-multiple-result-sets/
For writes I guess you are asking "how can I write to 2 tables in one operation". Off the top of my head I would probably say I would simply perform 2 writes. I think Petapoco will ignore fields that don't map so you may be able to use your truck object for both writes.
Could easily be wrong as I haven't tested this.
I think (have not tested though) that if you do something like this..
repo.Fetch<Truck>(";Select car.*, truck.maximumload from car left join truck on car.id = truck.carid");
or
repo.Fetch<Truck>(";Select car.*, truck.maximumload from car left join truck on car.id = truck.carid where truck.carid = #0", truckid);
I would probably have called my base class vehicle rather than car, buts that's just me.
Hope that helps ?
Related
What is the best way to do the following:
Suppose I have a class called Person and many derived classes for specialized persons.
Suppose at the beginning of my app, I know I have to deal with a person but I won't know what kind of person it is until much later (something beyond my control so I cannot determine the Person type at the beginning).
So at the beginning I will create a Person and fill in attributes for it. Later, when I know what kind of Person it is, I would instantiate a specialized person and copy over the any saved attributes for her.
Is there a more elegant way to do this without creating two objects?
If you don't know the type of person up front, you won't be able to avoid instantiating two objects. There has to be something to contain the base Person attributes before you know the specialized person, but you can't take advantage of polymorphism without instantiating the specialized object later.
One option is to use a composition pattern, in which each specialized person contains a Person instance rather than inheriting from it. You still have to instantiate two objects, but you don't have to rewrite the code to copy over the saved attributes every time. Here's an example (C# syntax):
public interface IPerson
{
string Name { get; }
int Age { get; }
}
public class Person : IPerson
{
public string Name { get; private set; }
public int Age { get; private set; }
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
public abstract class SpecialPersonBase : IPerson
{
private IPerson myPerson;
protected SpecialPersonBase(IPerson person)
{
myPerson = person;
}
public string Name { get { return myPerson.Name; } }
public int Age { get { return myPerson.Age; } }
public abstract string Greet();
}
public class Doctor : SpecialPersonBase
{
public Doctor(IPerson person) : base(person) { }
public override string Greet()
{
return "How are you feeling?";
}
}
public class Accountant : SpecialPersonBase
{
public Accountant(IPerson person) : base(person) { }
public override string Greet()
{
return "How are your finances?";
}
}
You could use the classes like this:
IPerson bob = new Person("Bob", "25");
// Do things with the generic object
// until you can determine the specific type
SpecialPerson specialBob;
if (bobIsDoctor)
{
specialBob = new Doctor(bob);
}
else if (bobisAccountant)
{
specialBob = new Accountant(bob);
}
specialBob.Greet();
I have 2 classes, Member.cs and Customer.cs and using table-per-type inheritance mapping described here.
This question poses the same problem, but with no answer.
Customer.cs
public class Customer
{
}
Member.cs
public class Member : Customer
{
public Member(Customer customer)
{
CreateFromCustomer(customer);
}
private void CreateFromCustomer(Customer customer)
{
// Here I assume I'll assign the Id so NHibernate wouldn't have to create a new Customer and know what Customer to be referred
Id = customer.Id;
}
}
CustomerMap.cs
public class CustomerMap : ClassMap<Customer>
{
public CustomerMap()
{
Id(x => x.Id)
.GeneratedBy.GuidComb();
}
}
MemberMap.cs
public class MemberMap : SubclassMap<Member>
{
public MemberMap()
{
KeyColumn("Id");
}
}
I tried several test case :
Test1.cs
[Test]
public void CanAddCustomer()
{
var customerRepo = /* blablabla */;
using (var tx = NHibernateSessionManager.GetSession().BeginTransaction())
{
var customer = new Customer()
customerRepo.RegisterCustomer(customer);
tx.Commit();
}
using (var tx = NHibernateSessionManager.GetSession().BeginTransaction())
{
/* Get the persisted customer */
var customer = customerRepo.GetCustomerByWhatever();
var member = customerRepo.RegisterMember(new Member(customer));
tx.Commit();
}
}
I'm expecting to have :
1 customer and 1 member which is a child of that customer
Instead I have :
2 customers (1 that is what was correctly created and 1 with all null columns) and 1 member that Id referred to all null columns Customer.
Is it the expected behavior?
I understand if we were wanted to create a child object from a transient parent object, this is a correct behavior.
But what if we were to create a child object that refers to an existing parent object?
The link I provided doesn't cover any persistence example, neither does googling.
Short Answer
No, it is not possible to "upgrade" an already persisted object to its subclass. Nhibernate simply doesn't support this. That's why you see 2 customers and one member entry. This is actually the expected behavior because Nhibernate simply creates a copy with a new ID of the object instead of creating the reference to Member...
So basically you could do either
Copy the data of Customer into Member, delete customer and save Member
Use a different object structure without subclasses where Member is a different table with it's own ID and a reference to Customer
Use native sql to insert the row into Member...
Some example:
Your classes could look like this
public class Customer
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
}
public class Member : Customer
{
public virtual string MemberSpecificProperty { get; set; }
}
Basically, Member could have additional properties, but will have the same properties as Customer of cause, too.
public class CustomerMap : ClassMap<Customer>
{
public CustomerMap()
{
Id(x => x.Id)
.GeneratedBy.GuidComb();
Map(x => x.Name);
}
}
and for the sub class, you have to map additional properties only!
public class MemberMap : SubclassMap<Member>
{
public MemberMap()
{
Map(x => x.MemberSpecificProperty);
}
}
testing it
{
session.Save(new Customer()
{
Name ="Customer A"
});
session.Save(new Member()
{
Name = "Customer B",
MemberSpecificProperty = "something else"
});
session.Flush();
}
This will create 2 entries in the customer table and one row into Member table. So this is as expected, because we created one customer and one member...
Now the "upgrade" from Customer A to Member:
using (var session = NHibernateSessionFactory.Current.OpenSession())
{
session.Save(new Customer()
{
Name ="Customer A"
});
session.Flush();
}
using (var session = NHibernateSessionFactory.Current.OpenSession())
{
var customer = session.Query<Customer>().FirstOrDefault();
//var member = customer as Member;
var member = new Member()
{
Name = customer.Name,
MemberSpecificProperty = "something else"
};
session.Delete(customer);
session.Save(member);
session.Flush();
}
Imagine a database table that looks like this:
create table [dbo].[user]
(
id int IDENTITY(1,1),
username varchar(50) NOT NULL,
firstname varchar(20) NOT NULL,
lastname varchar(30) NOT NULL,
currentid int NULL,
processedby varchar(50) NOT NULL,
processeddate varchar(50) NOT NULL
processedaction varchar(50) NOT NULL
)
What I want to do is to setup NHibernate to load it into my user object, but I only want the current version of the object "user" to be brought back. I know how to do a SQL select to do this on my own, and I feel as if there's something in nHibernate with the usage of triggers and event listeners, but can anyone tell me how to implement the nHibernate repository so I can:
{Repository}.GetCurrent(id) <- pass it any of the ids that are assigned to any of the historical or the current record, and get back the current object.
{Repository}.Save(user) <- I want to always insert the changes to a new row, and then update the old versions to link back to the new id.
Edit
So, there's some confusion here, and maybe I explained it wrong... What I'm trying to do is this, in regards to always getting the current record back...
Select uc.*
FROM User uo
JOIN User uc on uo.currentid=uc.id
WHERE uo.id==:id
But, I don't want to expose "CurrentID" to my object model, since it has no bearing on the rest of the system, IMHO. In the above SQL statement, uo is considered the "original" object set, and uc is considered the current object in the system.
Edit #2:
Looking at this as a possible solution.
http://ayende.com/blog/4196/append-only-models-with-nhibernate
I'm honestly being pigheaded, as I'm thinking about this backward. In this way of running a database, the autoincrementing field should be the version field, and the "id" field should be whatever the autoincrementer's value has at the time of the initial insert.
Answer:
I don't want to take #Firo's fury, and I'm not going to remove it from him, as he took me down the right path... what I wound up with was:
Created a base generic class with two types given
a. type of the object's "ID"
b. type of the object itself.
instantiate all classes.
create a generic interface IRepository class with a type of the object to store/retrieve.
create an abstract generic class with a type of the object to store/retrieve.
create a concrete implementation class for each type to store/retrieve.
inside of the create/update, the procedure looks like:
Type Commit(Type item)
{
var clone = item.DeepClone();
_Session.Evict(item);
clone.Id = 0;
clone.ProcessedDate = DateTime.Now;
if (clone.Action.HasValue)
{
if (clone.Action == ProcessedAction.Create)
clone.Action = ProcessedAction.Update;
}
else
{
clone.Action = ProcessedAction.Create;
}
clone.ProcessedBy = UserRepos.Where(u => u.Username == System.Threading.Thread.CurrentPrincipal.Identity.Name).First().Current;
var savedItem = (_Session.Merge(clone) as Type);
_Session.CreateQuery("UPDATE Type SET CurrentID = :newID where ID=:newID OR CurrentID=:oldID")
.SetParameter("newID", savedItem.Id)
.SetParameter("oldID", item.Id)
.ExecuteUpdate();
return savedItem;
}
In the delete method, we simply update the {object}.Action = ProcessedAction.Delete
I wanted to do this another way, but realizing we need to eventually do historical comparisons, we weren't able to ask nHibernate to filter the deleted objects, as the users will want to see that. We'll create a business facade to take care of the deleted records.
Again, much thanks to #Firo for his help with this.
So, with all that, I can finally do this:
var result = {Repository}.Where(obj => obj.Id == {objectID from caller}).FirstOrDefault();
if (result != null)
{
return result.Current;
}
else
{
return null;
}
and always get my current object back for any requesting ID. Hope it helps someone that is in my situation.
in mapping if you use FluentNHibernate
public UserMap : ClassMap<User>
{
public UserMap()
{
Where("id = currentid"); // always bring back the most recent
}
}
// in Userrepository
public void Update(User user)
{
var clone = user.Clone();
session.Evict(user); // to prevent flushing the changes
var newId = session.Save(clone);
session.CreateQuery("UPDATE User u SET u.currentid = :current") // <-- hql
.SetParameter("current", newId)
.ExecuteUpdate();
}
objectgraphs are a lot trickier with this simple code. I would then do one of the following:
use NHibernate.Envers to store auditing information for me
explicitly creating new entities in BL code
i once saw an append-only-model doing something like the following
// UserBase is there to ensure that all others referencing the User doesnt have to update because user properties changed
class UserBase
{
public virtual int Id { get; set; }
public virtual ICollection<PersonDetails> AllDetails { get; private set; }
public virtual PersonDetails CurrentDetails
{
get { return _currentDetauils; }
set { _currentDetauils = value; AllDetails.Add(value); }
}
// same as above
public virtual ICollection<ConfigDetails> AllConfigs { get; set; }
}
class Order
{
public virtual int Id { get; set; }
public virtual UserBase User { get; set; }
public virtual IList<OrderDetail> AllDetails { get; private set; }
public virtual IList<OrderDetail> ActiveDetails { get; private set; }
public virtual void Add(OrderDetail detail)
{
AllDetails.Add(detail);
ActiveDetails.Add(detail);
}
public virtual void Delete(OrderDetail detail)
{
detail.Active = false;
ActiveDetails.Remove(detail);
}
}
class OrderDetail
{
public virtual int Id { get; set; }
public virtual Order Parent { get; set; }
public virtual bool Active { get; set; }
}
class OrderMap : ClassMap<Order>
{
public OrderMap()
{
HasMany(o => o.AllDetails);
HasMany(o => o.ActiveDetails).Where("active=1");
}
}
// somewhere
public void UpdateTaxCharge(OrderDetail detail, TaxCharge charge)
{
var clone = detail.Clone();
clone.TaxCharge = charge;
detail.Order.Delete(detail);
detail.Order.Add(clone);
}
You can tell NHibernate what exactly SQL it should generate when persisting and loading an entity. For example you can tell NHibernate to use a stored procedure instead of a plain SQL statement. If this is an option for you I can farther elaborate my answer.
I'm a fluent nhibernate newbie and I'm struggling mapping a hierarchy of polymorhophic objects. I've produced the following Model that recreates the essence of what I'm doing in my real application.
I have a ProductList and several specialised type of products;
public class MyProductList
{
public virtual int Id { get; set; }
public virtual string Name {get;set;}
public virtual IList<Product> Products { get; set; }
public MyProductList()
{
Products = new List<Product>();
}
}
public class Product
{
public virtual int Id { get; set; }
public virtual string ProductDescription {get;set;}
}
public class SizedProduct : Product
{
public virtual decimal Size {get;set;}
}
public class BundleProduct : Product
{
public virtual Product BundleItem1 {get;set;}
public virtual Product BundleItem2 {get;set;}
}
Note that I have a specialised type of Product called BundleProduct that has two products attached.
I can add any of the specialised types of product to MyProductList and a bundle Product can be made up of any of the specialised types of product too.
Here is the fluent nhibernate mapping that I'm using;
public class MyListMap : ClassMap<MyList>
{
public MyListMap()
{
Id(ml => ml.Id);
Map(ml => ml.Name);
HasManyToMany(ml => ml.Products).Cascade.All();
}
}
public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Id(prod => prod.Id);
Map(prod => prod.ProductDescription);
}
}
public class SizedProductMap : SubclassMap<SizedProduct>
{
public SizedProductMap()
{
Map(sp => sp.Size);
}
}
public class BundleProductMap : SubclassMap<BundleProduct>
{
public BundleProductMap()
{
References(bp => bp.BundleItem1).Cascade.All();
References(bp => bp.BundleItem2).Cascade.All();
}
}
I haven't configured have any reverse mappings, so a product doesn't know which Lists it belongs to or which bundles it is part of.
Next I add some products to my list;
MyList ml = new MyList() { Name = "Example" };
ml.Products.Add(new Product() { ProductDescription = "PSU" });
ml.Products.Add(new SizedProduct() { ProductDescription = "Extension Cable", Size = 2.0M });
ml.Products.Add(new BundleProduct()
{
ProductDescription = "Fan & Cable",
BundleItem1 = new Product() { ProductDescription = "Fan Power Cable" },
BundleItem2 = new SizedProduct() { ProductDescription = "80mm Fan", Size = 80M }
});
When I persist my list to the database and reload it, the list itself contains the items I expect ie MyList[0] has a type of Product, MyList[1] has a type of SizedProduct, and MyList[2] has a type of BundleProduct - great!
If I navigate to the BundleProduct, I'm not able to see the types of Product attached to the BundleItem1 or BundleItem2 instead they are always proxies to the Product - in this example BundleItem2 should be a SizedProduct.
Is there anything I can do to resove this either in my model or the mapping?
Thanks in advance for your help.
As it stands, the BundleItem1 and BundleItem2 properties will always have a Product proxy because NH creates your proxies without touching the database, so it doesn't know if they are Products or some derived type. But when you call a method on your bundle items, NH should hit the DB and load the correct record, and you should get polymorphic behavior.
You could test this out. Add an override of ToString to your SizedProduct:
public override string ToString()
{
return "I'm a sized product!";
}
Then load your BundleProduct and do this:
Debug.WriteLine(bp.BundleItem1.ToString());
Debug.WriteLine(bp.BundleItem2.ToString());
You should find that the second call prints out "I'm a sized product!", and this will demonstrate that you have working polymorphism.
Assuming this all worked as I've described, its time to tackle the real question: what exactly do you want to do? Maybe you could provide some code that doesn't actually work as you would like it to.
Is there a way to persist an enum to the DB using NHibernate? That is have a table of both the code and the name of each value in the enum.
I want to keep the enum without an entity, but still have a foreign key (the int representation of the enum) from all other referencing entities to the enum's table.
Why are you guys over complicating this? It is really simple.
The mapping looks like this:
<property name="OrganizationType"></property>
The model property looks like this:
public virtual OrganizationTypes OrganizationType { get; set; }
The Enum looks like this:
public enum OrganizationTypes
{
NonProfit = 1,
ForProfit = 2
}
NHibernate will automatically figure it all out. Why type more than you need????
You can use the enum type directly: http://web.archive.org/web/20100225131716/http://graysmatter.codivation.com/post/Justice-Grays-NHibernate-War-Stories-Dont-Use-Int-If-You-Mean-Enum.aspx. If your underlying type is a string, it should use the string representation, if it is numeric, it will just use the numeric representation.
But your question wording sounds like you're looking for something different, not quite an enum. It seems that you want a lookup table without creating a separate entity class. I don't think this can be done without creating a separate entity class though.
An easy but not so beautiful solution:
Create an integer field with and set the mapping in the mapping file to the field.
Create a public property that uses the integer field.
private int myField;
public virtual MyEnum MyProperty
{
get { return (MyEnum)myField; }
set { myField = value; }
}
I am using NHibernate 3.2, and this works great:
type="NHibernate.Type.EnumStringType`1[[enum_full_type_name, enum_assembly]], NHibernate"
Not sure when the generic EnumStringType got added, though.
Try using a stategy pattern. Uou can then put logic into your inner classes. I use this quite alot espically when there is logic that should be contained in the "enum". For example the code below has the abstract IsReadyForSubmission() which is then implemented in each of the nested subclasses (only one shown). HTH
[Serializable]
public abstract partial class TimesheetStatus : IHasIdentity<int>
{
public static readonly TimesheetStatus NotEntered = new NotEnteredTimesheetStatus();
public static readonly TimesheetStatus Draft = new DraftTimesheetStatus();
public static readonly TimesheetStatus Submitted = new SubmittedTimesheetStatus();
//etc
public abstract int Id { get; protected set; }
public abstract string Description { get; protected set; }
public abstract bool IsReadyForSubmission();
protected class NotEnteredTimesheetStatus: TimesheetStatus
{
private const string DESCRIPTION = "NotEntered";
private const int ID = 0;
public override int Id
{
get { return ID; }
protected set { if (value != ID)throw new InvalidOperationException("ID for NotEnteredTimesheetStatus must be " + ID); }
}
public override string Description
{
get { return DESCRIPTION; }
protected set { if (value != DESCRIPTION)throw new InvalidOperationException("The description for NotEnteredTimesheetStatus must be " + DESCRIPTION); }
}
public override bool IsReadyForSubmission()
{
return false;
}
}
//etc
}