I am new to NHibernate and am facing some issues with Fluent NHibernate automap.
I am using Nhibernate 3.3.3.400, Fluent Nhibernate 1.3.0.733 Automapper 2.2.1
I have a column in Database which is of type Xml. When I try to create a ma mapping column it give me the following error.
An association from the table Product refers to an unmapped class: System.Xml.XmlDocument
Following is the code I am trying to implement.
using System.Collections.Generic;
using System.Xml;
//using System.Xml.Linq;
namespace Examples.FirstAutomappedProject.Entities
{
public class Product
{
public virtual int Id { get; protected set; }
public virtual string Name { get; set; }
public virtual double Price { get; set; }
public virtual Location Location { get; set; }
public virtual IList<Store> StoresStockedIn { get; set; }
public virtual XmlDocument SalesRange { get; set; }
public Product()
{
StoresStockedIn = new List<Store>();
}
}
}
I have been struggling for a couple of days now anhy help or samples would be greatly appreciated.
it seems it FNH will not map it on it's own. you'll need a Override there
Map(x => x.SalesRange).CustomType<NHibernate.Type.XmlDocType>();
Since Firo has sent his answer in comment I am answering it on his behalf.
basically here is what I ended up doing.
I created an override class as follows
using FluentNHibernate.Automapping;
using FluentNHibernate.Automapping.Alterations;
using Examples.FirstAutomappedProject.Entities;
using NHibernate.Mapping;
using NHibernate.Type;
namespace Examples.FirstAutomappedProject.Overrides
{
public class OrderQueueOverride : IAutoMappingOverride<Product>
{
public void Override(AutoMapping<Product> mapping)
{
mapping.Map(x => x.SalesRange).CustomType<XmlDocType>();
}
}
}
So the auto mapper will pick this override and map the column accodingly.
Related
I'm working on a project which has Database-First approach.
In the said project, I have Offer.OfferPlaces and OfferPlaces.Offer relation.
The first one doesn't seem to work, loading it via Include(x => x.OfferPlaces) yields empty collection, while loading Include(x => x.Offer) works fine and loads related Offer.
Is that a common issue in EFCore, do I have to implement a workaround of sorts or perhaps add something to OnModelCreating of the DbContext?
Here's the Offer model:
using Microsoft.EntityFrameworkCore.Internal;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
namespace MarketplaceOffe.DAL.Model
{
[Table("Offers")]
public partial class Offer
{
public Offer()
{
OfferPlaces = new HashSet<OfferPlace>();
}
public long Id { get; set; }
public long CompanyId { get; set; }
public virtual ICollection<OfferPlace> OfferPlaces { get; set; }
}
}
And the OfferPlace model:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
namespace MarketplaceOffe.DAL.Model
{
[Table("OfferPlaces")]
public partial class OfferPlace
{
public long Id { get; set; }
public long OfferId { get; set; }
public virtual Offer Offer { get; set; }
}
}
I am not sure what the protocol is w.r.t. to changing your question but I guess it would be better if I change it as there is a lot of information and the original intent of asking the question has been lost.
Original question:
I was originally having an issue while generating a database using EF (Code first approach / POCO classes).
One of the issues was that I was making use of Constructors in my entity classes to initialize the members. After #RickStahl suggested that it is not required, I changed my implementation.
For the sake of readers, I didn't want the information to be lost as some of the old comments intend to adress that issue.
However, during the course of time since this thread was initially created, the situation has changed. I have been able to get over some of the issues.
Current Issue:
I am having problem retrieving the content from the underlying database tables.
Exception:
Object reference not set to an instance of an object.
The exception occurs at the following statement of Program.cs (Line 21):
foreach(PhoneNumber p in cd.Phones)
For the sake of ease in understanding the issue, I am pasting the whole source code.
Here's the source code of my Entity class.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ConsoleApplication1
{
public class ContactData
{
[Key]
public int ContactId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public IList<PhoneNumber> Phones { get; set; }
public IList<EmailAddress> Emails { get; set; }
}
public class PhoneNumber
{
[Key]
public int PhoneId { get; set; }
public int ContactId { get; set; }
public string Phone { get; set; }
public ContactData ContactData { get; set; }
public PhoneNumber()
{
}
}
public class EmailAddress
{
[Key]
public int EmailId { get; set; }
public int ContactId { get; set; }
public string Email { get; set; }
public ContactData ContactData { get; set; }
public EmailAddress()
{
}
}
}
ContactContext.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.Entity;
namespace ConsoleApplication1
{
public class ContactContext : DbContext
{
public ContactContext()
: base("ContactDBContext")
{
Database.SetInitializer<ContactContext>(new DropCreateDatabaseIfModelChanges<ContactContext>());
}
public DbSet<ContactData> Contacts { get; set; }
}
}
ContactManager.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class ContactManager
{
ContactContext cDbContext = new ContactContext();
public IList<ContactData> GetContactList()
{
IQueryable<ContactData> cContactList = cDbContext.Contacts;
IList<ContactData> cListData = new List<ContactData>();
cListData = cContactList.ToList();
return cListData;
}
}
}
Program.cs (Entry point)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main()
{
ContactManager cMgr = new ContactManager();
foreach (ContactData cd in cMgr.GetContactList())
{
Console.WriteLine(cd.ContactId);
Console.WriteLine(cd.FirstName);
Console.WriteLine(cd.LastName);
foreach(PhoneNumber p in cd.Phones)
{
Console.WriteLine(p.Phone);
}
foreach (EmailAddress e in cd.Emails)
{
Console.WriteLine(e.Email);
}
}
Console.ReadKey();
}
I finally managed to find a solution to my own problem. But I want to extend my 'Thanks' to both #SteveGreene and #RickStahl for their valuable inputs.
#RickStahl gave a good input or recommendation of not making use of parameterized constructors in my Entity classes. Per Rick, the parameterized constructors do not work with EF.
#SteveGreene - I finally realized that you were pointing me into right direction. I was not able to get it at that time due to my lack of understanding of EF. However, after reading about the EF in detail and in particular about Eager loading and Lazy loading helped me finally.
The error related to 'Object reference not set to an instance of an object' was easy to resolve after I figured out that I had to instantiate the collection properties for 'Phones' and 'Emails' in the ContactData class constructor.
Please refer to the code change below.
public class ContactData
{
[Key]
public int ContactId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string BusinessName { get; set; }
public IList<PhoneNumber> Phones { get; set; }
public IList<EmailAddress> Emails { get; set; }
public ContactData()
{
//By instantiating Phones and Emails member collections below, resolved 'Object reference not set to an instance of an object' exception
Phones = new List<PhoneNumber>();
Emails = new List<EmailAddress>();
}
}
The next step was to populate data into related Entities as I was only able to populate the parent Entity Contact up to this point.
For that I had to discover from my research and understand about different approaches such as Eager loading, Lazy loading etc. towards loading data into related entities.
That is done by using the following statements in the ContactManager.GetContactList() method. I have used eager loading method as far as I understand. For lazy loading, you have to use virtual members for child classes such as Phone and Email in this case.
var phones = cDbContext.Contacts.Include("Phones").ToList();
var emails = cDbContext.Contacts.Include("Emails").ToList();
Here's a complete code of ContactManager class.
class ContactManager
{
ContactContext cDbContext = new ContactContext();
public IList<ContactData> GetContactList()
{
ContactData cd = new ContactData();
IQueryable<ContactData> cContactList = cDbContext.Contacts;
IList<ContactData> cListData = new List<ContactData>();
var phones = cDbContext.Contacts.Include("Phones").ToList();
var emails = cDbContext.Contacts.Include("Emails").ToList();
cListData = cContactList.ToList();
return cListData;
}
}
After learning about this I understood that #SteveGreene was pointing me into correct direction. Only issue that I ran into after all this was that I used a lambda expression with Include method. But I got a compile time exception 'Include method expects a string'. I tried using the following lambda expression with the include method.
var phones = cDbContext.Contacts.Include(b => b.Phones).ToList(); // this was causing a compile time error 'Include method expects a string'.
After passing (as below) the name of the parameter as string with the name of 'Phones' and 'Emails' collection members of ContactData class, it worked fine.
var emails = cDbContext.Contacts.Include("Emails").ToList();
If any queries then please post a comment.
My class:
[PersistClass]
public class ExternalAccount
{
public virtual AccountType AccountType { get; set; }
public virtual int Id { get; private set; }
public virtual User User { get; set; }
public virtual Dictionary<string, string> Parameters { get; set; }
public ExternalAccount()
{
Parameters = new Dictionary<string, string>();
}
}
The Dictionary is not getting mapped. I understand that automapping doesn't work by default with Dictionaries, how do I configure the mapping? All Parameters is is a list of key/value pairs - so I would expect them to be stored in a table with a foreign key to the externalaccount table. I know I can do this with another class - but it makes access to the parameters in the class more difficult - I'd rather have to configure the complexity once.
Please bear in mind I am new Fluent and to nHibernate.
Thanks
Using a simple class relationship such as the following:
public class Foo {
public virtual IDictionary<string, Bar> Bars { get; set; }
}
public class Bar {
public virtual string Type { get; set; }
public virtual int Value { get; set; }
}
You can map this with Fluent NHibernate in this way:
mapping.HasMany(x => x.Bars)
.AsMap(x => x.Type);
Where Bar.Type is used as the index field into the dictionary.
FluentNHibernate mapping for Dictionary
I am new to using Fluent NHibernate and NHibernate for the first time. I've used a custom written mapper since about 2000 that was written in house. Made a switch to LinqToSQL about 2 years ago, and about 6 months ago to Entities.
I'd like to see what Fluent/NHibernate have to offer. However, I can't seem to get it to run correctly. The following is a copy of my classes, their references, the ClassMaps. Can someone tell me if this simple implementation is correct?
This is my mappings and object classes:
using System;
using FluentNHibernate.Mapping;
namespace MyData.Data.Mappings
{
public class Login
{
public virtual int LoginId { get; private set; }
public virtual string Username { get; set; }
public virtual User User { get; set; }
}
public class LoginClassMap : ClassMap<Login>
{
public LoginClassMap()
{
Table("Logins");
Id(d => d.LoginId).GeneratedBy.Guid();
Map(d => d.Username).Not.Nullable().Length(50);
HasOne(d => d.User).ForeignKey("UserId").Cascade.All();
}
}
public class User
{
public virtual Guid UserId{ get; private set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual Login Login { get; set; }
}
public class UserClassMap : ClassMap<User>
{
public UserClassMap()
{
Table("Users");
Id(d => d.UserId).GeneratedBy.Guid();
Map(d => d.FirstName).Not.Nullable().Length(100);
Map(d => d.LastName).Not.Nullable().Length(100);
References(r => r.Login, "UserId").Not.Nullable();
}
}
}
This is my Repository:
using System;
using System.Linq;
using NHibernate;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate.Linq;
using MyData.Data.Mappings;
namespace MyData.Data.Repository
{
public class LoginRepository
{
private readonly ISessionFactory _sessionFactory;
ISession _session;
public LoginRepository()
{
_sessionFactory = this.CreateSessionFactory();
_session = _sessionFactory.OpenSession();
}
private ISessionFactory CreateSessionFactory()
{
string connString = "MyDataConnectionString";
FluentConfiguration config = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005.ConnectionString(
x => x.FromConnectionStringWithKey(connString)))
.ExposeConfiguration(
c => c.SetProperty("current_session_context_class", "webContext"))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<Login>());
return config.BuildSessionFactory();
}
public IQueryable<Login> GetAllLogins()
{
return _session.Linq<Login>();
}
}
}
Whenever it gets to config.BuildSessionFactory() it throws the following error:
An invalid or incomplete configuration was used while creating a SessionFactory.
The inner Exception is:
Exception has been thrown by the target of an invocation.
Is this the correct way to approach this? Any ideas or tweaks would be greatly appreciated!
Sean, you cannot use GeneratedBy.Guid() on LoginId because it's an int. Try to use another generator or change the type of LoginId.
I suspect you cannot use "private set" on UserId because it's the Id.
Also, HasOne(d => d.User).ForeignKey("UserId") means that if you expose your database model from your mappings, the name of the FK constraint will be "UserId". It seems to me that your intention was to specify the name column that holds the PK on table "Users".
These links have more infos:
http://wiki.fluentnhibernate.org/Fluent_mapping
http://jagregory.com/writings/i-think-you-mean-a-many-to-one-sir/
one-to-one fluent nhibernate?
It seems you forgot to specify proxy factory. It can be done like this:
OracleDataClientConfiguration persistenceConfigurer = OracleDataClientConfiguration
.Oracle10
.ConnectionString(connectionStringBuilder => connectionStringBuilder.FromAppSetting("Main.ConnectionString"))
.ProxyFactoryFactory<ProxyFactoryFactory>()
.CurrentSessionContext<ThreadStaticSessionContext>()
.AdoNetBatchSize(25)
.DoNot.ShowSql();
FluentConfiguration cfg = Fluently.Configure()
.Database(persistenceConfigurer)
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<UserMap>());
return cfg.BuildConfiguration();
How should the following mapping configuration be solved?
public abstract class RepositoryEntity
{
public virtual int Id { get; set; }
}
public class Descriptor : RepositoryEntity
{
public virtual String Name { get; set; }
public virtual DateTime Timestamp { get; set; }
}
public class Proxy<TDescriptor> : RepositoryEntity
{
public virtual TDescriptor Descriptor { get; set; }
public virtual Byte[] SerializedValue { get; set; }
};
public class TestUnit : Proxy<Descriptor>
{
};
I receive problems when testing the TestUnit mapping - it says it's impossible to map the item with generic parameters. This happens if I attempt to map every class from the specified before.
If I attempt to map everything, except Proxy<T>, then I receive that there is no persister for the 'TestUnit'.
If I stop inheriting TestUnit from Proxy<Descriptor>, the mapping test works fine.
Does Fluent NHibernate have possibility to automap types inherited from some concrete Class<T> template? Could you help me with mapping these entities?
I used a combination of Fluent and Auto mappings.
Fluent mappings should be used for generics.
Configuration = Fluently.Configure()
.Database(SQLiteConfiguration.Standard.ShowSql().InMemory)
.Mappings(x =>
{
x.FluentMappings.AddFromAssemblyOf<RepositoryEntity>();
x.AutoMappings.Add(autoPersistenceModel);
});