I have a use case where the same type is used in different one-to-many relationships, and the data on the many side of the relationship has to be stored to different tables depending on the usage. Is there a way I can tell (fluent) NHibernate which ClassMap to use (e.g. like my fake UseClassMap method below)?
Rough example code:
public class Foo
{
public long Id {get; set;}
public IEnumerable<Bar> TheoreticalBar {get; set;}
public IEnumerable<Bar> ActualBar {get; set; }
}
public class FooMap : ClassMap<Foo>
{
Table("Foo");
Id(x => x.Id);
HasMany(x => x.TheoreticalBar).UseClassMap(TheoryBarMap);
HasMany(x => x.ActualBar).UseClassMap(ActualBarMap);
}
public class TheoreticalBarMap : ClassMap<Bar>
{
TableNames("TheoreticalBar");
...
}
public class ActualBarMap : ClassMap<Bar>
{
TableNames("ActualBar");
...
}
In the hopes of garnering more arbitrary and unexplained downvotes, I'll answer this question: I wasn't able to find a specific method to explicitly declare the ClassMap to use, but I was able to find a solution without adding unncessary subtypes or similar ClassMaps by adding a discriminator value to the child class that is set by the parent, and then using the .Where() method to filter on the discriminator value ... something like this:
class Bar
{
public string BarType {get; protected set};
public Bar(string barType)
{
BarType = barType;
}
}
public class FooMap : ClassMap<Foo>
{
Table("Foo");
Id(x => x.Id);
HasMany(x => x.TheoreticalBar).Where("BarType = theoretical");
HasMany(x => x.ActualBar).Where("BarType = actual");
}
The .Where() method allows use of string interpolation and substitutions in case you want to avoid magic strings in my simplistic example here.
Related
I have quite a number of DbSets hanging off of my DbContext. The generic type for each of these DbSets derives from a common base class.
How can I query a common property in the base class across all of the DbSets?
To illustrate, say for instance I have the following two DbSets:
public DbSet<Person> People { get; set; }
public DbSet<Vehicle> Vehicles { get; set; }
Both Person and Vehicle derive from EntityBase.
public class Person : EntityBase {
...
}
public class Vehicle : EntityBase {
...
}
Say EntityBase is defined as follows:
public class EntityBase {
public virtual string ExampleProperty { get; set; }
}
How can I select the values of ExampleProperty across all of the DbSets? For my contrieved example I could do a simple union, but I am looking for an easy way to query across all DbSets holding types deriving from EntityBase, since I have hundreds of them.
I can query the change tracker as follows, but that will only work for entities with pending changes.
dbContext.ChangeTracker
.Entries<EntityBase>()
.Select(obj => obj.Entity)
.Select(obj => obj.ExampleProperty);
How can I do the same thing for all entities?
I was able to grab the data I need using the following query (I prefer the fluent API):
dbContext.GetType().GetProperties()
.Where(p => p.PropertyType.IsGenericType)
.Where(p => p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>))
.Where(p => p.PropertyType.GetGenericArguments().First().IsSubclassOf(typeof(EntityBase)))
.SelectMany(p => (IEnumerable<EntityBase>)p.GetValue(dbContext, null))
.Select(obj => obj.ExampleProperty);
Unfortunately, the SQL generated is querying all columns for each of the DBSet types which is pulling quite a bit of data into memory.
Assume I have the following entity classes:
public class Customer {
public int Id { get; set; }
public virtual ICollection<Order> Orders { get; set; }
}
public class Order {
public int Id { get; set; }
public virtual Customer Customer { get; set; }
}
How should those be mapped in Entity Framework 6 fluent code-first mapping? I want to be explicit about the mapping and not rely on automatic mapping conventions.
Option 1
Just map the local properties of both classes. That's how I would do it in Fluent NHibernate.
public class CustomerMap : EntityTypeConfiguration<Customer> {
public CustomerMap() {
HasMany(x => x.Orders);
}
}
public class OrderMap : EntityTypeConfiguration<Order> {
public OrderMap() {
HasRequired(x => x.Customer);
}
}
Option 2
Map both sides of the relationship in both classes.
public class CustomerMap : EntityTypeConfiguration<Customer> {
public CustomerMap() {
HasMany(x => x.Orders).WithRequired(x => x.Customer);
}
}
public class OrderMap : EntityTypeConfiguration<Order> {
public OrderMap() {
HasRequired(x => x.Customer).WithMany(x => x.Orders);
}
}
Option 3
Map both sides of the relation, but only in one of the classes. The code would be similar to option 2, just one of the two constructors would be empty.
Is there any difference between those options? If yes, please also explain why I should or shouldn't use a specific option.
I would go for option 3.
In option 1 you can forget to map the inverse end of an association. In this simple example it's clear that Order.Customer and Customer.Orders are two ends of the same association. When things get more complex, this isn't always obvious. Also, it is redundant code.
In option 2 you could have conflicting mappings. For instance when you have...
HasOptional(x => x.Customer).WithMany(x => x.Orders);
...in OrderMap, you will get a runtime exception telling you that both mappings don't match. And again, it is redundant code.
So option 3 is DRY and safe. The only issue is that it's a bit arbitrary where to configure the mappings. I tend to adhere to mapping children in their parent's mapping.
One more comment. You may want to add a primitive property CustomerId in Order. The mapping would look like:
public class CustomerMap : EntityTypeConfiguration<Customer>
{
public CustomerMap()
{
HasMany(x => x.Orders).WithRequired(x => x.Customer)
.HasForeignKey(o => o.CustomerId);
}
}
Now you have full control over both ends of the association and the foreign key name to be used. Besides that, there are some advantages of these foreign key associations as opposed to independent associations (without a primitive foreign key property). For instance, the ability to establish an association without having to fetch the parent object from the database. You can just by set an Id value.
I am not sure if fluent n hibernate can do this or not, but I cannot figure out how.
I have a table - cases and some properties
ownerId, brokerId, shipperId
I want to map this to my property:
int[] OrgsWithAccess
Is this possible?
This way when I am checking if an org has access to the case, I can check the property OrgsWithAccess rather than OwnerId == myorg.id or brokerId == myorg.id etc.
If I understand your question correctly, I wouldn't recommend trying to map in the way that you have asked.
cases table looks like it is some form of junction table between other tables. I'll assume that these other tables each contain data that are represented as entities in the application, and that there are three tables, Owner, Broker and Shipper.
OrgsWithAccess should be mapped using the references to the entities that is has in the application i.e. assume the class looks something like
public class OrgsWithAccess
{
public virtual Owner { get; set; }
public virtual Broker { get; set; }
public virtual Shipper { get; set; }
}
Then the mapping will look like
public class OrgsWithAccessMap : ClassMap<OrgsWithAccess>
{
public OrgsWithAccessMap()
{
References(x => x.Owner);
References(x => x.Broker);
References(x => x.Shipper);
}
}
Then when querying, you would simply look at the properties on OrgsWithAccess
session.QueryOver<OrgsWithAccess>().Where(x => x.Owner.Id == id);
Say I have the following class with an aggregation of an external class:
public class MyMovie
{
public virtual string id{get;set;}
public virtual Movie movie{get;set;}
}
//These classes are externally defined and cannot be changed.
public class Movie
{
public string title{get;set;}
public IList<Director> Directors{get;set;}
}
public class Director
{
public string name{get;set;}
public IList<Movie> DirectedMovies{get;set;}
}
The db schema for this would be three tables:
Movie(m_id, title)
Director(d_id, name)
Directs(m_id, d_id)
Is it possible to map this with fluent nhibernate? I just don't understand how this would be done with the many to many relation being in the external classes where I cannot map create a map class for Director as this does not define members as virtual.
Map your class MyMovie as usual, and use disable lazyloading of Movie and Director. Aftter all lazy-loading for many-to-many part should work as usualy, cause for collection laziness proxy is not need.
public class MyMovieMap : ClassMap<MyMovie>
{
public MyMovieMap()
{
Id(x => x.id);
References(x => x.movie);
}
}
public class MovieMap : ClassMap<Movie>
{
public MovieMap()
{
Not.LazyLoad();
Id<int>("m_id");
Map(x => x.title);
HasManyToMany(x => x.Directors)
.Table("Directs")
.LazyLoad();
}
}
public class DirectorMap : ClassMap<Director>
{
public DirectorMap()
{
Not.LazyLoad();
Id<int>("d_id");
Map(x => x.name);
HasManyToMany(x => x.DirectedMovies)
.Table("Directs")
.LazyLoad();
}
}
Basically, your issue here is that you are trying to tell nhibernate to load objects, but it doesn't know anything about the objects. For instance, you are telling it MyMovie contains a Movie, yet it doesn't know what field Movie.title belongs to, and it doesn't know how to join in Director's with the movies because it is unmapped. So basically in order to pull this off without a mapping file, you need to use Criteria and result transformers to accomplish this (basically issuing a sql query and converting the results to objects via an on the fly mapping), you could encapsulate this logic in a function so it can be called in your code without being too messy, but other than that I can't see any other way around it. Check out this post, the code is not exactly what you are trying to do (because you will have to join in directors), but it is using the same tools you will have to use... http://ayende.com/blog/2741/partial-object-queries-with-nhibernate
I have the a class similar to the following (nb! names have been changed to protect the innocent):
public class Person
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual DateTime Birthday { get; set; }
public virtual TimeSpan Age { get { return DateTime.Now - this.Birthday; } }
}
I use Fluent NHibernate to configure my mapping:
public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Id(x => x.Id);
Map(x => x.Name);
Map(x => x.Birthday);
}
}
The problem is that this throws an exception:
Could not find a setter for property
'Age' in class 'Person'
If Age is not marked virtual I get:
The following types may not be used as
proxies: Person: method get_Age should
be 'public/protected virtual' or
'protected internal virtual'
Of course it cant find a setter and it shouldn't! How can I make this mapping work?
The real question to me is why is fluent NHibernate trying to map the Age property at all? It's not even in your mapping. I've only used earlier versions of fluent NHibernate, prior to the whole auto-mapping functionality, and never had this problem.
I suspect that either your Conventions are causing it to try to map Age, or you somehow have auto-mapping enabled which is conflicting with your manual mapping.
Also be aware that Fluent NHibernate somewhat recently changed conventions. So I would take a look at the following documentation:
http://wiki.fluentnhibernate.org/show/Conventions
http://wiki.fluentnhibernate.org/show/ConvertingToNewStyleConventions
http://wiki.fluentnhibernate.org/show/AutoMapping