i m getting the exception "No persister for: MVCTemplate.Common.Entities.User" . I Google this issue and apply all the solution i found. but all are useless for me.
Does anyone know what i m doing wrong ?
my User Class code is
public class User
{
public virtual Guid UserID { get; private set; }
public virtual string UserName { get; set; }
public virtual string Password { get; set; }
public virtual string FullName { get; set; }
public virtual string Email { get; set; }
public virtual TimeSpan LastLogin { get; set; }
public virtual bool IsActive { get; set; }
public virtual DateTime CreationDate { get; set; }
public virtual IList<UserInRole> UserInRoles { get; set; }
}
User Mapping :
public class UserMap : ClassMap<User>
{
public UserMap()
{
Table("tblUsers");
Id(user => user.UserID).GeneratedBy.GuidComb();
Map(user => user.UserName).Not.Nullable();
Map(user => user.Password).Not.Nullable();
Map(user => user.FullName).Not.Nullable();
Map(user => user.Email).Not.Nullable();
Map(user => user.LastLogin).Not.Nullable();
Map(user => user.IsActive).Nullable();
Map(user => user.CreationDate).Not.Nullable();
HasMany(user => user.UserInRoles);
}
}
FNH Configuration :
return Fluently.Configure()
.Database(FluentNHibernate.Cfg.Db.MsSqlConfiguration.MsSql2008
.ConnectionString(c => c.FromConnectionStringWithKey("FNHConnection"))
)
.Mappings(m =>
m.FluentMappings.AddFromAssemblyOf<User>())
.BuildSessionFactory();
Thanks
Double check that your mapping class is public.
Check that you have something like this in your fluent config....
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<UserMap>())
The following will cause this error in simple terms:
private static void MainFN()
{
using (var session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
Data[] balance = new Data[12];
for (int i = 0; i < 12; i++)
{
balance[i] = new Data();
balance[i].Test1 = "Example Data " + (i + 1).ToString();
balance[i].Test2 = i + 11;
balance[i].Test3 = (i % 2 == 0);
session.SaveOrUpdate(balance[i]); //Should be like this
}
//session.SaveOrUpdate(balance); //This will give the error
Related
I've created the following domain classes:
public class Car
{
public virtual int Id { get; set; }
public virtual string Registration { get; set; }
public virtual User ResponsibleContact { get; set; }
protected Car()
{}
public Fahrzeug(User responsibleContact, string registration)
{
ResponsibleContact = responsibleContact;
Registration = registration;
ResponsibleContact.Cars.Add(this);
}
}
public class User
{
public virtual int Id { get; set; }
public virtual byte[] EncryptedUsername { get; set; }
public virtual byte[] EncryptedPassword { get; set; }
public virtual IList<Car> Cars { get; private set; }
public virtual string Username
{
get
{
var decrypter = UnityContainerProvider.GetInstance().UnityContainer.Resolve<IRijndaelCrypting>();
return decrypter.DecryptString(EncryptedUsername);
}
}
protected User()
{ }
public User(byte[] encryptedUser, byte[] encryptedPassword)
{
Cars = new List<Car>();
EncryptedUsername = encryptedUser;
EncryptedPassword = encryptedPassword;
}
}
and the mapping classes:
public class CarMap : ClassMap<Car>
{
public CarMap()
{
Id(c => c.Id).GeneratedBy.Native();
Map(c => c.Registration);
References(c => c.ResponsibleContact).Not.Nullable();
}
}
public class UserMap : ClassMap<User>
{
public UserMap()
{
Id(st => st.Id).GeneratedBy.Native();
Map(st => st.EncryptedUsername).Column("Username");
Map(st => st.EncryptedPassword).Column("Password");
HasMany(st => st.Cars).Inverse().AsBag();
}
}
If I query some Member objects, I get the members, but the cars collection is empty!
If I query some Cars I got all the cars with the right Member. But within the member, the cars collection is also empty!
Is there anybody who has an Idea of what can happened?
you have to make sure the foreign key column of the collection and the reference is the same otherwise there is a mismatch.
References(c => c.ResponsibleContact, "ResponsibleContact_id").Not.Nullable();
and
HasMany(st => st.Cars).Inverse().KeyColumn("ResponsibleContact_id");
I try a simple Test Application with FluentNhibernate but it doesnt work as I expected.
Here are my classes:
public class Document : DataEntity
{
public virtual string Title { get; set; }
public virtual string FileName { get; set; }
public virtual DateTime LastModificationDate { get; set; }
public virtual User LastModificationBy { get; set; }
public virtual byte[] Content { get; set; }
public virtual User Owner { get; set; }
}
public class User : DataEntity
{
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual string Login { get; set; }
public virtual string PasswordHash { get; set; }
public virtual string Email { get; set; }
public virtual IList<Document> OwnedDocuments { get; set; }
public User()
{
this.OwnedDocuments = new List<Document>();
}
}
internal class UserMapping : ClassMap<User>
{
public UserMapping()
{
this.Id(x => x.Id);
this.Map(x => x.FirstName);
this.Map(x => x.LastName);
this.HasMany(x => x.OwnedDocuments).Inverse();
}
}
public DocumentMapping()
{
this.Id(x => x.Id);
this.Map(x => x.Title);
this.Map(x => x.FileName).Not.Nullable();
this.Map(x => x.LastModificationDate).Index("IDX_ModificationDate");
this.Map(x => x.Content);
this.References(x => x.LastModificationBy).Column("LastModificationBy");
this.References(x => x.Owner).Column("Owner");
this.Table("Document");
}
I try to test it with the following code
using (var transaction = Session.BeginTransaction())
{
var users = this.kernel.Get<IRepository<User>>();
var document = this.kernel.Get<IRepository<Document>>();
var user = new User { Login = "Blubb" };
users.Add(user);
var list = Builder<Document>.CreateListOfSize(50).All().With(x => x.Owner = user).Build().ToList();
var list2 = Builder<Document>.CreateListOfSize(50).All().Build().ToList();
list.ForEach(x => user.OwnedDocuments.Add(x));
document.Add(list);
document.Add(list2);
transaction.Commit();
var i = document.All().Count();
i.Should().Be(50);
var docs = ((IGuidKeyedRepository<User>)users).FindBy(user.Id).OwnedDocuments.Count();
docs.Should().Be(50);
}
The first problem is, why is the document count always 0 when I dont call document.Add(list);? I thought when I add some documents to the document collection of the user, they will be automaticly added to the documents?
And why ist the last test 100? Because I filter on the documents belongs to that user.
It looks like you need to set a cascade option on the child collection OwnedDocuments
this.HasMany(x => x.OwnedDocuments).Inverse().Cascade.AllDeleteOrphan();
The setting above will save all the children if you add any to the collection and if you remove them from the collection and save the object it will delete those child objects. You can find out more information about these settings in the nhibernate documentation:
http://www.nhforge.org/doc/nh/en/
I have classes
public abstract class Content : IContent
{
public virtual Guid Id { get; protected set; }
public virtual IPage Parent { get; set; }
public virtual DateTime Created { get; set; }
/* ... */
}
public abstract class Page : Content, IPage
{
public virtual string Slug { get; set; }
public virtual string Path { get; set; }
public virtual string Title { get; set; }
/* ... */
}
public class Foo : Page, ITaggable
{
// this is unique property
// map to joined table
public virtual string Bar { get; set; }
// this is a unique collection
public virtual ISet<Page> Related { get; set; }
// this is "shared" property (from ITaggable)
// map to shared table
public virtual ISet<Tag> Tags { get; set; }
}
And as a result I'd like to have the following tables. I've tried implementing tons of different IConventions, but even the hierarchy mappings (table-per-abstract-hierarchy / table-per-concrete-subclass) seem to fail.
Content
Id
Type (discriminator)
ParentId
Created
Slug
Path
Title
Content_Tags (Tags from ITaggable)
ContentId
TagId
Content$Foo
Bar
Content$Foo_Related
ParentFooId
ChildPageId
I already have ugly, working fluent mappings, but I would like to get rid of some ugliness
public class ContentMapping : ClassMap<Content>
{
public ContentMapping()
{
Table("Content");
Id(x => x.Id).GeneratedBy.GuidComb();
References<Page>(x => x.Parent, "ParentId");
Map(x => x.Created);
DiscriminateSubClassesOnColumn("Type");
}
}
public class PageMapping : SubclassMap<Page>
{
public PageMapping()
{
Map(x => x.Slug);
Map(x => x.Path);
Map(x => x.Title);
}
}
public class ConcreteContentMapping<T> : SubclassMap<T> where T : Content, new()
{
public ConcreteContentMapping() : this(true) { }
protected ConcreteContentMapping(bool mapJoinTable)
{
DiscriminatorValue(typeof(T).FullName);
MapCommonProperties();
if(mapJoinTable)
{
MapJoinTableWithProperties(CreateDefaultJoinTableName(), GetPropertiesNotFrom(GetContentTypesAndInterfaces().ToArray()));
}
}
private void MapCommonProperties()
{
if (typeof(ITagContext).IsAssignableFrom(typeof(T)))
{
Map(x => ((ITagContext)x).TagDirectory);
}
if (typeof(ITaggable).IsAssignableFrom(typeof(T)))
{
HasManyToMany(x => ((ITaggable)x).Tags).Table("Content_Tags").ParentKeyColumn("ContentId").ChildKeyColumn("TagId").Cascade.SaveUpdate();
}
}
/* ... */
// something I would like to get rid of with automappings...
protected void MapCollectionProperty(JoinPart<T> table, PropertyInfo p)
{
var tableName = ((IJoinMappingProvider)table).GetJoinMapping().TableName + "_" + p.Name;
var elementType = p.PropertyType.GetGenericArguments()[0];
var method = table.GetType().GetMethods().Where(m => m.Name == "HasManyToMany")
.Select(m => new { M = m, P = m.GetParameters() })
.Where(x => x.P[0].ParameterType.GetGenericArguments()[0].GetGenericArguments()[1] == typeof(object))
.FirstOrDefault().M.MakeGenericMethod(elementType);
dynamic m2m = method.Invoke(table, new object[] { MakePropertyAccessExpression(p)});
m2m.Table(tableName).ParentKeyColumn("Parent" + typeof(T).Name + "Id").ChildKeyColumn("Child" + elementType.Name + "Id");
}
protected Expression<Func<T, object>> MakePropertyAccessExpression(PropertyInfo property)
{
var param = Expression.Parameter(property.DeclaringType, "x");
var ma = Expression.MakeMemberAccess(param, property);
return Expression.Lambda<Func<T, object>>(ma, param);
}
}
How do I get the same result with automappings?
I've got the following classes:
public class Client {
public virtual Guid ClientID { get; set; }
public virtual string ClientName { get; set; }
public virtual IList<ClientMonthlyRevenue> Revenue { get; set; }
...
public virtual void SetMonthlyRevenue(int year, int month, double revenue)
{
// Make sure it's not null... this might happen depending on how the client is created
if (Revenue == null)
Revenue = new List<ClientMonthlyRevenue>();
// Check for existance - we don't want any duplicates
ClientMonthlyRevenue clientMonthlyRevenue = Revenue.Where(x => x.Year == year && x.Month == month).FirstOrDefault();
if (clientMonthlyRevenue == null)
{
// If it doesn't exist, create a new one and add to the list
clientMonthlyRevenue = new ClientMonthlyRevenue(this, year, month, revenue);
this.Revenue.Add(clientMonthlyRevenue); // This is the line throwing the error
}
else
{
// If it exists, just update it
clientMonthlyRevenue.Revenue = revenue;
}
}
}
public class ClientMonthlyRevenue {
public virtual Client ParentClient { get; set; }
public virtual int Year { get; set; }
public virtual int Month { get; set; }
public virtual double Revenue { get; set; }
...
}
And these two mappings:
public class ClientMap : ClassMap<Client>
{
Id(x => x.ClientID).GeneratedBy.Assigned();
Map(x => x.ClientName);
HasMany<ClientMonthlyRevenue>(x => x.Revenue)
.Table("ClientMonthlyRevenue")
.KeyColumn("ClientID")
.Cascade.All()
.Fetch.Join();
}
public class ClientMonthlyRevenueMap : ClassMap<ClientMonthlyRevenue>
{
CompositeId()
.KeyReference(x => x.Client, "ClientID")
.KeyProperty(x => x.Year)
.KeyProperty(x => x.Month);
Map(x => x.Revenue);
}
When I get a Client from the database:
Client client = Session.Get<Client>(clientID);
all the data is there, which is great. But when I try to add a new ClientMonthlyRevenue child:
client.Revenue.Add(new ClientMonthlyRevenue(this.ClientID, year, month, revenue));
I get the error:
Collection was of a fixed size.
Am I missing or misunderstanding something here? And what do I need to modify to be able to add items to this persisted list?
I would change the Client object to have the following:
public class Client
{
public Client()
{
Revenue = new List<ClientMonthlyRevenue>();
}
public virtual Guid ClientID { get; set; }
public virtual string ClientName { get; set; }
public virtual IList<ClientMonthlyRevenue> Revenue { get; set; }
public virtual void AddRevenue(ClientMonthlyRevenue revenue)
{
revenue.ParentClient = this;
Revenue.Add(revenue);
}
}
Then you can call like this:
public void TestMapping()
{
session.BeginTransaction();
var client = new Client{ClientID = Guid.NewGuid()};
session.SaveOrUpdate(client);
client = session.Get<Client>(client.ClientID);
client.AddRevenue(new ClientMonthlyRevenue(2001,07,1200));
session.Transaction.Commit();
}
The error you are receiving sounds like it could be created higher up in the stack. I was able to recreate your scenario. See full source: https://gist.github.com/1098337
have you tried to mark your collection as Inverse? I dont know if it could help.
HasMany<ClientMonthlyRevenue>(x => x.Revenue)
.Table("ClientMonthlyRevenue")
.KeyColumn("ClientID")
.Cascade.All()
.Fetch.Join()
.Inverse();
I have an Account table that stores Master Accounts and Sub Accounts. Master Accounts are basically the same as Sub Accounts except that they can have an associated Company. Account is an abstract class and both MasterAccount and SubAccount derive from it.
A MasterAccount is any account entry with a null ParentAccountId. If an Account record has a ParentAccountId then it is a SubAccount and the ParentAccountId references the AccountId field for the MasterAccount.
I am trying get FluentNhibernate mappings for them.
The classes look like the following
public class Account : EntityBase
{
public Account() { }
public virtual string AccountNumber { get; set; }
public virtual string AccountName { get; set; }
public virtual string ContactRole { get; set; }
public virtual bool EmailBillDataFile { get; set; }
public virtual bool EmailBill { get; set; }
public virtual bool PostBill { get; set; }
public virtual BillingMethod BillingMethod { get; set; }
public virtual BillingAddressType BillingAddressType { get; set; }
public virtual Contact Contact { get; set; }
public virtual bool IsInvoiceRoot { get; set; }
public virtual string Password { get; set; }
public virtual bool HasRequestedInvoicing { get; set; }
public virtual bool IsInternational { get; set; }
public virtual decimal AmountPaid { get; set; }
public virtual decimal PreviousBill { get; set; }
public virtual void MakePayment(decimal amount)
{
MakePayment(amount, null);
}
public virtual void MakePayment(decimal amount, string invoiceNumber)
{
AmountPaid += amount;
if (string.IsNullOrEmpty(invoiceNumber))
LogActivity(string.Format("Made payment of {0:c}", amount));
else {
LogActivity(string.Format("Made payment of {0:c} on Invoice '{1}'", amount, invoiceNumber));
}
}
public virtual Invoice CreateInvoice()
{
Invoice invoice;
invoice = IsInternational ? new NoGstInvoice() : new Invoice();
// Can update invoice properties that rely on account data here.
return invoice;
}
#region Business Rules
public override IEnumerable<RuleViolation> GetRuleViolations()
{
if (string.IsNullOrEmpty(AccountName))
yield return new RuleViolation("Account Name required", "AccountName");
if (string.IsNullOrEmpty(AccountNumber))
yield return new RuleViolation("Acocunt Number required", "AccountNumber");
if (string.IsNullOrEmpty(Password))
yield return new RuleViolation("Password required", "Password");
yield break;
}
#endregion
}
public class MasterAccount : Account
{
private Company _company;
private IList<SubAccount> _subAccounts;
public MasterAccount() : this(null) { }
public MasterAccount(Company company)
{
_company = company;
_subAccounts = new List<SubAccount>();
}
public virtual Company Company
{
get { return _company; }
}
public virtual IEnumerable<SubAccount> SubAccounts
{
get { return _subAccounts; }
}
public virtual SubAccount CreateSubAccount(string accountNumber, string accountName)
{
var subAccount = new SubAccount(this)
{
AccountName = accountName,
AccountNumber = accountNumber,
Contact = this.Contact,
ContactRole = this.ContactRole,
PreviousBill = 0,
AmountPaid = 0,
BillingAddressType = this.BillingAddressType,
BillingMethod = this.BillingMethod,
IsInternational = this.IsInternational,
IsInvoiceRoot = false,
EmailBill = this.EmailBill,
EmailBillDataFile = this.EmailBillDataFile,
Password = this.Password,
PostBill = this.PostBill
};
return subAccount;
}
}
public class SubAccount : Account
{
private MasterAccount _masterAccount;
public SubAccount() { }
public SubAccount(MasterAccount master)
{
_masterAccount = master;
}
public virtual MasterAccount MasterAccount
{
get { return _masterAccount; }
}
}
The mappings I have are:
public class AccountMap : ClassMap<Account>
{
public AccountMap()
{
Table("Account");
Id(x => x.Id).Column("AccountId").GeneratedBy.Identity();
Map(x => x.AccountName).Length(50).Not.Nullable();
Map(x => x.AccountNumber).Length(10).Not.Nullable();
Map(x => x.ContactRole).Length(50);
Map(x => x.BillingMethod).Not.Nullable();
Map(x => x.EmailBill).Not.Nullable();
Map(x => x.PostBill).Not.Nullable();
Map(x => x.EmailBillDataFile).Not.Nullable();
Map(x => x.BillingAddressType).Not.Nullable();
Map(x => x.IsInvoiceRoot).Not.Nullable();
Map(x => x.HasRequestedInvoicing).Not.Nullable();
Map(x => x.IsInternational).Not.Nullable();
Map(x => x.PreviousBill).Not.Nullable();
Map(x => x.AmountPaid).Not.Nullable();
Map(x => x.Password).Length(20).Not.Nullable();
References(x => x.Contact).Column("ContactId").Not.Nullable();
DiscriminateSubClassesOnColumn("ParentAccountId");
}
}
public class MasterAccountMap : SubclassMap<MasterAccount>
{
public MasterAccountMap()
{
References(x => x.Company).Column("CompanyId");
HasMany(x => x.SubAccounts).KeyColumn("ParentAccountId").Inverse().Cascade.All();
}
}
public class SubAccountMap : SubclassMap<SubAccount>
{
public SubAccountMap()
{
References(x => x.MasterAccount).Column("ParentAccountId").Not.Nullable();
}
}
However, when I execute the following test:
[Test]
public void Can_add_subAccount_to_database()
{
var master = Session.Get<MasterAccount>(1);
var subAccount = master.CreateSubAccount("TST123", "Test Account");
Session.Save(subAccount);
Session.Flush();
Session.Clear();
var fromDb = Session.Get<SubAccount>(subAccount.Id);
Assert.AreNotSame(subAccount, fromDb);
}
I get an exception on the Session.Save(subAccount); line.
System.ArgumentOutOfRangeException : Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
I do not get the exception if I comment out the References mapping in SubAccountMap.
Any help on correctly mapping this relationship is appreciated.
Seems I needed to use the Formula method off DiscriminateSubClassesOnColumn
DiscriminateSubClassesOnColumn("").Formula("case when parentaccountid is null then '0' else '1' end");
and then use the following in each of the subclasses
DiscriminatorValue("0"); // In MasterAccountMap
DiscriminatorValue("1"); // in SubAccountMap
see http://wiki.fluentnhibernate.org/Fluent_mapping