Fluent NHibernate ignore property inside the ClassMap, using FluentMappings - c#

I am using NHibernate 3.1 and Fluent NHibernate as ORM in my project. I need to have a property of a POCO ignored by Fluent NHibernate. At first, my post might look as exact duplicate of this question, but it is not.
My complications come first from the fact that the POCOs are defined in a different assembly than the mapping and I am using fluent mappings for my POCOs. I have additional requirement not to write ingore-property code where the session factory configuration takes place (this happens at a centralized place outside the modules), but as part of the module that defines the mappings. Ideally, I believe the right place would be the concrete ClassMap implementation, since it knows exactly how to describe a POCO to the ORM.
However, I am stuck on this mainly because this is my first impact with NHibernate and its fluent API. Up to now I am having very good impression of its capabilities and extensibility, and I hope there is a way to achieve my requirement in a way that the mapping related code is encapsulated in its corresponding module.
Here is my configuration, from a centralized place:
List<Assembly> assemblies = GetModules().Select(x => x.GetType().Assembly).ToList();
ISessionFactory nhibernateSessionFactory = Fluently
.Configure()
.Mappings(m => assemblies.ForEach(asm => m.FluentMappings.AddFromAssembly(asm)))
.Database(
MsSqlConfiguration.MsSql2005
.ShowSql()
.ConnectionString(DatabaseConfig.Instance.ConnectionString))
.ExposeConfiguration(c => new SchemaUpdate(c).Execute(true, true))
.BuildSessionFactory();
I use standard class mappings that inherit from ClassMap:
public class User
{
public virtual int ID { get; set; }
public virtual String Username { get; set; }
public virtual String Password { get; set; }
public virtual DateTime DateCreated { get; set; }
public virtual DateTime DateModified { get; set; }
// Must ignore
public string ComputedProperty { get { ... } }
}
public class UserMap : ClassMap<User>
{
public UserMap()
{
Table("User");
Id(x => x.ID).GeneratedBy.Identity();
Map(m => m.Username).Not.Nullable().Length(255).UniqueKey("User_Username_Unique_Key");
Map(m => m.Password).Not.Nullable().Length(255);
Map(m => m.DateCreated).Not.Nullable();
Map(m => m.DateModified).Not.Nullable();
}
}

I know this post is bit old, but I post anyway since I didn't find any up todate posts on the subject.
I guess the easiest way should be to add an attribute to each property we dont want to be persisted to a table. By add a extension that check if it has for eg. has a [NoEntity] attibute.
/// <summary>
/// Tells a single Property to not be persisted to table.
/// </summary>
public class NoEntity : Attribute { }
/// <summary>
/// Extension to ignore attributes
/// </summary>
public static class FluentIgnore
{
/// <summary>
/// Ignore a single property.
/// Property marked with this attributes will no be persisted to table.
/// </summary>
/// <param name="p">IPropertyIgnorer</param>
/// <param name="propertyType">The type to ignore.</param>
/// <returns>The property to ignore.</returns>
public static IPropertyIgnorer SkipProperty(this IPropertyIgnorer p, Type propertyType)
{
return p.IgnoreProperties(x => x.MemberInfo.GetCustomAttributes(propertyType, false).Length > 0);
}
}
And in the fluent config setup:
return Fluently.Configure()
.Database(DatabaseConfig)
.Mappings(m => m.AutoMappings.Add(AutoMap.Assembly(typeof(IDependency).Assembly)
.OverrideAll(p => {
p.SkipProperty(typeof(NoEntity));
}).Where(IsEntity)))
.ExposeConfiguration(ValidateSchema)
.ExposeConfiguration(BuildSchema)
.BuildConfiguration();

I think you are right that the ClassMap is the best place to ignore this property.
Example:
.Override<Shelf>(map =>
{
map.IgnoreProperty(x => x.YourProperty);
});
Documentation: https://github.com/jagregory/fluent-nhibernate/wiki/Auto-mapping#ignoring-properties
As far as getting the mappings from another assembly, it should be as easy as something like this (depending on your current configuration):
.Mappings(m =>
{
m.FluentMappings.AddFromAssemblyOf<ProvideClassFromYourOtherAssembly>();
});

