Entity Framework get user from contex in saveChanges - c#

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.

Related

Is it necessary to check context.Inspections for null?

I have an entity and context defined in Asp.Net Core Web API project with NRT (Nullable Reference Type) enabled as follows.
public class Inspection
{
public int Id { get; set; }
[StringLength(20)]
public string Status { get; set; } = string.Empty;
}
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> opts) : base(opts) { }
public DbSet<Inspection> Inspections { get; set; } = null!;
}
Visual Studio Community generates a controller and one of its endpoints is as follows.
[HttpGet]
public async Task<ActionResult<IEnumerable<Inspection>>> GetInspections()
{
if (context.Inspections == null)
{
return NotFound();
}
return await _context.Inspections.ToListAsync();
}
However, many tutorials I read and watch don't check context.Inspections for null.
In addition, I have read that ToListAsync will return an empty list if no entry found.
Question: Is it necessary to do such a check? Does EF Core guarantee that the properties of type DbSet will never be null?
No, these properties are initialized by the DbContext constructor.
The constructor uses an IDbSetInitialzer object to initialize all DbSet properties.
ServiceProviderCache.Instance.GetOrAdd(options, providerRequired: false)
.GetRequiredService<IDbSetInitializer>()
.InitializeSets(this);
Using an IDbSetInitializer like this allows replacing it with mock initializers for test purposes.
The initializer will find all DbSets on the DbContext and initialize them:
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual void InitializeSets(DbContext context)
{
foreach (var setInfo in _setFinder.FindSets(context.GetType()).Where(p => p.Setter != null))
{
setInfo.Setter!.SetClrValue(
context,
((IDbSetCache)context).GetOrAddSet(_setSource, setInfo.Type));
}
}
Before EF Core 6, only entity sets specified through DbSet<> properties were cached, which resulted in performance gains over direct calls to Set<T>()

Optimistic Concurrency in Entity Framework between Oracle and T-SQL

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();

Where to put Created date and Created by in DDD?

