Optimistic Concurrency in Entity Framework between Oracle and T-SQL - c#

I am building EF Code first POCOs that'll be used between an Oracle back-end and a MS SQL Server backend. I'm having a problem finding the right way to tackle a Timestamp property that'll work on either database back-end.
MS SQL Server would have me use a common property like this:
[Timestamp]
public byte[] Timestamp {get;set;}
And then in the fluent mapping it would look like this
map.Property(p => p.Timestamp).IsRowVersion();
However Oracle would have me change my common property type to this:
public int Timestamp {get;set;}
And then in the fluent mapping it would look like this
map.Property(p => p.Timestamp).HasColumnName("ORA_ROWSCN").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed).IsConcurrencyToken();
So my first guess was maybe I could change the data type to long, since timestamp is eight bytes, but SqlServer didn't like the mapping.
My next guess is to give up on Timestamp and Ora_RowScn and make up my own Optimistic Concurrency property. Any suggestions or know if a way to have a happy model that works between both Sql and Oracle? Thanks.

This is how I resolved the issue. I got rid of the [Timestamp] attribute. Then I also created two assemblies for my repositories, one for Oracle and the other for MSSQL. Then my base model looks like this.
[DataContract]
public abstract class DomainBase
{
/// <summary>
/// Gets or sets the id.
/// </summary>
[DataMember]
[Key]
public long Id { get; set; }
private byte[] _timestamp=new Guid().ToByteArray();
/// <summary>
/// Gets or sets the timestamp.
/// </summary>
[DataMember]
public byte[] Timestamp { get { return _timestamp; }
set { _timestamp = value;
if (_timestamp != null && _signature != Convert.ToBase64String(_timestamp))
_signature = Convert.ToBase64String(_timestamp);
}
}
private string _signature = Convert.ToBase64String(new Guid().ToByteArray());
/// <summary>
/// Gets the signature.
/// </summary>
[DataMember]
public string Signature
{
get { return _signature ?? (Timestamp != null ? _signature = Convert.ToBase64String(Timestamp) : null); }
protected set { _signature = value;
if ((_timestamp == null && !String.IsNullOrWhiteSpace(_signature)) ||
(_timestamp != null && !String.IsNullOrWhiteSpace(_signature) && Convert.ToBase64String(_timestamp) != _signature))
_timestamp = Convert.FromBase64String(value);
}
}
/// <summary>
/// Gets a value indicating whether has signature.
/// </summary>
public bool HasSignature
{
get { return Timestamp != null; }
}
}
Then this is how I handle the mappings in each fluent setup.
For MSSQL Server.
Property(p => p.Timestamp).HasColumnType("timestamp").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed).IsRowVersion();
Ignore(p => p.Signature);
For Oracle
Ignore(p => p.Timestamp);
Property(p => p.Signature).HasColumnName("Timestamp").IsConcurrencyToken();

Related

Entity Framework get user from contex in saveChanges