Will not Justin. This is the thing with this extension. Just the property you want gets ignored.
public class Person : IEntity{
public virtual string Name{..}
public virtual string Lastname{..}
[NoProperty]
public virtual string FullName{ // Not created property
get { return Name + " " + Lastname; }
}
}
public class Group : IEntity{
public virtual string FullName{..} //Created property
}

Related

.NET Core 3 automapper not mapping different name variables? Worked in .NET framework

Below is the Automapping profile. An error is thrown on the destination Id being of the wrong input format.
The destination Id property is not even being mapped to.
public class AspGetUserLoginReturnModelToSRSDLUserLoginModel : Profile
{
public AspGetUserLoginReturnModelToSRSDLUserLoginModel()
{
CreateMap<asp_GetUserLoginReturnModel, SRSDLUserLoginModel>()
.ForMember(dest => dest.UserPassword, opt => opt.MapFrom(src => src.PasswordHash))
.ForMember(dest => dest.UserId, opt => opt.MapFrom(src => src.Id)
;
}
}
Now the destination class SRSDLUserLoginModel is derived from a base class that has a public accessor named Id which is type of nullable long.
The source Id property used above is of type string.
As the code shown above the source Id is mapped to the destination UserId property.
But Automapper is mapping the source Id to the destination's based classes Id property.
WHY IS THIS HAPPENING?
The code is straight forward and there is nothing new trying to be accomplished.
It Automapper like most everything other packages that I have work with?
In the full/classic .NET framework, the packages worked fine, but in .NET Core those packages just suck?
// source class
public partial class asp_GetUserLoginReturnModel
{
public string Id { get; set; }
}
// destination classes
public class SRSDLUserLoginModel: SRSDLModel, ISRSDLUserLoginModel
{
public string UserId { get; set; }
public string UserPassword { get; set; }
// [...]
}
public class SRSDLModel : ISRSDLModel
{
public long? Id { get; set; }
// [...]
}
AutoMapper will map properties with the same name by default so you don't have to write them all the time. In this case it tries to set the SRSDLModel.Id property with the value from the asp_GetUserLoginReturnModel.Id property. As you already noticed, that doesn't work because of the mismatch between long? and string.
To fix this, use the .Ignore() method in your mapping to specify that this specific property should no be set during mapping. The configuration might look like this:
.ForMember(dest => dest.Id, i => i.Ignore());

Map Nested object to a custom object in C# using a mapping library

I have the following object structure :
/// <summary>
/// nested message instance provided by a business service
/// </summary>
public class Message
{
public string Subject { get; set; }
public DateTime CreationDate { get; set; }
public List<Message> Messages { get; set; }
}
and I want to map that object to the following object structure :
/// <summary>
/// UI Object used to display a nested message structure
/// </summary>
public class MessageViewModel : ViewModelBase
{
public bool IsSelected { get; set; }
public string Subject { get; set; }
public DateTime CreationDate { get; set; }
public List<MessageViewModel> Messages { get; set; }
}
Is there any mapper that can get the job done easily ?
You can use AutoMapper for this. To map some types first you need to create some mappings:
Mapper.Initialize(cfg => cfg
.CreateMap<Message, MessageViewModel>());
This creates a mapping from Message to MessageViewModel. By default AutoMapper maps all the properties with the same name. Nested complex properties will also be mapped when there are mappings for their types specified. So in your example the above mapping is enough because nested property type is the same as its parent type and each property of a source type has its corresponding property with the same name in a target type. Collections are mapped to other collections implicitly.
To map a concrete object you can use Mapper.Map method:
var messageViewModel = Mapper.Map<MessageViewModel>(message);
This is only example for a static mapper but there is also a possibility to create mapper as an object. For more advanced topics you can read the docs: http://docs.automapper.org/en/stable/
I would strongly recommend using Automapper as its very simple and easy to use. In Automapper, fields having the same name get mapped by default and require minimal configuration. The mapping you want to achieve would be done as follows:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Message, MessageViewModel>();
});
In case of collections, Automapper can map the following, provided a configuration has been defined for their data types:
IEnumerable
ICollection
IList
List
Arrays
Since a mapping is already provided for the data types of the Lists in your case, further configuration will not be needed.
In case you want to map fields with different names or you want some basic level validation in the process, you can use the following syntax to define a configuration:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Message, MessageViewModel>()
.ForMember(destination => destination.SomeDestinationField, map => map.MapFrom(source => source.SomeSourceFieldWithDifferentName))
.ForMember(destination => destination.SomeDestinationField, map => map.MapFrom(source => source.SomeSourceField ?? SomeDefaultValue));
});
We can then use a MapperConfiguration object to initialize a Mapper and perform our mappings as follows:
SourceClass SourceObject = new SourceClass();
// Populate SourceObject with values
var mapper = config.CreateMapper();
DesitnationClass DestinationObject = mapper.Map<DesitnationClass>(SourceObject);
I would recommend going through these docs.

