I have a table A, which has following columns:
AId - TargetId - IsActive
Corresponding to this table, I have below class (with an additional calculated property) and mapper:
public class A
{
public virtual long AId { get; set; }
public virtual int TargetId { get; set; }
public virtual int IsActive { get; set; }
//calculated property, doesn't exist in the table
public virtual bool IsClientSide
{
get { return ((this.TargetId & TargetEnum.ClientSide) != 0); }
}
}
using NHibernate.Mapping.ByCode;
using NHibernate.Mapping.ByCode.Conformist;
using NHibernate.Type;
using ANamespace.A;
namespace mapping
{
public class AMap : ClassMapping<A>
{
public AMap()
{
this.Cache(x => { x.Usage(CacheUsage.NonstrictReadWrite); x.Region("LongTerm"); x.Include(CacheInclude.All); });
this.Lazy(false);
this.Mutable(true);
this.DynamicUpdate(true);
this.Id(x => x.AId, map => map.Generator(Generators.Native));
this.Property(x => x.TargetId, map => { map.NotNullable(true); map.Type<EnumType<TargetEnum>>(); });
this.Property(x => x.IsActive, map => map.NotNullable(true));
}
}
}
I am not mapping this IsClientSide property, because it is not in the table. But I want to use it on my query like below:
A aobject = null;
alist = session.QueryOver(() => aobject)
.Where(a => a.IsClientSide)
.And(tt => a.IsActive)
......
I have found Hendry Luk's "Linq-ing Calculated Properties" post but ILinqToHqlGenerator seemed over-complicated to use this tiny property.
How can I formulate IsClientSide property in my mapper class, instead?
We would need 1) Formula mapping and 2) bitwise operator
Property(x => x.IsClientSide, map =>
{
map.Formula("(Target_ID & 1 <> 0)");
});
and property should be assignable by NHibernate
public virtual bool IsClientSide { get; protected set; }
And now we can use IsClientSide in any query...
Related
Afternoon all. I'm trying to create a mapping for a flight segment database where at the bottom of the mapping tree a FlightSegment references an origin and destination table with a compositeID consisting of a three letter code and a Boolean determining whether the code is or isn't a city.
Below are the relevant simplified class structures:
public class GTIFlightSegment
{
public virtual int ID { get; protected set; }
public virtual GTIOriginAirport Origin { get; set; }
public virtual GTIDestinationAirport Destination { get; set; }
public virtual GTIFlightSegmentGroup Parent { get; set; }
}
public class GTIAirport
{
public virtual string Code { get; set; }
public virtual string Name { get; set; }
public virtual string City { get; set; }
public virtual string CountryCode { get; set; }
public virtual GTIGeoCode GeoCode {get; set; }
public virtual string Terminal { get; set; }
public virtual bool IsCity { get; set; }
public GTIAirport()
{
GeoCode = new GTIGeoCode();
IsCity = false;
}
public override bool Equals(object obj)
{
var other = obj as GTIAirport;
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return this.Code == other.Code && this.IsCity == other.IsCity;
}
public override int GetHashCode()
{
unchecked
{
int hash = GetType().GetHashCode();
hash = (hash * 31) ^ Code.GetHashCode();
hash = (hash * 31) ^ IsCity.GetHashCode();
return hash;
}
}
}
public class GTIOriginAirport : GTIAirport
{
public virtual GTIFlightSegment Parent { get; set; }
public GTIOriginAirport() : base()
{
}
}
public class GTIDestinationAirport : GTIAirport
{
public virtual GTIFlightSegment Parent { get; set; }
public GTIDestinationAirport() : base()
{
}
}
And below are the mappings I've created so far for the objects:
public class GTIFlightSegmentMap : ClassMap<GTIFlightSegment>
{
public GTIFlightSegmentMap()
{
Id(x => x.ID);
References(x => x.Destination).Columns(new string[] { "DestinationCODE", "DestinationIsCity" }).Cascade.All();
References(x => x.Origin).Columns(new string[] { "OriginCODE", "OriginIsCity"}).Cascade.All();
References(x => x.Parent).Not.Nullable();
}
}
public class GTIAirportMap : ClassMap<GTIAirport>
{
public GTIAirportMap()
{
Table("GTIAirport");
ReadOnly();
CompositeId()
.KeyProperty(x => x.Code, "CODE")
.KeyProperty(x => x.IsCity, "isCity");
Map(x => x.Name).Column("Airport");
Map(x => x.City);
Map(x => x.CountryCode);
Component(x => x.GeoCode, m =>
{
m.Map(x => x.Latitude);
m.Map(x => x.Longitude);
});
}
}
public class GTIOriginAirportMap : SubclassMap<GTIOriginAirport>
{
public GTIOriginAirportMap()
{
KeyColumn("CODE");
KeyColumn("isCity");
HasOne(x => x.Parent).PropertyRef(x => x.Origin);
}
}
public class GTIDestinationAirportMap : SubclassMap<GTIDestinationAirport>
{
public GTIDestinationAirportMap()
{
KeyColumn("CODE");
KeyColumn("isCity");
HasOne(x => x.Parent).PropertyRef(x => x.Origin);
}
}
What I'm trying to achieve is that when a FlightSegment is created in the system it will store the DestinationIsCity/DestinationCode and the OriginIsCity/OriginCode in the flight segments table but not try and update the GTIAirport reference table view. But when the objects are retrieved from the database with a query over, the rich information from the GTIAirport reference table will be fetched.
Or possibly have the Code and IsCity in the DepartureAirport and OriginAirport tables respectively with a ID reference back to the flight segment.
No matter what connotations I'm trying I'm hitting a wall of some kind. Basically I've got myself in a bit of a mess. I've flipped the relationships between the Airport and segment, swapping out to only references. Cascaded, not cascaded.
Common Errors being encountered whilst playing with the mappings:
1) The UPDATE statement conflicted with the FOREIGN KEY constraint
2) {"broken column mapping for: Destination.id of: GilesSabreConnection.Profiling.Model.BookingEngineModel.Model.GTIFlightSegment, type component[Code,IsCity] expects 2 columns, but 1 were mapped"}
3) {"Foreign key (FK582A9C81E6C3913B:GTIFlightSegment [Destination_id])) must have same number of columns as the referenced primary key (GTIDestinationAirport [CODE, isCity])"}
In case it's needed the fluent config is:
return Fluently.Configure().Database(MsSqlConfiguration.MsSql2012.ConnectionString(#"Data Source=Dev2;Initial Catalog=Sandbox;Integrated Security=True")).Mappings(m => m.FluentMappings.AddFromAssemblyOf<Profile>()).ExposeConfiguration(cfg => new SchemaUpdate(cfg).Execute(true, true)).BuildSessionFactory();
Any help to point me in the right direction from the database and fluent gurus would be greatly appreciated.
Many thanks in advance.
Realised that by setting the GTIAirport Map as readonly I was inherently making the Destination and Origin maps as readonly so the mapping could never work no matter how I was configuring the relationships.
So I've split the static reference database of detailed airport info accessed via the View named GTIAiport into two, so the Boolean in the composite key is no longer required. I now have the flight segment mapping simply logging the Airport origin/destination codes within the flight segment table using the Component class in fluent. In the Airport class itself now have a function to independently fetch the rich data from the static reference database table on request using the stored Airport code.
Couldn't figure out a way to do this with fluent. Interested to know if there is a way.
public GTIFlightSegmentMap()
{
Id(x => x.ID);
Component(x => x.Destination, m =>
{
m.Map(y => y.Code).Column("DestinationCode");
});
Component(x => x.Origin, m =>
{
m.Map(y => y.Code).Column("OriginCode");
});
;
References(x => x.Parent).Not.Nullable();
}
}
I have a problem with union and automapper projections.
I have two entities:
public class Entity
{
public DateTime ActionDate { get; set; }
public int SomeProp { get; set; }
}
public class ExtendedEntity
{
public DateTime ActionDate { get; set; }
public int SomeProp { get; set; }
public int SomeOtherProp { get; set; }
}
and projection:
public class EntityProjection
{
public DateTime ActionDate { get; set; }
public int SomeProp { get; set; }
public int SomeOtherProp { get; set; }
public string Source { get; set; }
}
i map entities to one projection, Entity does not have SomeOtherProp so i set 0 to it:
public class EntityProfile : Profile
{
protected override void Configure()
{
CreateMap<ExtendedEntity, EntityProjection>()
.ForMember(dest => dest.Source, opt => opt.MapFrom(src => "ext entity"));
CreateMap<Entity, EntityProjection>()
.ForMember(dest => dest.SomeOtherProp, opt => opt.MapFrom(src => 0))
.ForMember(dest => dest.Source, opt => opt.MapFrom(src => "entity"));
}
}
when i try to use next code i get error:
var entities = context.Set<Entity>()
.Project().To<EntityProjection>();
var extEntities = context.Set<ExtendedEntity>()
.Project().To<EntityProjection>();
var result = entities.Union(extEntities).OrderBy(p => p.ActionDate).ToList();
Error text: The type 'UserQuery+EntityProjection' appears in two structurally incompatible initializations within a single LINQ to Entities query. A type can be...
That means that properties in projection must be initialized in same order, how i can set projection properties initialization order by automapper?
Very late answer, and the short version seems to be "You can't".
I had exactly the same question (Can I force Automapper to initialise properties in a certain order?) and ended up mapping everything within a LINQ select statement.
For ease, I made it a static method within my DTO (cut-down code):
public static IQueryable<MyDto> QueryableFromTaskType1(
IQueryable<TaskType1> query)
{
return query.Select(src => new MyDto()
{
TaskId = src.Id,
AssetTypeName = src.Asset.AssetType.Name,
AssetId = src.Asset.Id,
AssetCode = src.Asset.Code,
AssetName = src.Asset.Name,
});
}
public static IQueryable<MyDto> QueryableFromTaskType2(
IQueryable<TaskType2> query)
{
return query.Select(src => new MyDto()
{
TaskId = src.Id,
AssetTypeName = src.AssetTypeName,
AssetId = src.AssetId,
AssetCode = src.AssetCode,
AssetName = src.AssetName,
});
}
then you can get your objects, as an IQueryable, simply pass them through the appropriate static method (which appends a select into the DTO - or projects as it's otherwise known) and then Union or Concat the resulting IQueryables.
The only downside is that Automapper will normally deal with recursive automapping, although I'm pretty certain that wouldn't map to SQL well anyway, so you probably don't lose much.
I have read a lot of the questions about that same error but none since to match my exact problem. I'm trying to access the property of an object, itself part of a root object, using Fluent NHibernate. Some answers say I need to use projections, others that I need to use join, and I think it should work through lazy loading.
Here are my two classes along with the Fluent mappings:
Artist class
public class Artist
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Album> Albums { get; set; }
public virtual string MusicBrainzId { get; set; }
public virtual string TheAudioDbId { get; set; }
public Artist() { }
}
public class ArtistMap : ClassMap<Artist>
{
public ArtistMap()
{
LazyLoad();
Id(a => a.Id);
Map(a => a.Name).Index("Name");
HasMany(a => a.Albums)
.Cascade.All();
Map(a => a.MusicBrainzId);
Map(a => a.TheAudioDbId);
}
}
Album class
public class Album
{
public virtual int Id { get; set; }
public virtual Artist Artist { get; set; }
public virtual string Name { get; set; }
public virtual IList<Track> Tracks { get; set; }
public virtual DateTime ReleaseDate { get; set; }
public virtual string TheAudioDbId { get; set; }
public virtual string MusicBrainzId { get; set; }
public Album() { }
}
public class AlbumMap : ClassMap<Album>
{
public AlbumMap()
{
LazyLoad();
Id(a => a.Id);
References(a => a.Artist)
.Cascade.All();
Map(a => a.Name).Index("Name");
HasMany(a => a.Tracks)
.Cascade.All();
Map(a => a.ReleaseDate);
Map(a => a.TheAudioDbId);
Map(a => a.MusicBrainzId);
}
}
And the error happens when this code is interpreted:
var riAlbum = session.QueryOver<Album>()
.Where(x => x.Name == albumName && x.Artist.Name == artist)
.List().FirstOrDefault();
The error happens when Fluent NHibernate tries to resolve the x.Artist.Name value:
{"could not resolve property: Artist.Name of: Album"}
What would be the correct way of doing this?
You have to think of your QueryOver query as (nearly) directly translating into SQL. With this in mind, imagine this SQL query:
select
Album.*
from
Album
where
Album.Name = 'SomeAlbumName' and
Album.Artist.Name = 'SomeArtistName'
This won't work because you can't access a related table's properties like that in a SQL statement. You need to create a join from Album to Artist and then use a Where clause:
var riAlbum =
session.QueryOver<Album>()
.Where(al => al.Name == albumName)
.JoinQueryOver(al => al.Artist)
.Where(ar => ar.Name == artistName)
.List()
.FirstOrDefault();
Also, since you're using FirstOrDefault, you may want to consider moving that logic to the database end. Currently, you're pulling back every record matching your criteria and then taking the first one. You could use .Take to limit the query to 1 result:
var riAlbum =
session.QueryOver<Album>()
.Where(al => al.Name == albumName)
.JoinQueryOver(al => al.Artist)
.Where(ar => ar.Name == artistName)
.Take(1)
.SingleOrDefault<Album>();
Another explanation is that you are missing your mapping of this property or field in a NHibernateClassMapping definition. I came here about why I was getting this error based on the following scenario.
var query = scheduleRepository.CurrentSession().Query<Schedule>()
.Where(x => x.ScheduleInfo.StartDate.Date < dateOfRun.Date);
This was giving me a Could Not Resolve Property error for StartDate. This was a head scratcher, since I use this syntax all the time.
My mapping file was the following:
public class ScheduleInfoMapping : NHibernateClassMapping<ScheduleInfo>
{
public ScheduleInfoMapping()
{
DiscriminateSubClassesOnColumn("Type");
Map(x => x.Detail).MapAsLongText();
}
}
which was missing the StartDate. Changed to:
public class ScheduleInfoMapping : NHibernateClassMapping<ScheduleInfo>
{
public ScheduleInfoMapping()
{
DiscriminateSubClassesOnColumn("Type");
Map(x => x.Detail).MapAsLongText();
Map(x => x.StartDate);
}
}
Which resolved the error.
I have the following database structure, which I cannot change:
CREATE TABLE Users (
ID INT NOT NULL IDENTITY(1,1),
PRIMARY KEY(ID)
)
CREATE TABLE UserAvatars (
UserID INT NOT NULL,
Width INT NOT NULL,
Height INT NOT NULL,
PRIMARY KEY(UserID),
FOREIGN KEY(UserID) REFERENCES Users(ID),
)
Basically, a User can have Zero-or-one avatars (I removed columns to simplify the example).
class User
{
public int ID { get; protected set; }
public UserAvatar Avatar { get; set; }
}
class UserAvatar
{
public User User { get; set; }
public int Width { get; set; }
public int Height { get; set; }
}
I am using the following mapping:
class UserMapping : ClassMapping<User>
{
public UserMapping()
{
Id(x => x.ID, m => {
m.Generator(Generators.Identity);
});
OneToOne(x => x.Avatar);
}
}
class UserAvatarMapping : ClassMapping<UserAvatar>
{
public UserAvatar()
{
Id(x => x.User, m =>
{
m.Generator(Generators.Foreign<User>(u => u.ID));
});
Property(x => x.Width);
Property(x => x.Height);
}
}
However, when trying run my app, I get an NHibernate.MappingException:
NHibernate.MappingException : Could not compile the mapping document:
mapping_by_code----> NHibernate.MappingException : Could not determine
type for: MyApp.Models.UserAvatar, MyApp, for columns:
NHibernate.Mapping.Column(User)
I can't make any sense of this cryptic error.
How do I accomplish the mapping where a User could have zero or one Avatars?
Taking from Radim Köhler's guidence, the final classes / mapping that solved the problem:
class User
{
public int ID { get; protected set; }
public UserAvatar Avatar { get; set; }
}
class UserAvatar
{
public int UserID { get; protected set; }
public User User { get; set; }
public int Width { get; set; }
public int Height { get; set; }
}
With the following mapping:
class UserMapping : ClassMapping<User>
{
public UserMapping()
{
Id(x => x.ID, m => {
m.Generator(Generators.Identity);
});
OneToOne(x => x.Avatar, m =>
{
m.Cascade(Cascade.All);
m.Constrained(false);
});
}
}
class UserAvatarMapping : ClassMapping<UserAvatar>
{
public UserAvatar()
{
Id(x => x.UserID, m =>
{
m.Generator(Generators.Foreign<UserAvatar>(u => u.User));
});
OneToOne(x => x.User, m =>
{
m.Constrained(true);
});
Property(x => x.Width);
Property(x => x.Height);
}
}
The ID is important almost for every entity mapped by NHibernate (ORM). So, we should extend the Avatar class:
class UserAvatar
{
// every(stuff) should have its ID
public int ID { get; protected set; }
public User User { get; set; }
...
The ID value will be managed by one-to-one relationship. So, now let's adjust the mapping.
public UserAvatar()
{
Id(x => x.ID);
HasOne(x => x.User)
.Constrained()
.ForeignKey();
...
public UserMapping()
{
Id(x => x.ID);
HasOne(s => s.Avatar).Cascade.All();
Related and really interesting reading:
How to do a fluent nhibernate one to one mapping?
Fluent NHibernate & HasOne(): how to implement a one-to-one relationship
I have entities:
[Table("Representatives")]
public class Representative
{
[Column("RepID"), Key]
public int Id { get; set; }
[Column("MemID")]
public int MemberId { get; set; }
public virtual Member Member { get; set; }
}
[Table("Members")]
public class Member
{
[Column("MemID"), Key]
public int Id { get; set; }
[ForeignKey("MemberId")]
public virtual ICollection<Representative> Representatives { get; set; }
}
where Representatives relate to Member as many to one. I want to retrieve some representatives with their parent members and project it all to some view model. I've configured automapper like this:
Mapper.CreateMap<Representative, RepresentativeViewModel>()
.ForMember(m => m.RepId, a => a.MapFrom(x => x.Id))
.ForMember(m => m.MemberModel, a => a.MapFrom(x => x.Member));
Mapper.CreateMap<Member, MemberViewModel>()
.ForMember(m => m.MemId, a => a.MapFrom(x => x.Id))
.ForMember(m => m.OrganizationName, a => a.MapFrom(x => x.Name));
If I run this code:
var prj = ctx.Representatives.Where(r => r.Id == 1).Project().To<RepresentativeViewModel>();
then I get expression tree that contains func invocation (debugged):
...
.Lambda #Lambda2<System.Func`2[ConsoleApplication1.Representative,ConsoleApplication1.RepresentativeViewModel]>(ConsoleApplication1.Representative $var1)
{
.New ConsoleApplication1.RepresentativeViewModel(){
Name = $var1.Name,
RepId = $var1.Id,
MemberModel = .Invoke (.Lambda #Lambda3<System.Func`2[ConsoleApplication1.Member,ConsoleApplication1.MemberViewModel]>)($var1.Member)
}
}
.Lambda #Lambda3<System.Func`2[ConsoleApplication1.Member,ConsoleApplication1.MemberViewModel]>(ConsoleApplication1.Member $var2)
{
.New ConsoleApplication1.MemberViewModel(){
MemId = $var2.Id,
OrganizationName = $var2.Name
}
}
...
so for some reason builder invokes Func instead of calling Expression. Do you have ideas why it's so? How can I make my mapper build correct expression and SQL query?