I use Entity Framework and want to use DDD principles. However, there are some information regarding the entities that is on the borderline between what is logging/persistence information and what is information about the domain objects.
I my situation these are put in an abstract base class that all entities inherit from:
public abstract class BaseEntity: IBaseEntity
{
/// <summary>
/// The unique identifier
/// </summary>
public int Id { get; set; }
/// <summary>
/// The user that created this instance
/// </summary>
public User CreatedBy { get; set; }
/// <summary>
/// The date and time the object was created
/// </summary>
public DateTime CreatedDate { get; set; }
/// <summary>
/// Which user was the last one to change this object
/// </summary>
public User LastChangedBy { get; set; }
/// <summary>
/// When was the object last changed
/// </summary>
public DateTime LastChangedDate { get; set; }
/// <summary>
/// This is the status of the entity. See EntityStatus documentation for more information.
/// </summary>
public EntityStatus EntityStatus { get; set; }
/// <summary>
/// Sets the default value for a new object
/// </summary>
protected BaseEntity()
{
CreatedDate = DateTime.Now;
EntityStatus = EntityStatus.Active;
LastChangedDate = DateTime.Now;
}
}
Now a Domain Object can't be instantiated without providing the date and time. However, I feel it is the wrong place to put it. I can argue for both really. Maybe it should not be mixed with the domain at all?
Since I'm using EF Code First it makes sense to put it there, or else I would need to create new classes that inherit from the base class in the DAL also, duplicating code and needing to map to both domain objects and MVC models which does seem more messy than the approach above.
The question(s):
Is it Ok to use DateTime.Now in the Domain model at all? Where do you put this kind of information using DDD and EF Code First? Should User to be set in the domain object or require it in the Business Layer?
Update
I think jgauffin har the right answer here - but it is really quite a fundamental change. However, on my search for an alternate solution I almost had it solved with this. I used the ChangeTracker.Entries to find ut if an entity is added or modified and set the fields accordingly. This is done in my UnitOfWork Save() method.
The problem is loading navigation properties, like User (DateTime is set correctly). It might be since the user is a property on the abstract base class the entity inherits from. I also don't like putting strings in there, however it might solve some simple scenarios for someone, so I post the solution here:
public void SaveChanges(User changedBy)
{
foreach (var entry in _context.ChangeTracker.Entries<BaseEntity>())
{
if (entry.State == EntityState.Added)
{
entry.Entity.CreatedDate = DateTime.Now;
entry.Entity.LastChangedDate = DateTime.Now;
entry.Entity.CreatedBy = changedBy;
entry.Entity.LastChangedBy = changedBy;
}
if (entry.State == EntityState.Modified)
{
entry.Entity.CreatedDate = entry.OriginalValues.GetValue<DateTime("CreatedDate");
entry.Entity.CreatedBy = entry.OriginalValues.GetValue<User>("CreatedBy");
entry.Entity.LastChangedDate = DateTime.Now;
entry.Entity.LastChangedBy = changedBy;
}
}
_context.SaveChanges();
}
Is it Ok to use DateTime.Now in the Domain model at all?
Yes.
Where do you put this kind of information using DDD and EF Code First? Should User to be set in the domain object or require it in the Business Layer?
Well. First of all: A DDD model is always in a valid state. That's impossible with public setters. In DDD you work with the models using methods since the methods can make sure that all required information has been specified and is valid.
For instance, if you can mark an item as completed it's likely that the UpdatedAt date should be changed too. If you let the calling code make sure of that it's likely that it will be forgotten somewhere. Instead you should have something like:
public class MyDomainModel
{
public void MarkAsCompleted(User completedBy)
{
if (completedBy == null) throw new ArgumentNullException("completedBy");
State = MyState.Completed;
UpdatedAt = DateTime.Now;
CompletedAt = DateTime.Now;
CompletedBy = completedBy;
}
}
Read my blog post about that approach: http://blog.gauffin.org/2012/06/protect-your-data/
Update
How to make shure that noone changes the "CreatedBy" and "CreatedDate" later on
I usually have two constructors for the models which also fits the DB. one protected one which can be used by my persistance layer and one which requires the mandatory fields. Put the createdby in that constructor and set the createdate in it:
public class YourModel
{
public YourModel(User createdBy)
{
CreatedDate = DateTime.Now;
CreatedBy = createdby;
}
// for persistance
protected YourModel()
{}
}
Then have private setters for those fields.
I get a lot of R# warning "Virtual member call in constructor", I've read about it before and it is not supposed to be a good practice.
That's usually not a problem. Read here: Virtual member call in a constructor
Is it Ok to use DateTime.Now in the Domain model at all?
It isn't terrible, but the problem is that you will end up having to duplicate code and it will more difficult to achieve consistency.
Where do you put this kind of information using DDD and EF Code First?
You are correct to assert that this type of information doesn't belong in your domain. It is typically called an audit log or trail. There are a few ways to implement auditing with EF. Take a look at AuditDbContext - Entity Framework Auditing Context for instance, or just search around for EF auditing implementations. The idea is that before EF persists changes to an entity, it raises an event which you can listen to and assign the required audit values.
Should User to be set in the domain object or require it in the
Business Layer?
It is best to handle this at the infrastructure/repository level with an auditing implementation as stated above. This is the final stop before data is persisted and thus is the perfect place to take care of this.

How to generalise access to DbSet<TEntity> members of a DbContext?