Setting MaxLength for all strings in Entity Framework code first

I'm developing a code-first database, using Entity Framework 6.
I know I can set [MaxLength(myLen)] on the property of a model.
What I wondered, is if this is possible to do in a filter or a custom attribute, so that all strings take on a default, of say 250, unless specified directly on the property.
Failing this, is there a way to change the default of nvarchar(max)?
Entity Framework introduced Custom Code First Conventions for this in 6.1
modelBuilder.Properties<string>()
.Configure(c => c.HasMaxLength(250));
Conventions operate in a last wins manner and the Fluent API and Data Annotations can be used to override a convention in specific cases
You can do this, which ensures all strings are the maximum length supported by the database provider:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Properties<string>().Configure(p => p.IsMaxLength());
}
Add this method (or modify the existing one) in your DbContext class.
In EF6 you can use a custom code first convention, but you will also need to have a way to specify nvarchar(max) data type to a string property. So, I came up with the following solution.
Also see:
https://msdn.microsoft.com/en-us/data/jj819164#order
/// <summary>
/// Set this attribute to string property to have nvarchar(max) type for db table column.
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public sealed class TextAttribute : Attribute
{
}
/// <summary>
/// Changes all string properties without System.ComponentModel.DataAnnotations.StringLength or
/// Text attributes to use string length 16 (i.e nvarchar(16) instead of nvarchar(max) by default).
/// Use TextAttribute to a property to have nvarchar(max) data type.
/// </summary>
public class StringLength16Convention : Convention
{
public StringLength16Convention()
{
Properties<string>()
.Where(p => !p.GetCustomAttributes(false).OfType<DatabaseGeneratedAttribute>().Any())
.Configure(p => p.HasMaxLength(16));
Properties()
.Where(p => p.GetCustomAttributes(false).OfType<TextAttribute>().Any())
.Configure(p => p.IsMaxLength());
}
}
public class CoreContext : DbContext, ICoreContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Change string length default behavior.
modelBuilder.Conventions.Add(new StringLength16Convention());
}
}
public class LogMessage
{
[Key]
public Guid Id { get; set; }
[StringLength(25)] // Explicit data length. Result data type is nvarchar(25)
public string Computer { get; set; }
//[StringLength(25)] // Implicit data length. Result data type is nvarchar(16)
public string AgencyName { get; set; }
[Text] // Explicit max data length. Result data type is nvarchar(max)
public string Message { get; set; }
}
In this code ModelBuilder class defines the shape of your entities, the relationships between them, and how they map to the database.
public class WebsiteDBContext : DbContext
{
public WebsiteDBContext(DbContextOptions<WebsiteDBContext> options) : base(options)
{
}
public DbSet<Global> Globals { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
// it should be placed here, otherwise it will rewrite the following settings!
base.OnModelCreating(builder);
builder.Entity<Global>();
builder.Entity<Global>(entity =>
{
entity.Property(global => global.MainTopic).HasMaxLength(150).IsRequired();
entity.Property(global => global.SubTopic).HasMaxLength(300).IsRequired(false);
entity.Property(global => global.Subject).IsRequired(false);
entity.Property(global => global.URL).HasMaxLength(150).IsRequired(false);
});
}
}

