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.
Related
I have a Web Api Application where I have EF DB first entities and DTO classes.
So this is my generic repository interface -
public interface IRepository<TEntity> where TEntity:class
{
IQueryable<TEntity> GetAll();
void Create(TEntity Entity);
TEntity GetById(int id);
void Update(TEntity Entity);
void Remove(int id);
}
Here is a sample of the GetAll method implementation for one of the services -
public class OrdersRepository : IRepository<SalesOrderHeader>
{
private AdventureWorksEntities db = new AdventureWorksEntities();
public IQueryable<SalesOrderHeader> GetAll()
{
return db.SalesOrderHeaders;
}
And here is my service or apicontroller calling the method along with additional mapping -
public IQueryable<Orders> GetSalesOrderHeaders()
{
**var Orders = orderRepo.GetAll();**
var OrderDetails = orderDetailRepo.GetAll();
return (from so in Orders
select new Orders()
{
SalesOrderID = so.SalesOrderID,
SalesOrderNumber = so.SalesOrderNumber,
ShipDate = so.ShipDate.ToString(),
Customer = customerRepo.GetById(so.CustomerID),
OrderItems = (from sod in OrderDetails
select new OrderItems()
{
SalesOrderId = sod.SalesOrderID,
ProductID = sod.ProductID,
Quantity = sod.OrderQty,
UnitPrice = sod.UnitPrice
}).Where(a => a.SalesOrderId == so.SalesOrderID).ToList()
});
}
As seen here the mapping is being done here in the apicontroller class. Similarly for Create/Add method of repository, where will this happen? If in the apicontroller, does that mean I need access to Entity in my apicontroller? if in the Repository, then i will have to map the DTO to Entity in my repository. Both seem dubious. Here is my DTO class -
public class Orders
{
public int SalesOrderID { get; set; }
public string SalesOrderNumber { get; set; }
public string ShipDate { get; set; }
public CustomerDTO Customer { get; set; }
public List<OrderItems> OrderItems { get; set; }
}
The entity class is called SalesOrderHeaders and has a lot more fields.
Neither. Like you, I faced this dilemma with my code and decided that implementing object mapping in aither the controller or the repository will only add innecesary complexity and will end up violating the DRY (Don't Repeat Yourself) principle, and since using an ORM was overkill for the size of the project, I end up creating a set of extension methods that allowed me do the job in a clean and reusable way.
Here is an example, though incompleted, that may help you build your own:
// File: ModelExtensions.Orders.cs
public static partial class ModelExtensions
{
public static IEnumerable<OrdersDto> MapToDto(this IEnumerable<Orders> list)
{
return list.select(item => item.MapToDto());
}
public static OrdersDto MapToDto(this Orders order)
{
var dto = new OrdersDto() {
SalesOrderID = order.SalesOrderID,
SalesOrderNumber = order.SalesOrderNumber,
ShipDate = order.ShipDate.ToString()
}
}
public static IEnumerable<OrdersDetailsDto> MapToDto(this IEnumerable<OrderDetails> list)
{
return list.select(item => item.MapToDto());
}
public static OrderDetailsDto MapToDto(this OrderDetails orderDetails)
{
var dto = new OrderDetailsDto() {
SalesOrderId = orderDetails.SalesOrderID,
ProductID = orderDetails.ProductID,
Quantity = orderDetails.OrderQty,
UnitPrice = orderDetails.UnitPrice
}
}
}
You can also create similar mapping functions to map from DTOs to Entities.
To use this functions, just include a reference to the namespace where the extension methods are defined, et voila, you have all your mappings at hand.
Basically, the transformation would occur in the API method, since there is where the exchange (DTO/Entity or Entity/DTO) is made.
Using this method, you could use the mapping functions wherever you need it without the need to repeat (DRY principle) the logic in different places.
Sample usage:
public IQueryable<Orders> GetSalesOrderHeaders()
{
var orders = orderRepo.GetAll().MapToDto();
var orderDetails = orderDetailRepo.GetAll();
foreach (var ord in orders)
{
ord.OrderItems = orderDetails.Where(a => a.SalesOrderId == ord.SalesOrderID).MapToDto();
}
return orders.ToList();
}
I have a class in Entity framework 5 (using MVC 4):
public class JobFunction
{
public int Id { get; set; }
public string JobFunctionName { get; set; }
public int StatusId { get; set; }
public Status JFStatus { get; set; }
}
In my OnModelCreating method, I establish a FK relationship with the Status table as follows:
modelBuilder.Entity<JobFunction>().HasRequired(a => a.JFStatus).
WithMany().HasForeignKey(u => u.StatusId).WillCascadeOnDelete(false);
In my controller, I get a list of JobFunction objects as follows:
List<JobFunction> jfList = uow.JobFunctionRepository.GetAll().ToList<Catalog>();
where uow is my Unit of Work object, and JobFunctionRepository is defined. When I examine any JobFunction object in jfList, I see the following in my watch window:
Id: 1
JfStatus: null
JobFunctionName: "Manager"
StatusId: 2
Note that JFStatus is null. My question is: what provisions do I make in my code to initialize JFStatus to the appropriate Status object (based on the value of StatusId), during my GetAll call?
Thanks in advance.
-Zawar
You need some instrument to apply eager loading when you load the data through your repository. For example you could give your GetAll method a parameter list of expressions for the navigation properties you want to include in your query:
using System.Data.Entity;
//...
public IQueryable<JobFunction> GetAll(
params Expression<Func<JobFunction, object>>[] includes)
{
IQueryable<JobFunction> query = context.JobFunctions;
foreach (var include in includes)
query = query.Include(include);
return query;
}
Then you call it like so:
List<JobFunction> jfList = uow.JobFunctionRepository
.GetAll(jf => jf.JFStatus)
.ToList();
The JFStatus property should be filled now.
We have a problem concerning Entity Framework objects and sending them through WCF.
We have a database, and Entity Framework created classes from that database, a 'Wallet' class in this particular situation.
We try to transfer a Wallet using this code:
public Wallet getWallet()
{
Wallet w = new Wallet();
w.name = "myname";
w.walletID = 123;
return w;
}
We need to transfer that Wallet class, but it won't work, we always encounter the same exception:
"An error occurred while receiving the HTTP response to localhost:8860/ComplementaryCoins.svc. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details."
We searched on the internet, and there is a possibility that the error is due to the need of serialization of Entity Framework-objects.
We have absolutely no idea if this could be the case, and if this is the case, how to solve it.
Our DataContract looks like this (very simple):
[DataContract]
public partial class Wallet
{
[DataMember]
public int getwalletID { get { return walletID; } }
[DataMember]
public string getname { get { return name; } }
}
Does anyone ever encountered this problem?
EDIT: Our Entity Framework created class looks like this:
namespace ComplementaryCoins
{
using System;
using System.Collections.Generic;
public partial class Wallet
{
public Wallet()
{
this.Transaction = new HashSet<Transaction>();
this.Transaction1 = new HashSet<Transaction>();
this.User_Wallet = new HashSet<User_Wallet>();
this.Wallet_Item = new HashSet<Wallet_Item>();
}
public int walletID { get; set; }
public string name { get; set; }
public virtual ICollection<Transaction> Transaction { get; set; }
public virtual ICollection<Transaction> Transaction1 { get; set; }
public virtual ICollection<User_Wallet> User_Wallet { get; set; }
public virtual ICollection<Wallet_Item> Wallet_Item { get; set; }
}
}
Thanks for helping us.
I had the same problem some time ago and the solution for this was:
The entity framework was returning a serialized class instead of normal class.
eg. Wallet_asfawfklnaewfklawlfkawlfjlwfejlkef instead of Wallet
To solve that you can add this code:
base.Configuration.ProxyCreationEnabled = false;
in your Context file.
Since the context file is auto generated you can add it in the Context.tt
In the Context.tt file it can be added around lines 55-65:
<#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext
{
public <#=code.Escape(container)#>()
: base("name=<#=container.Name#>")
{
base.Configuration.ProxyCreationEnabled = false;
<#
if (!loader.IsLazyLoadingEnabled(container))
{
#>
this.Configuration.LazyLoadingEnabled = false;
<#
Try specifying a setter for the properties, something like this :
[DataContract]
public partial class Wallet
{
[DataMember]
public int getwalletID { get { return walletID; } set { } }
[DataMember]
public string getname { get { return name; } set { } }
}
If it still doesn't work, you may consider creating an intermediate POCO class for this purpose, and use mapper library like AutoMapper or ValueInjecter to transfer the data from the EF objects.
The POCO class should have same properties as your EF class :
[DataContract]
public class WalletDTO
{
[DataMember]
public int walletID { get; set; }
[DataMember]
public string name { get; set; }
}
And modify your method to return this class instead :
public WalletDTO getWallet()
{
Wallet w = new Wallet(); // or get it from db using EF
var dto = new WalletDTO();
//assuming we are using ValueInjecter, this code below will transfer all matched properties from w to dto
dto.InjectFrom(w);
return dto;
}
Are you trying to recieve a IEnumerable<Wallets>? If - yes, please modify your server class that returns the IEnumerable by adding .ToArray() method
With FluentNHibernate I have mapped a UserPreference entity which references the GeneralPreference, GeneralPreferenceOption, and Profile entities:
public class UserPreference
{
public virtual long Id { get; set; }
public virtual Profile Profile { get; set; }
public virtual GeneralPreference Preference { get; set; }
public virtual GeneralPreferenceOption Value { get; set; }
}
It's easy enough to map a list of UserPreference on my Profile entity, but what I actually would like to do is wrap this list inside another class so that I can simplify operations concerning a user's given preferences:
public class Preferences
{
public IList<UserPreferences> UserPreferences{get;set;}
public Language Language {
{
//look up the language preference here
}
}
This kind of feels like a Component, but Components were not created for this type of scenario. Does anyone have any pointers on how I might map this?
I figured out a way to do this by mapping a private property on my Profile Entity. Using the techniques from the Fluent NHibernate wiki on mapping private properties (http://wiki.fluentnhibernate.org/Fluent_mapping_private_properties) I map a collection of UserPreference on my Profile Entity. Then I create another class PropertyHandler which takes an IEnumerable as a constructor parameter and make an instance of this a public property on Profile as well:
public class Profile
{
private PreferenceHandler _preferenceHandler;
get { return _preferenceHandler ?? (_preferenceHandler = new PreferenceHandler(UserPreferences)); }
private IEnumerable<UserPreference> UserPreferences { get; set; }
public static class Expressions
{
public static readonly Expression<Func<Profile, IEnumerable<UserPreference>>> UserPreferences = x => x.UserPreferences;
}
}
Notice the nested static class. It's used to enable mapping of a private property with FluentNHibernate.
The mapping class looks something like this:
public class ProfileMappings : ClassMap<Profile>
{
public ProfileMappings()
{
//... other mappings
HasMany(Profile.Expressions.UserPreferences);
}
}
I can now use the PreferenceHandler class to create helper methods over my collection of UserPreference.
An alternative is to build extension methods for IEnumberable. This works, but I decided not to do this because
1) I'm not really extending the IEnumerable functionality and
2) my helper methods disappear inamongst all the other IEnumerable extension methods making the whole thing a bit cluttered.
I have been reading Nhibernate in Action, but the section on mapping polymorphic collections is a little too short on how to do this.
I have the following code
[Class]
[Discriminator(Column="MachineType",TypeType=typeof(string))]
public abstract class Machine
{
[Property]
public string Name{get;set;}
}
[Subclass(DiscriminatorValue="Heavy",ExtendsType=typeof(Machine))]
public class HeavyMachine : Machine
{
[Property]
public int Weight { get; set; }
}
[Subclass(DiscriminatorValue="Fast",ExtendsType=typeof(Machine))]
public class FastMachine : Machine
{
[Property]
public float Speed { get; set; }
}
[Class]
public class Module
{
List<Machine> machines = new List<Machine>();
[Bag(Name = "Machines", Cascade = "all", Lazy = false, Inverse=true)]
[Key(1, Column = "Machine")]
[OneToMany(2, ClassType = typeof(Machine))]
public IList<Machine> Machines
{
get
{
return machines.AsReadOnly();
}
private set
{
machines = value.ToList();
}
}
}
With the code above I am not getting any errors, but the Collection of machines in Module remains empty after retreiving my objects from the database. The mapping of the Machine (and it's subclasses) does seem ok, because a property of type Machine is correctly returned.
What Nhibernate.Mapping.Attributes do I need to map my collection of abstract classes?
thx in advance!
Ok I found the solution.
After removing the "Inverse=true" tag from my IList mapping it worked.