I have a DbContext with several of the following type of members:
public DbSet<JobLevel> JobLevels { get; set; }
public DbSet<Country> Countries { get; set; }
public DbSet<Race> Races { get; set; }
public DbSet<Language> Languages { get; set; }
public DbSet<Title> Titles { get; set; }
All these are where T: IdNamePairBase, which has Id and Name members only. I am trying desperately to find a common interface with which to access any of these members, to generalise the following MVC3 controller code into one controller:
public ActionResult Edit(DropDownListModel model, Guid)
{
var dbSet = _dbContext.Countries;
var newItems = model.Items.Where(i => i.IsNew && !i.IsDeleted).Select(i => new { i.Name });
foreach (var item in newItems)
{
if (!string.IsNullOrWhiteSpace(item.Name))
{
var undead = ((IEnumerable<IdNamePairBase>)dbSet).FirstOrDefault(p => p.Name.ToLower() == item.Name.ToLower());
if (undead != null)
{
// Assign new value to update to the new char. case if present.
undead.Name = item.Name;
undead.IsDeleted = false;
_dbContext.SaveChanges();
continue;
}
var newPair = new Country { Name = item.Name };
dbSet.Add(newPair);
_dbContext.SaveChanges();
}
}
return RedirectToAction("Edit", new {listName = model.ListName});
}
How could I go about resolving my problem that right now I need one controller for each of the DbContext members, like the one above is dedicated to DbSet<Country> Countries?
PARTIAL SOLUTION: Along lines similar to GertArnold's answer below, before I knew about the _dbContext.Set<T> all he highlights, I implemented this method on my context class to get sets of a specific type:
public IEnumerable<DbSet<T>> GetDbSetsByType<T>() where T : class
{
//var flags = BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance;
var props = GetType().GetProperties()
.Where(p => p.PropertyType.IsGenericType && p.PropertyType.Name.StartsWith("DbSet"))
.Where(p => p.PropertyType.GetGenericArguments().All(t => t == typeof(T)));
return props.Select(p => (DbSet<T>)p.GetValue(this, null));
}
Some generalization is possible by using
var dbSet = _dbContext.Set<T>
and putting most of your method in a method with a generics type parameter.
However, there should be a switch somewhere to decide which type should be specified and which type to create, because I think the type is supplied as a property of the model (is it?). So it probably won't really look elegant, but probably be a lot shorter, with DRY-er code.
To add on Gert Arnold's answer, I want to note that there is another method overload on the dbContext that returns a general DbSet from a type object:
var dbSet = dbContext.Set(typeof(T))
If you want to add blind an object, then create the object using the set.Create() method, or if you already have an object created with the "new" keyowrd, you can convert it by using (similar to this answer)
var entity = dbSet.Create();
dbSet.Add(entity);
DbEntityEntry entry = context.Entry(entity);
entry.CurrentValues.SetValues(yourObject);
I've been looking for an answer to this question and I've found that it is easy to do using the Managed Extensibility Framework. There is a quicker way at the bottom of this post, however MEF allows for a much more scalable approach.
MEF allows you to build dynamic access plugins from disparate Assemblies; however it can be used to quickly populate Collections within a single assembly application.In essence, we'll be using it as a safe way of reflecting our assembly back into the class. In order to make this fully functional, I'm also going to implement the Strategy Pattern to the Entity Framework Model.
Add a reference to your project, pointing to System.ComponentModel.Composition. This will give access to the MEF library.
Now, we need to implement the Strategy Pattern. If you don't have an Interfaces folder, create one, and add IEntity.cs, as below.
IEntity.cs
namespace Your.Project.Interfaces
{
/// <summary>
/// Represents an entity used with Entity Framework Code First.
/// </summary>
public interface IEntity
{
/// <summary>
/// Gets or sets the identifier.
/// </summary>
/// <value>
/// The identifier.
/// </value>
int Id { get; set; }
}
}
Now, each of you concrete entities need to implement this Interface:
public class MyEntity : IEntity
{
#region Implementation of IEntity
/// <summary>
/// Gets or sets the identifier.
/// </summary>
/// <value>
/// The identifier.
/// </value>
public int Id { get; set; }
#endregion
// Other POCO properties...
}
I find that it is best practice, not to create individual interfaces for each entity, unless you're working in a high testing environment. Pragmatically, interfaces should only be used where that level of abstraction is needed; mainly when more than one concrete class will inherit, or when working with an over-enthusiastic Inversion of Control engine. If you have interfaces for everything in your production model, your architecture more than likely, has major flaws. Anyway, enough of the rambling.
Now that we have all of our entities "strategised", we can use MEF to collate them and populate a collection within your context.
Within your context, add a new property:
/// <summary>
/// Gets a dynamically populated list of DbSets within the context.
/// </summary>
/// <value>
/// A dynamically populated list of DbSets within the context.
/// </value>
[ImportMany(typeof(DbSet<IEntity>))]
public IEnumerable<DbSet<IEntity>> Sets { get; private set; }
The [ImportMany(typeof(DbSet<IEntity>))] here, allows MEF to populate the collection.
Next, add the corresponding Export attribute to each DbSet within the context:
[Export(typeof(DbSet<IEntity>))]
public DbSet<MyEntity> MyEntities { get; set; }
Each of the Imported and Exported properties is known as a "part". The final piece to the puzzle is to compose those parts. Add the following to your context's constructor:
// Instantiate the Sets list.
Sets = new List<DbSet<IEntity>>();
// Create a new Types catalogue, to hold the exported parts.
var catalogue = new TypeCatalog(typeof (DbSet<IEntity>));
// Create a new Composition Container, to match all the importable and imported parts.
var container = new CompositionContainer(catalogue);
// Compose the exported and imported parts for this class.
container.ComposeParts(this);
Now, with any luck, you should have a dynamically populated list of DbSets, within your context.
I have used this method to allow easy truncating of all tables via an extension method.
/// <summary>
/// Provides extension methods for DbSet objects.
/// </summary>
public static class DbSetEx
{
/// <summary>
/// Truncates the specified set.
/// </summary>
/// <typeparam name="TEntity">The type of the entity.</typeparam>
/// <param name="set">The set.</param>
/// <returns>The truncated set.</returns>
public static DbSet<TEntity> Truncate<TEntity>(this DbSet<TEntity> set)
where TEntity : class, IEntity
{
set.ToList().ForEach(p => set.Remove(p));
return set;
}
}
I have added a method to the context to truncate the whole database.
/// <summary>
/// Truncates the database.
/// </summary>
public void TruncateDatabase()
{
Sets.ToList().ForEach(s => s.Truncate());
SaveChanges();
}
EDIT (Overhaul):
The solution above has now been depreciated. Some tweeking as had to be done to get this to work now. To make this work, you need to import the DbSets into a temporary collection of DbSet of type "object", then cast this collection to DbSet of your required interface type. For basic purposes, the IEntity interface will suffice.
#region Dynamic Table List
/// <summary>
/// Gets a dynamically populated list of DbSets within the context.
/// </summary>
/// <value>
/// A dynamically populated list of DbSets within the context.
/// </value>
public List<DbSet<IEntity>> Tables { get; private set; }
/// <summary>
/// Gets a dynamically populated list of DbSets within the context.
/// </summary>
/// <value>
/// A dynamically populated list of DbSets within the context.
/// </value>
[ImportMany("Sets", typeof (DbSet<object>), AllowRecomposition = true)]
private List<object> TableObjects { get; set; }
/// <summary>
/// Composes the sets list.
/// </summary>
/// <remarks>
/// To make this work, you need to import the DbSets into a temporary collection of
/// DbSet of type "object", then cast this collection to DbSet of your required
/// interface type. For basic purposes, the IEntity interface will suffice.
/// </remarks>
private void ComposeSetsList()
{
// Instantiate the list of tables.
Tables = new List<DbSet<IEntity>>();
// Instantiate the MEF Import collection.
TableObjects = new List<object>();
// Create a new Types catalogue, to hold the exported parts.
var catalogue = new TypeCatalog(typeof (DbSet<object>));
// Create a new Composition Container, to match all the importable and imported parts.
var container = new CompositionContainer(catalogue);
// Compose the exported and imported parts for this class.
container.ComposeParts(this);
// Safe cast each DbSet<object> to the public list as DbSet<IEntity>.
TableObjects.ForEach(p => Tables.Add(p as DbSet<IEntity>));
}
#endregion
Next, run the CompileSetsList() facade from the constructor (with best practices for Web shown):
public MvcApplicationContext()
{
// Enable verification of transactions for ExecuteSQL functions.
Configuration.EnsureTransactionsForFunctionsAndCommands = true;
// Disable lazy loading.
Configuration.LazyLoadingEnabled = false;
// Enable tracing of SQL queries.
Database.Log = msg => Trace.WriteLine(msg);
// Use MEF to compile a list of all sets within the context.
ComposeSetsList();
}
Then, just decorate your DbSet<>s like this:
/// <summary>
/// Gets or sets the job levels.
/// </summary>
/// <value>
/// The job levels.
/// </value>
[Export("Sets", typeof(DbSet<object>))]
public DbSet<JobLevel> JobLevels { get; set; }
Now it will work properly.

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