i have two projects in my solution, UI as mvc and class project for entitiy model code first. I have severall entities in my model but now I need to extend them by new audit fields where I need to save who changed entity.
I added new interface
public interface IAuditable
{
/// <summary>Gets or sets the name.</summary>
/// <value>The name.</value>
DateTime CreatedDate { get; set; }
/// <summary>Gets or sets the name.</summary>
/// <value>The name.</value>
string CreatedBy { get; set; }
/// <summary>Gets or sets the name.</summary>
/// <value>The name.</value>
DateTime UpdatedDate { get; set; }
/// <summary>Gets or sets the name.</summary>
/// <value>The name.</value>
string UpdatedBy { get; set; }
}
and try to extend SaveChanges in this way
foreach (var auditableEntity in ChangeTracker.Entries<IAuditable>())
{
if (auditableEntity.State == EntityState.Added ||
auditableEntity.State == EntityState.Modified)
{
// implementation may change based on the useage scenario, this
// sample is for forma authentication.
string currentUser = ;
// modify updated date and updated by column for
// adds of updates.
auditableEntity.Entity.UpdatedDate = DateTime.Now;
auditableEntity.Entity.UpdatedBy =
// pupulate created date and created by columns for
// newly added record.
if (auditableEntity.State == EntityState.Added)
{
auditableEntity.Entity.CreatedDate = DateTime.Now;
auditableEntity.Entity.CreatedBy = currentUser;
}
else
{
auditableEntity.Property(p => p.CreatedDate).IsModified = false;
auditableEntity.Property(p => p.CreatedBy).IsModified = false;
}
}
but how do I get the userName here ? I can't use any httpContex getUser becuase this is class project. Any ideas?
this is my contex
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>, IDbContext
so I thought to extend ApplicationUser by another field like LogedUserName, and fill it when user is loging, but how do I get this field in my SaveChanges method ?
If you are sure that this class library will be always used in ASP.NET pipeline you actually can access HttpContext.
You need a reference to System.Web in your class library and then:
using System.Web;
[...]
public void SaveChanges()
{
var username = HttpContext.Current.User.Identity.Name;
}
In this case HttpContext is a static class, not a property.
Ofcourse this will fail badly if this class is ever used outside ASP.NET pipeline (for example in WPF application, console app etc). Also it doesn't seem clean to do it this way. But it's probably the fastest way which requires minimal existing code change.
Another way would be to pass either username or whole identity to either class responsible for saving changes or directly to SaveChanges method.
One implementation could look like this:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>, IDbContext
{
private IPrincipal _currentUser;
public ApplicationDbContext(IPrincipal currentUser)
{
_currentUser = currentUser;
}
}
and then in Controller (if you use context directly in MVC controllers):
using(var db = new ApplicationDbContext(User))
{
[...]
}
where User is controller's property holding current user.

Where returns wrong record

UPDATE
I've just had a thought that may well be relevant to this issue. I'm using a code first approach with this project. Originally my ZoneMapping class was defined as you can see below, however the database only had a single PrimaryKey field in the database. I believe because EF hadn't interpreted the data quite correctly.
At this point I made a modification to my migration SQL script output to add the additional primary key that I applied to the database. I've just updated the migration instead from:
CreateTable(
"dbo.NetC_EF_ZoneMapping",
c => new
{
PostcodeKey = c.String(nullable: false, maxLength: 128),
Zone_ID = c.Int(),
})
.PrimaryKey(t => t.PostcodeKey)
.ForeignKey("dbo.NetC_EF_Zone", t => t.Zone_ID)
.Index(t => t.Zone_ID);
I've just tried adding an additional PrimaryKey manually in the migration after the PostcodeKey one has been defined.
.PrimaryKey(t => t.Zone_ID)
Unfortunately I'm still getting my error - I'm assuming this migration isn't used to build the EF 'model' in code, but I'm wondering if it thinks that there can only be a single entry with any given PostcodeKey which may explain the situation?
I'm posting a new question based on Linq Except not functioning as expected - duplicate items because I feel that enough has been discovered that the question is invalid, and Except is not the problem at all.
The problem I have is that I have a Linq Where clause that seems to be returning the wrong data. The data in my database looks like:
The class that represents this data has a compound key:
/// <summary>
/// Represents a mapping between a postcode and a zone
/// </summary>
[Table("NetC_EF_ZoneMapping")]
public class ZoneMapping
{
/// <summary>
/// Gets or sets the postcode identifier
/// </summary>
[Key]
public String PostcodeKey { get; set; }
/// <summary>
/// Gets or sets the Zone identifier
/// </summary>
[Key]
public Zone Zone { get; set; }
}
So I'm executing the following code, which results in different IDs:
var result = this.context.ZoneMappings.Include("Zone").Where(z => z.Zone.ID == 257 && z.PostcodeKey == "2214");
var result2 = new FreightContext().ZoneMappings.Include("Zone").Where(z => z.Zone.ID == 257 && z.PostcodeKey == "2214");
if (result.First().Zone.ID != result2.First().Zone.ID)
throw new InvalidOperationException();
The SQL (or ToString() for these two items is identical). So the only difference is that one is a new context, while the other has been passed in and used for some other stuff. The code that creates the context returning the wrong result is:
// Copy the contents of the posted file to a memory stream
using (StreamReader sr = new StreamReader(fileUpload.PostedFile.InputStream))
using (FreightContext context = new FreightContext())
{
// Attempt to run the import
ZoneMappingCSVImporter importer = new ZoneMappingCSVImporter(sr, context, System.Globalization.CultureInfo.CurrentUICulture);
var items = importer.GetItems().ToList();
importer.SaveItems(items);
this.successBox.Value = "Import completed and added " + items.Count() + " zones mappings.";
}
This then registers a ClassMap in the library that I'm using where:
public ZoneMappingCSVImporter(TextReader textReader, FreightContext context, CultureInfo culture)
: base(textReader, context, culture)
{
this.reader.Configuration.RegisterClassMap(new ZoneMappingMap(this.context));
}
I do a lookup using the context:
/// <summary>
/// Initializes a new instance of the <see cref="ZoneMap"/> class.
/// </summary>
public ZoneMappingMap(FreightContext context)
{
if (context == null)
throw new ArgumentNullException("context");
Map(m => m.PostcodeKey);
Map(m => m.Zone).ConvertUsing(row =>
{
// Grab the name of the zone then go find this in the database
String name = row.GetField<String>("Zone");
return context.Zones.Where(z => String.Compare(z.Name, name, true) == 0).FirstOrDefault();
});
}
I can't see anything strange going on here, I've verified the SQL generated by Entity Framework, verified the database is the same one - I can't understand why the wrong record would be returned. Can anyone shed any light on this?
The explanation for this problem is most likely the following:
Your way to define composite primary key in your entity is incorrect. With your current model EF only considers ZoneMapping.PostcodeKey as the primary key. More on this and how to fix it below.
You get a wrong result in the case when the corresponding context already contains the entity with PostcodeKey == "2214" and Zone_ID == 256 before you run your query. (I guess you never had a wrong result2.) When EF loads entities from the database it always looks after the query if an entity with the same key already exists in the context. If yes, the queried entity is thrown away and instead the attached entity is added to the result collection. In your case you are querying for PostcodeKey == "2214" and Zone_ID == 257. After the query EF picks the values of the primary key from the result row. But because EF "thinks" the primary key is only PostcodeKey == "2214" it searches for an attached entity with that key value and finds the entity with PostcodeKey == "2214", but Zone_ID == 256 and returns that as the result to you. You never get this problem with result2 because the corresponding context is new and empty, so EF will return the result that just has been loaded, not any older attached entity.
If you want to define a composite key you must have scalar properties for both key parts. You cannot use a navigation property like ZoneMapping.Zone as a key part. It means that you must have a property for your column Zone_ID in the model class (in addition to the navigation property Zone):
public class ZoneMapping
{
public String PostcodeKey { get; set; }
public int Zone_ID { get; set; }
public Zone Zone { get; set; }
}
Then in order to define a composite key with data annotations you must use the Column(Order = n) attribute as already shown in #Jonas Jämtberg's answer. You should also apply the ForeignKey attribute to Zone_ID because the underscore makes the property name "unconventional" so that EF won't detect it as FK by convention:
public class ZoneMapping
{
[Key, Column(Order = 0)]
public String PostcodeKey { get; set; }
[Key, ForeignKey("Zone"), Column(Order = 1)]
public int Zone_ID { get; set; }
public Zone Zone { get; set; }
}
The migration class that is generated by EF with this mapping should have a primary key definition that looks like this:
.PrimaryKey(t => new { t.PostcodeKey, t.Zone_ID })
...not like .PrimaryKey(t => t.PostcodeKey).PrimaryKey(t => t.Zone_ID) that you tried unsuccessfully to fix the problem.

Fluent NHibernate ignore property inside the ClassMap, using FluentMappings

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
}

