Hi I'm used to have the following entity
public class Foo
{
private IList<Bar> _bars;
public IEnumerable<Bar> Bars { get { return bars; } }
public void Add(Bar bar)
{
/* Validation logic here */
_bars.Add(bar);
}
}
I'm suspecting this won't work with RavenDb or am I'm wrong?
Can I keep my collection with Bars protected from outside manipulating (in other words not allowing foo.Bars.Add(bar);)
I found the solution to use two properties.
public IEnumerable Bars { get { return InnerBars; } }
private List InnerBars { get; set; }}
A private setter on an automatic property is the easiest and most readable way without doing anything special.
public class Foo
{
public IEnumerable<Bar> Bars { get; private set; }
public void Add(Bar bar)
{
Bars.Add(bar);
}
}
Another way would be with attributes:
// pick one or the other
using Newtonsoft.Json // on 1.0
using Raven.Imports.Newtonsoft.Json // on 2.0
...
public class Foo
{
[JsonProperty(PropertyName = "Bars")]
private IList<Bar> _bars;
[JsonIgnore]
public IEnumerable<Bar> Bars { get { return bars; } }
public void Add(Bar bar)
{
_bars.Add(bar);
}
}
Related
Hello i am trying to create a hierarchy of classes using a discriminated unionand it seems i can't serialize them.I keep getting this error :
Newtonsoft.Json.JsonSerializationException: 'Self referencing loop detected for property 'AsRun' with type 'MsgPattern.Message+Run'. Path ''.'
Base class
[Serializable]
public abstract partial class Message {
public enum Type {
WALK = 0,
RUN = 1
}
protected abstract Type Discriminator { get; }
public Type Kind => this.Discriminator;
internal static Message Create(string data) {
var message = JsonConvert.DeserializeObject<Message>(data);
switch (message.Kind) {
case Type.RUN:message= message.AsRun;break;
case Type.WALK:message= message.AsWalk;break;
}
return message;
}
[JsonIgnore]
public bool IsWalk => this.Kind==Type.Walk;
[JsonIgnore]
public bool IsRun => this.Kind==Type.Run;
[JsonIgnore]
public Message.Walk AsWalk => this as Message.Walk;
[JsonIgnore]
public Message.Run AsRun => this as Message.Run;
}
Dervived
partial class Message {
public class Run : Message {
protected override Type Discriminator => Type.RUN;
public string Location { get; set; }
public int Speed { get; set; }
}
}
partial class Message {
public class Walk : Message {
protected override Type Discriminator => Type.WALK;
public int Gait { get; set; }
public bool IsJogging { get; set; }
}
}
Usage
class Program {
static void Main(string[] args) {
Message.Run run = new Message.Run { Location = "asa", Speed = 33 };
string data = JsonConvert.SerializeObject(run);
Message msg=Message.Create(data);
}
}
I will get these type of messages via json and i want to be able to do actions based on their type. I do not understand why i can't serialize them .
P.S I know it's a self-referencing loop but I need those As[something] and Is[Something] fields.
When trying to map, I got this error:
Association references unmapped class: System.Object
My class:
public partial class MessageIdentifier
{
public virtual int ID { get; set; }
public virtual object Item { get; set; }
}
And the convention:
public class MyUsertypeConvention : IPropertyConvention
{
public void Apply(IPropertyInstance instance)
{
if (instance.Type.Name == "Object")
instance.CustomType<string>();
}
}
Kindly suggest how to map?
As a simple (quick, naive) solution - I would suggest to create and map real string property. And then let your setter and getter (or some AOP or listener) to do the "to/from string conversion":
public partial class MessageIdentifier
{
public virtual int ID { get; set; }
public virtual object Item
{
get { return ... my conversion from string; }
set { ItemString = ...my conversion to string; }
}
public virtual string ItemString { get; set; }
}
A smart and preferred (but a bit more challenging) is to create CustomType - which will hide that conversion and support REUSE. Check e.g. here
NHibernate Pitfalls: Custom Types and Detecting Changes
Creating and Testing a Custom NHibernate User Type
Not a satisfactory answer. It doesn't work with class that is generated from xsd by using XML. You can try the following:
public partial class MessageIdentifier
{
public virtual int ID { get; set; }
private object itemField;
public object Item
{
get { return this.itemField; }
set { this.itemField = value; }
}
}
I'm trying to create a discriminator column. This column would hold one of the many statuses available. Like my code will show, each status has a name as well as a background color. Each status shares the same base class.
Here is my code:
public class Item
{
public virtual int Id { get; set; }
public virtual Status ItemStatus { get; set; }
}
public abstract class Status
{
private readonly int _id;
public static readonly Status Foo = new FooStatus(1);
public static readonly Status Bar = new BarStatus(2);
public Status()
{
}
protected Status(int id)
{
_id = id;
}
public virtual int Id { get { return _id; } }
public abstract string Name { get; }
public abstract string BackgroundColor { get; }
}
public class FooStatus : Status
{
public FooStatus()
{
}
public FooStatus(int id)
: base(id)
{
}
public override string Name
{
get { return "Foo Status"; }
}
public override string BackgroundColor
{
get { return "White"; }
}
}
public class BarStatus : Status
{
public BarStatus()
{
}
public BarStatus(int id)
: base(id)
{
}
public override string Name
{
get { return "Bar Status"; }
}
public override string BackgroundColor
{
get { return "Black"; }
}
}
And here is my mapping:
public class ItemMap : ClassMap<Item>
{
public ItemMap()
{
Id(x => x.Id).GeneratedBy.Identity();
DiscriminateSubClassesOnColumn<int>("ItemStatus", 0).AlwaysSelectWithValue();
}
}
Essentially, what I'd like is that if I set ItemStatus to Status.Foo then the ItemStatus column would have a value of 1. What I have now doesn't throw any exceptions, but it always inserts ItemStatus as 0.
This is the inserting code I'm using:
using (var session = sessionFactory.OpenSession())
using (var transaction = session.BeginTransaction())
{
var item = new Item
{
ItemStatus = Status.Foo
};
session.Save(item);
transaction.Commit();
var firstItem = session.Get<Item>(1);
Console.WriteLine(firstItem.ItemStatus.Name);
}
Where can I read up on this topic using FNH?
Before anyone suggests be to check on Google I did search several things but nowhere can I find a full example.
Your SubclassMap would look something like this:
public class FooStatusMap : SubclassMap<FooStatus>
{
public FooStatusMap()
{
DiscriminatorValue(1);
}
}
This is called "table-per-class-hierarchy," and you're right it doesn't look like there are many resources on it out there.
I believe if you don't call DiscriminatorValue in a SubclassMap, NHibernate attempts to discriminate by looking at the name of the subclass being mapped and seeing if it matches up with the value in the discriminator column.
I wouldnt write submaps for all the subclasses you can just do this instead
public class FooMap: ClassMap<T>
{
//other mapping
DiscriminateSubClassesOnColumn("DiscriminatorColumn")
.SubClass<Foo1>(m => { })
.SubClass<Foo2>(m => { })
.SubClass<Foo3>(m => { });
}
Hope that helps
If you're open to the Discriminator column having the class names of the derived classes, you can implement this via automapping.
In your session factory:
private static ISessionFactory CreateSessionFactory()
{
var cfg = new MyMappingConfiguration();
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("MyConnectionKey")).FormatSql().ShowSql()
)
.Mappings(m => m.AutoMappings.Add(AutoMap.AssemblyOf<Status>(cfg)
.IncludeBase<Status>()
.Conventions.Add<PrimaryKeyConvention>()))
.BuildSessionFactory();
}
Then add the MyMappingConfiguration override:
public class MappingConfiguration : DefaultAutomappingConfiguration
{
public override bool IsId(Member member)
{
return member.Name == member.DeclaringType.Name + "Id";
}
public override bool IsDiscriminated(Type type)
{
return true;
}
}
Hope that h
I basically have:
Public Class Job: MyBaseClass
{
public virtual string JobInformation {get;set;}
...
public virtual List<Item> JobItems {get;set;}
}
I was using a List and initializing it (JobItems = new List();) in the constructor. However, I was getting an Exception (Nhibernate.Collection.Generic.PersistentGenericBag)
I read this question and it said I should use IList instead.
So now I have
public class Job: MyBaseClass
{
public virtual string JobInformation {get;set;}
...
public virtual IList<Item> JobItems {get;set;}
}
public virtual void AddItem(Item item)
{
//snip validation
this.JobItems.Add(item);
}
However, it throws a NullReferenceException because JobItems isn't initialized yet. When does NHibernate initialize this collection? Or how can I solve this issue?
use a backing field and on return make sure it isn't null
public IList<Item> JobItems
{
get { return _jobItems ?? (_jobItems = new List<Item>()); }
set { _jobItems = value; }
}
You should initialize the collection if it hasn't been previously initialized.
private IList<Item> _jobItems;
public IList<Item> JobItems
{
get
{
return _jobItems ?? (_jobItems = new List<Item>());
}
private set
{
_jobItems = value;
}
}
I am using the Specification pattern, and have a working implementation (taken from the WhoCanHelpMe Codeplex project) for getting data via NLinq, generic repositories and all that goodness.
The root method is:
public IList<Case> GetCasesByUsername(string username)
{
CaseByUserNameSpecification spc = new CaseByUserNameSpecification(username);
return this.caseRepository.FindAll(spc).ToList();
}
The FindAll() method does the following:
public IQueryable<T> FindAll(ILinqSpecification<T, T> specification)
{
return specification.SatisfyingElementsFrom(this.Session.Linq<T>());
}
And, SatisfyingElementsFrom() does this:
public virtual IQueryable<TResult> SatisfyingElementsFrom(IQueryable<T> candidates)
{
if (this.MatchingCriteria != null)
{
return candidates.Where(this.MatchingCriteria).ToList().ConvertAll(this.ResultMap).AsQueryable();
}
return candidates.ToList().ConvertAll(this.ResultMap).AsQueryable();
}
So, for querying cases by CaseNb property of a Case, it's pretty straight-forward. A Specification like the one below works for me and gets the cases I'd want.
public class CaseByCaseNbSpecification : QuerySpecification<User>
{
private string caseNb;
public CaseByCaseNbSpecification(string caseNb)
{
this.caseNb = caseNb;
}
public string UserName
{
get { return this.caseNb; }
}
public override Expression<Func<Case, bool>> MatchingCriteria
{
get { return u => u.CaseNb.Equals(this.caseNb, StringComparison.CurrentCultureIgnoreCase); }
}
}
However, I am at a loss to understand how to do this when crossing multiple entities. What I'd like to have is a Specification that allows me to get Cases by UserName. Basically, in the database, there are three tables and these have been carried into entities. Here's are entities:
Here's the Case class:
public class Case : Entity
{
private ICollection<CaseUser> caseUsers = new HashSet<CaseUser>();
public virtual Patient Patient { get; set; }
public virtual string CaseNb { get; set; }
...
public virtual IEnumerable<CaseUser> CaseUsers { get { return caseUsers; } }
}
Here's the CaseUser:
public class CaseUser : Entity
{
public virtual Case Case { get; set; }
public virtual User User { get; set; }
...
}
And, User:
public class User : Entity
{
private ICollection<CaseUser> caseUsers = new HashSet<CaseUser>();
public virtual Account Account { get; set; }
public virtual string UserName { get; set; }
...
public virtual IEnumerable<CaseUser> CaseUsers { get { return caseUsers; } }
}
How would I write the Expression to get the data across the association table?
I believe your specification implementation should look something like this:
public class CaseByUsernameSpecification : QuerySpecification<Case>
{
private string userName;
public CaseByUsernameSpecification(string userName)
{
this.userName = userName;
}
public string UserName
{
get { return this.userName; }
}
public override Expression<Func<Case, bool>> MatchingCriteria
{
get { return c => c.CaseUsers.Any(cu => cu.User.Username == this.userName); }
}
}