Nhibernate cache problem while using SQL Server triggers

I am using Nhibernate with Fluent, to persist a SQL Server 2008 express database in a business application.
I have a class named Receipt which contains a list with many objects named ReceiptItems.
The user can create a receipt, add Receiptitems to it, and edit it as long as its not marked Finished.
This part works well and saves to the db correctly.
Now for the problem:
I also have a trigger on the sql table Receipt, that fires if the inserted.Finished is true.
The trigger fetches new prices from the "supplier table", and updates the prices for all ReceiptItems,
in the ReceiptItems table.
When calling
session.SaveorUpdate(value)
and then
transaction.Commit()
the latter causes the exception:
StaleObjectStateException
Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) in ReceiptItems
Removing the trigger ofcourse fixes the problem, but i need it to update the prices. Is there any way for nhibernate to ignore the
error, and refresh its cache after the trigger fires?
straightforward class example definitions with Fluent mappings:
public class Receipt
{
public Receipt() { }
/// <summary>Identificator/// </summary>
public virtual int Id { get; private set; }
/// <summary> if finished true, cant edit data/// </summary>
public virtual Boolean Finished { get; set; }
/// <summary>Items of this Receipt/// </summary>
public virtual IList<ReceiptItems> Items{ get; set; }
}
/// <summary>Mapping for NHibernate Fluent/// </summary>
public class ProdajaMap : ClassMap<Prodaja>
{
public ReceiptMap()
{
Table("Receipt");
OptimisticLock.All();
DynamicUpdate();
Id(x => x.Id);
Map(x => x.Finished);
HasMany<ReceiptItems>(x => x.Items).AsBag().KeyColumn("Receipt_ID");
}
}
public class ReceiptItem
{
public ReceiptItem() { }
public virtual int Id { get; private set; }
/// <summary>Id of the Receipt/// </summary>
public virtual int Receipt_ID{ get; set; }
/// <summary>Supplier price/// </summary>
public virtual decimal Price{ get; set; }
/// <summary>Supplier discount/// </summary>
public virtual decimal Discount { get; set; }
}
/// <summary>Mapping for NHibernate Fluent/// </summary>
public class ReceiptItemMap : ClassMap<ReceiptItem>
{
public ReceiptItemMap()
{
Table("ReceiptItems");
OptimisticLock.All();
DynamicUpdate();
Id(x => x.Id);
Map(x => x.Receipt_ID).Column("Receipt_ID");
Map(x => x.Price);
Map(x => x.Discount );
}
}
Thank you very much !
UPDATE:
I've found a nhibernate property, which does exactly what i need, as the Price and Discount values have to be generated by the trigger:
5.6. Generated Properties Generated properties are properties which have
their values generated by the
database. Typically, Hibernate
applications needed to refresh objects
which contain any properties for which
the database was generating values.
Marking properties as generated,
however, lets the application delegate
this responsibility to Hibernate.
Essentially, whenever Hibernate issues
an SQL INSERT or UPDATE for an entity
which has defined generated
properties, it immediately issues a
select afterwards to retrieve the
generated values.
Properties marked as generated must
additionally be non-insertable and
non-updateable.
As im new at this.., does the last sentance mean, that i can't insert or update the values with nhibernate?
Use SET NOCOUNT ON in the trigger to suppress (xx rows affected) "dummy" result sets from trigger processing.
Example SO question: TooManyRowsAffectedException with encrypted triggers

Fluent NHibernate: How to tell it not to map a base class

