I'm getting this nasty error in Castle Active Record (wrapped around NHibernate) when I try to save a class:
Invalid index n for this SqlParameterCollection with Count=m
I know that this error is caused by a property being mapped multiple times in a class however I'm not sure how to get around it. I have two child classes that both map back to the class in question using the same column (IpAddressNumber). Also IpAddressNumber is the primary key of the class, which results in NHibernate trying to map the IpAddressNumber property three times (just a guess.)
Here is the class:
[ActiveRecord(Lazy=true)]
public class DeviceConfiguration : UsersDatabase<DeviceConfiguration>
{
[PrimaryKey]
public virtual long IPAddressNumber { get; set; }
[BelongsTo("IPAddressNumber", Lazy = FetchWhen.OnInvoke)]
public virtual Vehicle Vehicle { get; set; }
[BelongsTo("IPAddressNumber", Lazy = FetchWhen.OnInvoke)]
public virtual JBusConfiguration JBusConfiguration { get; set; }
}
Any help would be greatly appreciated...
I ended up having to just remove the second association altogether to get around this issue. Not a great solution but the only one I could find.
Related
I have a many-to-many relationship between Assignment and User
When trying to delete an user from an assignment, I see all users are loaded in the collection.
How to I avoid that?
public class User
{
public virtual int Id { get; private set; }
public virtual IList<Assignment> Assignments { get; set; }
}
public class Assignment
{
public virtual int Id { get; private set; }
public virtual ICollection<User> Users { get; set; }
}
Mappings:
HasManyToMany(user => user.Assignments).Table("UserToAssignment").ParentKeyColumn("UserId").ChildKeyColumn("AssignmentId").Inverse().ExtraLazyLoad();
HasManyToMany(productAssignment => productAssignment.Users).AsSet().Table("UserToAssignment").ParentKeyColumn("AssignmentId").ChildKeyColumn("UserId").LazyLoad();
Calling code:
assignment.Users.Remove(user)
Initially I used Bag instead of Set for Assignment mapping, but when updating it, it was deleting and then reinserting alot of rows in the AssignmentsToUsers table. So I changed to using Set.
But now I see a problem with using Set: it brings all data in memory.
What is the recommended way of doing this?
You can't avoid this and I would ignore it if performance is acceptable. If performance is a problem, there are three ways I can think of to tackle it:
If the other side of the collection (User.Assignments) is lighter weight then remove the assignment from the user instead.
Model the many-to-many table and delete the object directly. You would have to be certain that the Users collection is not going to be loaded prior to this because the in-memory representation will still contain the deleted record.
Direct delete using SQL -- this has the same caveat as #2.
You should use extra lazy mode also for Assignment.Users.
I am using Nhibernate and I have a problem when fetching a base class with multiple derived classes (each class mapping a different table). When I watch the request, Nhibernate joins on every derived tables which has a huge an impact on the performances...
Here is a simplified vision of my classes :
public class Animal{
public virtual int ID { get; set;}
public virtual string Name { get; set;}
}
public class Dog : Animal{
//others properties
}
public class Cat: Animal{
//others properties
}
public class Person{
public virtual int ID { get; set;}
public virtual IEnumerable<Animal> Animals { get; set;}
}
A person has a list of Animals and I just want their names. The example is not perfect and more it's more complicated (a banking program) but it reflect well my problematic.
I KNOW it can be done differently etc, but it is a legacy so I don't have a choice...
Thanks in advance.
IMO NHibernate will only joind tables which contain projected columns. define a query but do not return Person but project into a dto/anonymous class the properties you need
After all, I created a class AnimalBase which is inherited by Dog, Cat and so forth and a class Animal without any child (both having the interface IAnimal).
As in 95% of my request, I only need Animal, I reference this class in my other objects like Person.
Not perfect but I did not find anything better...
Thanks Firo for your help.
What is best way to map following classes?
I have classes where communication is "base" class as it contains shared data for either option. There can be only communication to ComSms or to ComEmail. The problem is i am unsure how to proceed with the mapping.
I would like if at all possible use one repository for accessing the Objects.
Class Set Up
public interface ICommunication
{
}
public Communication: ICommunication{
//PK and FK on this item
public virtual int CommunicationId{get;set;}
public virtual string Name {get;set;}
public virtual string Surname {get;set;}
public virtual DateTime Date{get;set;}
//I am expecting to switch on type between ComSms and ComEmail
public virtual Type {get;set;}
}
public ComSms : Communication{
public virtual string Number {get;set;}
public virtual string Text {get;set;}
}
public ComEmail : Communication{
public virtual string Subject{get;set;}
public virtual string Body {get;set;}
public virtual string Address {get;set;}
}
Database
Imagined usage
ICommunication smsToSave = new ComSms()
smsToSave.Name = "UserName";
smsToSave.Surname ="UserSurname";
smsToSave.Date = DateTime.Now;
smsToSave.Type = 1;
smsToSave.Number ="123456789";
smsToSave.Text = "Hello boys";
CommunicationRepository.Save(smsToSave)
I don't have time to write out any mappings but what you need is a discriminator. This would allow you to say have a CommunicationType foreign key in your table that could link to either the Sms or Email table. The nice thing about discriminators is that NHibernate takes care of everything for you.
And yes, seeing as your aggregate root is Communication then you could use one repository for accessing either of your communication types.
ok
i have resolved it by separated repositories that handles ever part of it instead
i could not bind it correctly, so chosen to go around the problem.
if you find any way of doing this please let me know
public class SearchText
{
public virtual int Id { get; set; }
public virtual string Text { get; set; }
}
public class SearchTextLog
{
public virtual int Id { get; set; }
public virtual SearchText SearchText { get; set; }
public virtual User User { get; set; }
public virtual int SearchCount { get; set; }
public virtual DateTime LastSearchDate { get; set; }
}
I am trying to select the top 5 SearchText items based on the sum of their count within the SearchTextLog. Currently I have only been able to resolve this by first performing a query to get the top 5 items, and then using the result within a second query. I was wondering if someone could show me the light and teach me how I could integrate these two seperate queries into a single unit.
Here is what I have currently:
var topSearchCriteria = Session.CreateCriteria(typeof (SearchTextLog))
.SetProjection(Projections.ProjectionList()
.Add(Projections.GroupProperty("SearchText.Id"))
.Add(Projections.Alias(Projections.Sum("SearchCount"), "SearchCount")))
.AddOrder(Order.Desc("SearchCount"))
.SetMaxResults(topSearchLimit)
.List<int>();
return Session.CreateCriteria<SearchText>()
.Add(Restrictions.In("Id", topSearchCriteria.ToArray()))
.List<SearchText>();
Edit:
Oh no, I just realised my current solution will lose the important order by of the results. So I will definitely have to incorporate the queries. :-/
Edit:
I tried a bidirectional mapping too to allow the following statement, however, I can't get it to return SearchText items. It simply complains that the SearchText properties aren't in a grouping.
return Session.CreateCriteria<SearchText>()
.CreateAlias("SearchTextLogs", "stl")
.AddOrder(Order.Desc(Projections.Sum("stl.SearchCount")))
.SetMaxResults(topSearchLimit)
.SetResultTransformer(Transformers.AliasToEntityMap)
.List<SearchText>();
Excuse my ignorance, but Nhibernate is completely new to me, and requires a completely different way of thinking.
Ok, I think I have figured out a solution.
My original solution as per my question won't work because NHibernate doesn't yet support the ability to do a group by property without adding it to the select clause (see: link text).
While fooling around however, I came across these cool things called ResultTransformers. Using the AliasToBean result transformer Nhibernate will automatically map the alias's I give to each projection item to properties by the same name within a type I specify. I simply specified my SearchText object (however, I had to add an additional TotalSearchCount property for the sum projection item). It populated my objects perfectly and returned them.
return Session.CreateCriteria(typeof(SearchTextLog))
.CreateAlias("SearchText", "st")
.SetProjection(Projections.ProjectionList()
.Add(Projections.Alias(Projections.GroupProperty("st.Id"), "Id"))
.Add(Projections.Alias(Projections.GroupProperty("st.Text"), "Text"))
.Add(Projections.Alias(Projections.Sum("SearchCount"), "TotalSearchCount")))
.SetMaxResults(topSearchLimit)
.AddOrder(Order.Desc("TotalSearchCount"))
.SetResultTransformer(Transformers.AliasToBean(typeof(SearchText)))
.List<SearchText>();
I am surprised this wasn't easier to do. It's taken me about 4 to 5 hours of research and dev to figure this one out. Hopefully my NHibernate experience will get easier with more and more experience.
I hope this helps someone else out there!
doesn't this work?
var critterRes = Session.CreateCriteria(typeof (SearchTextLog))
.SetProjection(Projections.ProjectionList()
.Add(Projections.GroupProperty("SearchText"))
.Add(Projections.Property("SearchText"))
.Add(Projections.Alias(Projections.Sum("SearchCount"), "SearchCount")))
.AddOrder(Order.Desc("SearchCount"))
.SetMaxResults(topSearchLimit)
.List<SearchText>()
I have a legacy DB which uses a guid to map children to the parent entity.
In my domain layer, I'd prefer to obscure this quirk, so I'd like to have my parent entity look like this:
public class Parent
{
virtual public int Id {get; protected set; }
virtual public string ParentContent { get; set; }
virtual public Guid ReferenceId { get; set; }
virtual public IDictionary<int,Child> Children { get; set; }
}
public class Child
{
virtual public int Id { get; protected set; }
virtual public Parent { get; set; }
virtual public string ChildContent { get; set; }
}
The parent would then map each Child.Id to Child in the Children dictionary. The Child mapping works fine, but I can't seem to find a reasonable mapping for the parent.
A field named ParentReferenceID exists in both Parent and Child tables, so I've attempted to map this with something like this:
mapping.HasMany<Broker>(x => x.Children)
.Table("Child")
.KeyColumn("ParentReferenceID")
.Inverse()
.AsMap<long>(index=>index.Id,val=>val.Type<Broker>());
Unfortunately, this produces an error:
The type or method has 2 generic parameter(s), but 1 generic argument(s) were provided. A generic argument must be provided for each generic parameter.
To simplify my problem, I started by trying Bag semantics, replacing the Parent's IDictionary with an IList. This was mapped using something like:
mapping.HasMany<Broker>(x => x.Brokers)
.Table("Child")
.KeyColumn("ParentReferenceId")
.Inverse()
.AsBag();
That produces the more obvious exception,
System.Data.SqlClient.SqlException: Operand type clash: uniqueidentifier is incompatible with int
Unfortunately, I can't seem to figure out the right way to tell it to join on the ReferenceID field. What's the right way to do that? I'd prefer the dictionary, but I'd be reasonably happy if I could even get the bag to work.
For clarity, I'm using a build of Fluent that is bundled with a recent SharpArchitecture pulled from git. The Fluent dll is marked version 1.0.0.594, but if a more recent build would help, I'm flexible.
Further digging has led me to a solution for the Bag case, though the dictionary is still giving me a bit of trouble.
The solution requires a patch to Fluent NHibernate's OneToManyPart mapping class. (Hat tip to This bug report: Could not map a one-to-many relationship where the key is not the primary key.
mapping.HasMany(x => x.Children)
.Table("Child").KeyColumn("ParentReferenceId")
.PropertyRef("ReferenceId")
.Inverse()
.AsBag();
Theoretically, AsMap should work almost the same way, but for some reason that I'm not entirely clear on, it doesn't work for me. I'll explore that later, but I'm open to suggestions.