EF Code First - Globally set varchar mapping over nvarchar

I have what should be an easy question but I have been unable to find the answer myself.
I am using EF4 CTP-5 Code First Model with hand generated POCOs. It is processing string comparisons in generated SQL as
WHERE N'Value' = Object.Property
I am aware that I can override this functionality using:
[Column(TypeName = "varchar")]
public string Property {get;set;}
Which fixes the issue for that single occurrence and correctly generates the SQL as:
WHERE 'Value' = Object.Property
However, I am dealing with a VERY large domain model and going through each string field and setting TypeName = "varchar" is going to be very very tedious. I would like to specify that EF should see string as varchar across the board as that is the standard in this database and nvarchar is the exception case.
Reasoning for wanting to correct this is query execution efficiency. Comparison between varchar and nvarchar is very inefficient in SQL Server 2k5, where varchar to varchar comparisons execute almost immediately.
Before EF 4.1, you could use conventions and add the following convention to your ModelBuilder:
using System;
using System.Data.Entity.ModelConfiguration.Configuration.Properties.Primitive;
using System.Data.Entity.ModelConfiguration.Conventions.Configuration;
using System.Reflection;
public class MakeAllStringsNonUnicode :
IConfigurationConvention<PropertyInfo, StringPropertyConfiguration>
{
public void Apply(PropertyInfo propertyInfo,
Func<StringPropertyConfiguration> configuration)
{
configuration().IsUnicode = false;
}
}
(Taken from http://blogs.msdn.com/b/adonet/archive/2011/01/10/ef-feature-ctp5-pluggable-conventions.aspx)
UPDATE: Pluggable conventions were dropped for the 4.1 release. Check my blog for an alternative approach)
For anyone looking to do this in EF Core (v3 and above), a quick way to achieve this is through the ModelBuilder.Model property; it provides easy access to all entities and properties within the model.
A "bare-bones" implementation follows:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Apply configurations via regular modelBuilder code-first calls
// ...
// ...
// Override the configurations to force Unicode to false
var entities = modelBuilder.Model.GetEntityTypes();
foreach (var entity in entities)
{
foreach (var property in entity.GetProperties())
{
property.SetIsUnicode(false);
}
}
}
EF Core happily ignores the SetIsUnicode call on non-string properties, so you don't even have to check the property type (but you easily could if it makes you feel better :)
For those who prefer to be a bit more explicit, tacking on a where clause to the GetProperties() call will do the trick:
...
var stringProperties = entity.GetProperties()
.Where(e=> e.ClrType == typeof(string));
foreach (var property in stringProperties)
{
property.SetIsUnicode(false);
}
...
UPDATE - Entity Framework Core 6
You are now able to do this type of global mapping out-of-the-box using
EF Core 6's pre-convention-model-configuration
A sample of how this would be achieved with this new feature is shown below:
configurationBuilder
.DefaultTypeMapping<string>()
.IsUnicode(false);
I extended Marc Cals' answer (and Diego's blog post) to globally set all strings on all entities as non-unicode as per the question, rather than having to call it manually per-class. See below.
/// <summary>
/// Change the "default" of all string properties for a given entity to varchar instead of nvarchar.
/// </summary>
/// <param name="modelBuilder"></param>
/// <param name="entityType"></param>
protected void SetAllStringPropertiesAsNonUnicode(
DbModelBuilder modelBuilder,
Type entityType)
{
var stringProperties = entityType.GetProperties().Where(
c => c.PropertyType == typeof(string)
&& c.PropertyType.IsPublic
&& c.CanWrite
&& !Attribute.IsDefined(c, typeof(NotMappedAttribute)));
foreach (PropertyInfo propertyInfo in stringProperties)
{
dynamic propertyExpression = GetPropertyExpression(propertyInfo);
MethodInfo entityMethod = typeof(DbModelBuilder).GetMethod("Entity");
MethodInfo genericEntityMethod = entityMethod.MakeGenericMethod(entityType);
object entityTypeConfiguration = genericEntityMethod.Invoke(modelBuilder, null);
MethodInfo propertyMethod = entityTypeConfiguration.GetType().GetMethod(
"Property", new Type[] { propertyExpression.GetType() });
StringPropertyConfiguration property = (StringPropertyConfiguration)propertyMethod.Invoke(
entityTypeConfiguration, new object[] { propertyExpression });
property.IsUnicode(false);
}
}
private static LambdaExpression GetPropertyExpression(PropertyInfo propertyInfo)
{
var parameter = Expression.Parameter(propertyInfo.ReflectedType);
return Expression.Lambda(Expression.Property(parameter, propertyInfo), parameter);
}
/// <summary>
/// Return an enumerable of all DbSet entity types in "this" context.
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
private IEnumerable<Type> GetEntityTypes()
{
return this
.GetType().GetProperties()
.Where(a => a.CanWrite && a.PropertyType.IsGenericType && a.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>))
.Select(a => a.PropertyType.GetGenericArguments().Single());
}
Finally, call it from your OnModelCreating(DbModelBuilder modelBuilder):
foreach (var entityType in GetEntityTypes())
SetAllStringPropertiesAsNonUnicode(modelBuilder, entityType);
Here is a project from Sergey Barskiy that extends EF to allow custom conventions, which as a result, you can make custom attributes instead of the fluent API.
Here is a code snippet from here that demonstrates the utility in action.
What you don't see here is the decimal precision attribute and others. So per your question, once you set Unicode to false, it should be varchar as opposed to nvarchar.
public class Product
{
public int ProductId { get; set; }
[Indexed("Main", 0)]
public string ProductNumber { get; set; }
[Indexed("Main", 1)]
[Indexed("Second", direction: IndexDirection.Ascending)]
[Indexed("Third", direction: IndexDirection.Ascending)]
public string ProductName { get; set; }
[String(4, 12, false)] //minLength, maxLength, isUnicode
public string Instructions { get; set; }
[Indexed("Third", 1, direction: IndexDirection.Descending)]
public bool IsActive { get; set; }
[Default("0")]
public decimal? Price { get; set; }
[Default("GetDate()")]
public DateTime? DateAdded { get; set; }
[Default("20")]
public int Count { get; set; }
}
Read this and this for detail.
With Diego's blog help, to make the public properties of a POCO varchar without using anotations is :
private void SetStringPropertiesAsNonUnicode<e>(DbModelBuilder _modelBuilder) where e:class
{
//Indiquem a totes les propietats string que no són unicode per a que es crein com a varchar
List<PropertyInfo> stringProperties = typeof(e).GetProperties().Where(c => c.PropertyType == typeof(string) && c.PropertyType.IsPublic).ToList();
foreach (PropertyInfo propertyInfo in stringProperties)
{
dynamic propertyExpression = GetPropertyExpression(propertyInfo);
_modelBuilder.Entity<e>().Property(propertyExpression).IsUnicode(false);
}
}
// Edit: Also stole this from referenced blog post (Scott)
static LambdaExpression GetPropertyExpression(PropertyInfo propertyInfo)
{
var parameter = Expression.Parameter(propertyInfo.ReflectedType);
return Expression.Lambda(Expression.Property(parameter, propertyInfo), parameter);
}

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