I have been googling and stackoverflowing for the last two hours and couldn't find an answer for my question:
I'm using ASP.NET MVC and NHibernate and all I'm trying to do is to manually map my entities without mapping its base class. I'm using the following convention:
public class Car : EntityBase
{
public virtual User User { get; set; }
public virtual string PlateNumber { get; set; }
public virtual string Make { get; set; }
public virtual string Model { get; set; }
public virtual int Year { get; set; }
public virtual string Color { get; set; }
public virtual string Insurer { get; set; }
public virtual string PolicyHolder { get; set; }
}
Where EntityBase SHOULD NOT be mapped.
My NHibernate helper class looks like this:
namespace Models.Repository
{
public class NHibernateHelper
{
private static string connectionString;
private static ISessionFactory sessionFactory;
private static FluentConfiguration config;
/// <summary>
/// Gets a Session for NHibernate.
/// </summary>
/// <value>The session factory.</value>
private static ISessionFactory SessionFactory
{
get
{
if (sessionFactory == null)
{
// Get the connection string
connectionString = ConfigurationManager.ConnectionStrings["connectionString"].ConnectionString;
// Build the configuration
config = Fluently.Configure().Database(PostgreSQLConfiguration.PostgreSQL82.ConnectionString(connectionString));
// Add the mappings
config.Mappings(AddMappings);
// Build the session factory
sessionFactory = config.BuildSessionFactory();
}
return sessionFactory;
}
}
/// <summary>
/// Add the mappings.
/// </summary>
/// <param name="mapConfig">The map config.</param>
private static void AddMappings(MappingConfiguration mapConfig)
{
// Load the assembly where the entities live
Assembly assembly = Assembly.Load("myProject");
mapConfig.FluentMappings.AddFromAssembly(assembly);
// Ignore base types
var autoMap = AutoMap.Assembly(assembly).IgnoreBase<EntityBase>().IgnoreBase<EntityBaseValidation>();
mapConfig.AutoMappings.Add(autoMap);
// Merge the mappings
mapConfig.MergeMappings();
}
/// <summary>
/// Opens a session creating a DB connection using the SessionFactory.
/// </summary>
/// <returns></returns>
public static ISession OpenSession()
{
return SessionFactory.OpenSession();
}
/// <summary>
/// Closes the NHibernate session.
/// </summary>
public static void CloseSession()
{
SessionFactory.Close();
}
}
}
The error that I'm getting now, is:
System.ArgumentException: 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
This happens when I try to add the mappings. Is there any other way to manually map your entities and tell NHibernate not to map a base class?
IncludeBase<T>
AutoMap.AssemblyOf<Entity>()
.IgnoreBase<Entity>()
.Where(t => t.Namespace == "Entities");
Read more here http://wiki.fluentnhibernate.org/Auto_mapping :)
If you don't want to automap a class, I would recommend using IAutoMappingOverride<T>. I don't about your database, but it might look like:
public class CarOverride : IAutoMappingOverride<Car>
{
public void Override(AutoMapping<Car> mapping){
mapping.Id( x => x.Id, "CarId")
.UnsavedValue(0)
.GeneratedBy.Identity();
mapping.References(x => x.User, "UserId").Not.Nullable();
mapping.Map(x => x.PlateNumber, "PlateNumber");
// other properties
}
}
Assuming you keep these maps centrally located, you could then load them on your autoMap:
var autoMap = AutoMap.Assembly(assembly).IgnoreBase<EntityBase>().IgnoreBase<EntityBaseValidation>()
.UseOverridesFromAssemblyOf<CarOverride>();
I know it's an old question but I think that some things are missing here :
When you use IgnoreBase<T> you are telling that you don't want to map inheritance into your database but Fluent Nhibernate will still map your base class as an individual class while you don't tell it not to do that, so if you want to tell Fluent Nhibnernate not to map the class itself you should inherit DefaultAutoMapConfiguration class and override its bool ShouldMap(Type type) and return false if the type is any type that you don't want to map it at all.
When you use AutoMapping generally you don't need any other mapping classes or overrides unless you want to make a change in your mappings and it's not possible doing that using Conventions or you just want to override a small part of one class.(Although you can do the same using Conventions and Inspectors)
You can use IsBaseType convention for your automappings

Categories

Resources