I have following problem:
We have multi-value fields in DB like ProductLineIdList which stores every allowed productLines separated by comma (for example "2,13,27,33"). I would like to map this field to IList (list with 4 entities). Is it possible to do that? Thx
How about saving the productLines as a string, and then use a non mapped property to return the list of product lines? I suspect you'd have a hard time pulling that off with pure NHibernate.
public class Product
{
// protected so we can't see this
protected virtual string productLines { get; set; }
// instruct NHibernate to ignore this property!
public IList<string> ProductLines
{
get
{
if (!string.IsNullOrEmpty(productLines))
{
return productLines.Split(',').ToList();
}
else
{
return new List<string>();
}
}
}
}
Related
I have this object
[DataContract]
public class FilterList<T> : List<T>
{
[DataMember]
public int Total { get; set; }
}
In my controller:
public ActionResult<FilterList<MyPOCO>> GetFilteredResult(string filter)
{
var l = new FilterList<MyPOCO>();
l.Total = 123456;
// Continue to add many MyPOCO objects into the list
return l;
}
I can get back the MyPOCO list at the client side, but the l.Total is NOT serialize. May I know what I had done wrongly?
Here is a workaround , you could try to use [JsonObject] attribute . But the items will not be serialized, because a JSON container can have properties, or items -- but not both. If you want both, you will need to add a synthetic list property to hold the items.
[JsonObject] will also cause base class properties such as Capacity to be serialized, which you likely do not want. To suppress base class properties, use MemberSerialization.OptIn. Thus your final class should look something like:
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class FilterList<T> : List<T>
{
[JsonProperty]
public int Total { get; set; }
[JsonProperty]
List<T> Items
{
get
{
return this.ToList();
}
set
{
if (value != null)
this.AddRange(value);
}
}
}
Result:
I'm using NHibernate 3.33 and QueryOver with Postgre 9.2.
I've got two entities:
public class User {
public virtual string Name { get; set; }
public virtual IList<Reports> Reports { get; set; }
}
and
public class Report {
public virtual string Type { get; set; }
public virtual DateTime ReportDate { get; set; }
public virtual User Author { get; set; }
}
with association - one-to-many (I didn't append additional fields to entities like Id or Name to snippets above). Some report's types are avaliable - month, day.
My goal is to get summary for user - find out whether user has day-report and month-report for current day.
Note: month-report's ReportDate looks like first day of month. Also I want to get it as one row (if it was an SQL) to transform to dto:
public class UserSummaryDto {
public bool HasDayReport { get; set; }
public bool HasMonthReport { get; set; }
}
To achieve my goal I've tried following:
Report dayReport = null;
Report monthReport = null;
var currentDay; // some value of current day
var firstDay; // some value of first day of month
var report = session.QueryOver<User>
.Left.JoinAlias(u => u.Reports, () => dayReport, r => r.ReportDate == currentDay)
.Left.JoinAlias(u => u.Reports, () => monthReport, r => r.ReportDate == firstDat)
.SelectList(
// some logic to check whether user has reports
.TransformUsing(Transformers.AliasToBean<UserSummaryDto>())
.List<UserSummaryDto>()
And I've got error:
'duplicate association path:Reports'.
Is it possible to avoid this problem or it's a limitation of HNibernate?
To answer your question:
...Is it possible to avoid this problem or it's a limitation of HNibernate?
Have to say NO.
For more information see similar Q & A: Rename NHibernate criteria
We are not querying the DB, not using SQL (which does allow to do a lot). Here we work with "mapped" domain model, and that could bring some limitations - as the one discussed here...
If that could help, the workaround is to map such property twice and use the WHERE clause: 6.2. Mapping a Collection
where="" (optional) specify an arbitrary SQL WHERE condition to be used when retrieving or removing the collection (useful if the collection should contain only a subset of the available data)
Objective: Use the model to show total sales of an id and total sales difference for multiple periods.
Issue: Using MVC4 I have a model for a report that displays total sales. I know I can add a variable for each additional period, but I have many periods that I'd like to use to compare and show a difference. How can this be done avoiding needless repetition?
public class Report
{
public long ID { get; set }
public long TotalSales { get; set}
public long TotalSales2 { get; set}
public long TotalSalesDiff { get; set}
}
Thank you in advance for any suggestions and/or comments
If I understood your scenario you have two option either create a dictionary that can create properties or use Dynamic object class
For dictionary
public class DictionaryProperty
{
Dictionary<string, object> Yourproperties = new Dictionary<string, object>( );
public object this[ string name ]
{
get
{
if ( Yourproperties.ContainsKey( name ) )
{
return Yourproperties[ name ];
}
return null;
}
set
{
Yourproperties[ name ] = value;
}
}
}
}
And for the Dynamic object please refer following link
http://msdn.microsoft.com/en-us/library/system.dynamic.dynamicobject.aspx
cheers
I don’t have enough points to add a comments that’s the why I make this is an answer.
Working on creating my first Orchard Module and I am running into issues getting the form data saved back to the database. I have everything registered correctly as far as I can tell from looking at a lot of samples so I must be missing something minor.
I am able to get the Apartment form to show under the new menu, validation is working but when I fill the form completly and hit save I get:
Your Apartment has been created.
Checking the database the record is not in the table and checking the logs shows:
2013-12-19 09:15:23,416 [19]
NHibernate.Transaction.ITransactionFactory - DTC transaction prepre
phase failed NHibernate.Exceptions.GenericADOException: could not
execute batch command.[SQL: SQL not available] --->
System.Data.SqlClient.SqlException: Cannot insert the value NULL into
column 'FloorPlanName', table
'Orchard.dbo.CommunityWebsiteSolutions_ApartmentPartRecord';
column does not allow nulls. INSERT fails.
Running SQL Profiler shows an insert with all columns being set to NULL.
Migrations.cs
SchemaBuilder.CreateTable(typeof(ApartmentPartRecord).Name, table => table
.ContentPartRecord()
.Column<string>("FloorPlanName", c => c.WithLength(25).NotNull())
.Column<string>("FullAddress", c => c.WithLength(256).NotNull()))
.Column<string>("ShortDescription", c => c.WithLength(150).NotNull())
.Column("NumberOfBedrooms", DbType.Int32, c => c.NotNull())
.Column("NumberOfBathrooms", DbType.Int32, c => c.NotNull())
.Column("SquareFootage", DbType.Int32, c => c.NotNull())
.Column("WhenAvailable", DbType.DateTime)
.Column("RentAmount", DbType.Decimal)
);
ContentDefinitionManager.AlterPartDefinition(typeof (ApartmentPart).Name, part => part.Attachable());
ApartmentPart
public class ApartmentPartRecord : ContentPartRecord {
public virtual string FloorPlanName { get; set; }
public virtual string ShortDescription { get; set; }
public virtual string FullAddress { get; set; }
public virtual int? NumberOfBedrooms { get; set; }
public virtual int? NumberOfBathrooms { get; set; }
public virtual int? SquareFootage { get; set; }
public virtual DateTime? WhenAvailable { get; set; }
public virtual decimal? RentAmount { get; set; }
}
public class ApartmentPart : ContentPart<ApartmentPartRecord> {
[Required, StringLength(256)]
[Display(Name = "Address / Unit Number")]
public string FullAddress {
get { return Record.FullAddress; }
set { Record.FullAddress = value; }
}
[Required, StringLength(25)]
[Display(Name = "Floor Plan")]
public string FloorPlanName {
get { return Record.FloorPlanName; }
set { Record.FloorPlanName = value; }
}
[Required, StringLength(150)]
[Display(Name = "Sales Description")]
public string ShortDescription {
get { return Record.ShortDescription; }
set { Record.ShortDescription = value; }
}
[Required]
[Display(Name = "Bedroom Count")]
public int? NumberOfBedrooms {
get { return Record.NumberOfBedrooms; }
set { Record.NumberOfBedrooms = value; }
}
[Required]
[Display(Name = "Bathroom Count")]
public int? NumberOfBathrooms {
get { return Record.NumberOfBathrooms; }
set { Record.NumberOfBathrooms = value; }
}
[Required]
[Display(Name = "Square Footage")]
public int? SquareFootage {
get { return Record.SquareFootage; }
set { Record.SquareFootage = value; }
}
[Display(Name = "First Availability")]
public DateTime? WhenAvailable {
get { return Record.WhenAvailable; }
set { Record.WhenAvailable = value; }
}
[Display(Name = "Rent Amount")]
public decimal? RentAmount {
get { return Record.RentAmount; }
set { Record.RentAmount = value; }
}
}
Driver
public class ApartmentPartDriver : ContentPartDriver<ApartmentPart>
{
protected override string Prefix
{
get { return "Apartment"; }
}
//GET
protected override DriverResult Editor(ApartmentPart part, dynamic shapeHelper)
{
return ContentShape("Parts_Apartment_Edit",
() => shapeHelper.EditorTemplate(
TemplateName: "Parts/Apartment",
Model: part,
Prefix: Prefix));
}
//POST
protected override DriverResult Editor(ApartmentPart part, IUpdateModel updater, dynamic shapeHelper)
{
updater.TryUpdateModel(part, Prefix, null, null);
return Editor(part, shapeHelper);
}
}
Handler
public class ApartmentPartHandler : ContentHandler {
public ApartmentPartHandler(IRepository<ApartmentPartRecord> repository)
{
Filters.Add(StorageFilter.For(repository));
}
}
Your error message explains this pretty clearly:
System.Data.SqlClient.SqlException: Cannot insert the value NULL into column 'FloorPlanName', table 'Orchard.dbo.CommunityWebsiteSolutions_ApartmentPartRecord'; column does not allow nulls. INSERT fails.
Your problem occurs because:
You are using nullable types such as string and int? types in your Record class, which means you want to allow nulls.
Yet, you are specifying in your DB migration that you want to disallow nulls.
And when C# instantiates your Record class, it initializes the fields using the default value, which is null for nullable types.
You can do one of the following:
Make your DB columns nullable (remove NotNull)
Make your Record class use non-nullable types (for example, int instead of int?). Note that this is not an option for reference types such as string.
Give non-null default values to the fields of your Record class by giving the class a constructor. This is arguably bad practice since you will be calling virtual properties in a base class, but seems to be ok in NHibernate.
Give non-null default values to the fields of your Record class by giving your part an OnInitializing handler, which would be placed in your Handler class.
UPDATE
You commented that you are expecting the fields to be filled in by the TryUpdateModel in the Editor function of your driver class. This does eventually happen, but the actual sequence of events that occurs is this (you can see this in the CreatePOST method of Orchard.Core.Contents.Controllers.AdminController):
ContentManager.New() with the content type ID to create content item in memory. This step calls OnInitializing for the appropriate content parts for the content type, which are defined in handlers.
ContentManager.Create() with the content item in Draft Mode. This step actually tries to persist the item to the DB once.
ContentManager.UpdateEditor(). This is the call that actually calls Editor of the appropriate driver for the content type.
Check the ModelState and roll back the transaction if anything has failed.
Step 2 will fail if you have NULL values in columns marked NotNull, because the fields have default values at that point. For these columns, you have to fill them in before step 2 by using OnInitializing or by using a constructor on your Record part.
In other words, TryUpdateModel in your driver is actually applying changes directly to the entity that has already been Created and is now attached to the NHibernate session.
I'm trying to save a mapped entity using NHibernate but my insert to the database fails because the underlying table has a column that does not allow nulls and IS NOT mapped in my domain object. The reason it isn't mapped is because the column in question supports a legacy application and has no relevance to my application - so I'd like to not pollute my entity with the legacy property.
I know I could use a private field inside my class - but this still feels nasty to me. I've read that I can use an NHibernate interceptor and override the OnSave() method to add in the new column right before my entity is saved. This is proving difficult since I can't work out how to add an instance of Nhibernate.type.IType to the types parameter of my interceptor's OnSave.
My Entity roughly looks like this:
public class Client
{
public virtual int Id { get; set; }
public virtual int ParentId { get; set; }
public virtual string Name { get; set; }
public virtual string Phone { get; set; }
public virtual string Email { get; set; }
public virtual string Url { get; set; }
}
And my interceptor
public class ClientInterceptor : EmptyInterceptor
{
public override bool OnSave(object entity, object id, object[] state, string[] propertyNames, NHibernate.Type.IType[] types)
{
if (entity is Client)
{
/*
manually add the COM_HOLD column to the Client entity
*/
List<string> pn_list = propertyNames.ToList();
pn_list.Add("COM_HOLD");
propertyNames = pn_list.ToArray();
List<Object> _state = state.ToList();
_state.Add(false);
state = _state.ToArray();
//somehow add an IType to types param ??
}
return base.OnSave(entity, id, state, propertyNames, types);
}
}
Does anyone have any ideas on how to do this properly?
I can't say for sure since I've never actually done this (like Stefan, I also prefer to just add a private property), but can you just add a NHibernate.Type.BooleanType to the types array?
List<IType> typeList = types.ToList();
typeList.Add(new BooleanType());
types = typesList.ToArray();
EDIT
Yes, it looks like you are right; the types have an internal constructor. I did some digging and found TypeFactory:
Applications should use static
methods and constants on
NHibernate.NHibernateUtil if the
default IType is good enough. For example, the TypeFactory should only
be used when the String needs to have a length of 300 instead of 255. At this point
NHibernate.String does not get you thecorrect IType. Instead use TypeFactory.GetString(300) and keep a
local variable that holds a reference to the IType.
So it looks like what you want is NHibernateUtil:
Provides access to the full range of
NHibernate built-in types. IType
instances may be used to bind values
to query parameters. Also a factory
for new Blobs and Clobs.
typeList.Add(NHibernateUtil.Boolean);
Personally I wouldn't do it so complicated. I would add the private property and assign it a default value - finished. You could also consider a default value in the database, then you don't need to do anything else.
private virtual bool COM_HOLD
{
get { return false; }
set { /* make NH happy */ }
}
Before writing a interceptor for that I would consider to write a database trigger. Because with the Interceptor you are "polluting" your data access layer. It could make it unstable and you could have strange problems.