Creating a common audit column in EF6 with inheritance - c#

I have an existing database that has auditing on some tables with effectively temporal table functionality behind them (records get timestamped when changed and previous versions are stored in a history table, this is taken care by SQL and not EF/app logic).
There is a TPC Table-Per-Concrete-Class inheritance model being used. I have simplified the problem in the code below (Console app that will create a db called EfAudit on local).
Table Supplier already has an audit column on it, called ChangedBy, which works perfectly.
But Supplier is a specialised type of Organisation, normally Organisation is sufficient to store all the information required for an organisation - hence it not being marked as abstract.
I now need to record when the Organisations table has been updated with a ChangedBy field. But when I add it to the model/ef only the underlying Organisations table is ever updated.
I know with EFCore you can use Shadow Properties to do similar work, but this system is built on Framework and not Core and switching isn't feasible.
I have tried setting the CurrentValues for the column explicitly on each entity, but only the base table ever has the value written to it. I've tried blocking the base class in C# with the new keyword. I cannot find out how I achieve this with EF6.
using System;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration;
using System.ComponentModel.DataAnnotations.Schema;
namespace EfAuditTest
{
class Program
{
static void Main(string[] args)
{
Context cxt = new Context();
cxt.Suppliers.Add(new Supplier() // Some test data
{
Name = "Acme Inc",
ContractStart = DateTime.Now,
});
cxt.Organisations.Add(new Organisation() // A generic organisation that isn't a supplier
{
Name = "Just a business"
});
cxt.SaveChanges();
}
}
#region Model
interface IAudit // An auditing interface applicable to most database tables
{
string ChangedBy { get; set; }
}
public abstract class Base // Common table stuff
{
public int Id { get; set; }
}
public class Organisation : Base, IAudit // An organisation, concrete as sometimes has everything needed
{
public string Name { get; set; }
public string ChangedBy { get; set; } = "Application User 42";
}
public class Supplier : Organisation // A specialised organsiation that extends with some extra fields
{
public DateTime ContractStart { get; set; } = DateTime.Now;
}
#endregion
#region Schema
public class OrganisationSchema : EntityTypeConfiguration<Organisation>
{
public OrganisationSchema()
{
Map(m =>
{
m.ToTable("Organisations");
});
HasKey(m => m.Id)
.Property(m => m.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
/* THIS BIT HERE
* If I add this to the Organisation table, it stops working in the Supplier table. */
Property(p => p.ChangedBy);
}
}
public class SupplierSchema : EntityTypeConfiguration<Supplier>
{
public SupplierSchema()
{
Map(m =>
{
m.ToTable("Suppliers");
});
/* THIS BIT HERE
* This mapping is now ignored by EF, and only the one in OrgansiationSchema is updated on writes */
Property(p => p.ChangedBy);
}
}
#endregion
#region Context
public class Context : DbContext
{
public virtual DbSet<Organisation> Organisations { get; set; }
public virtual DbSet<Supplier> Suppliers { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new OrganisationSchema());
modelBuilder.Configurations.Add(new SupplierSchema());
base.OnModelCreating(modelBuilder);
}
public Context() : base("Server=(local);Database=EfAudit;Integrated Security=SSPI")
{
}
}
#endregion
}
Many thanks for any answers!

Related

EF Core 3.1 - DDD and inheritance

I am working on a legacy app that mainly manages employees and contractors. Below is an excerpt of the EF Core 3.1 legacy model. The complete source code is available here.
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public int? ManagerId { get; set; }
public virtual Person Manager { get; set; }
}
public class Employee : Person
{
public int Grade { get; set; }
}
public class Contractor : Person
{
public int ContractorId { get; set; }
public virtual ContractingCompany Company { get; set; }
}
public class ContractingCompany
{
public int Id { get; private set; }
public string Name { get; private set; }
private readonly List<Contractor> _contractors = new List<Contractor>();
public virtual IReadOnlyList<Contractor> Contractors => _contractors;
protected ContractingCompany()
{
}
public ContractingCompany(string name) : this()
{
Name = name;
}
public void Add(Contractor contractor)
{
_contractors.Add(contractor);
}
}
These entities pull data from the same table as we are using TPH strategy.
We are extending our application and renaming the contractors to Partners instead. We decided to go with DDD this time and have new models read existing data using table splitting. As we move things to the new model, we need to keep the app working, so we can't remove the legacy model altogether until all use cases have moved to the new DDD model.
The DDD model is as follows and will pull data from the existing database:
public class Partner /* Pulls data from the Contracting Company */
{
public int Id { get; set; }
public string Name { get; set; }
private readonly List<PartnerEmployee> _employees = new List<PartnerEmployee>();
public virtual IReadOnlyList<PartnerEmployee> Employees => _employees;
protected Partner(){}
}
public class PartnerEmployee /* Pulls data from the Contractor table */
{
public int Id { get; set; }
public int ContractorId { get; set; }
}
The EF mappings are as follows:
public class PartnerConfiguration : IEntityTypeConfiguration<Partner>
{
public void Configure(EntityTypeBuilder<Partner> builder)
{
builder.ToTable("ContractingCompany");
builder.HasKey(c => c.Id);
builder.Property(c => c.Id).HasColumnName("Id");
builder.Property(c => c.Name).HasColumnName("Name");
builder.HasOne<ContractingCompany>().WithOne().HasForeignKey<Partner>(c => c.Id);
}
}
public class PartnerEmployeeConfiguration : IEntityTypeConfiguration<PartnerEmployee>
{
public void Configure(EntityTypeBuilder<PartnerEmployee> builder)
{
builder.ToTable("Person");
builder.HasKey(c => c.Id);
builder.Property(c => c.Id).HasColumnName("Id");
builder.Property(c => c.ContractorId).HasColumnName("ContractorId");
builder.Property<int?>("PartnerId").HasColumnName("CompanyId");
builder.HasOne<Contractor>().WithOne().HasForeignKey<PartnerEmployee>(c => c.Id);
}
}
Problem: we are trying to read the existing data from the database:
var contractingCompany = context.ContractingCompanies.First(); <-- Works fine
var partner = context.Partners.First(); <-- Crashes
The second line above throws an exception:
Microsoft.Data.SqlClient.SqlException:
Invalid column name 'Contractor_CompanyId'.
Invalid column name 'ContractorId1'.'
Can anyone help me understand why EF looks up columns Contractor_CompanyId ContractorId1?
Looks like configuring a column name for a property of an entity participating in table splitting (other than PK) invalidates the conventional column names for the other participating entity/entities.
Unfortunately this behavior is not explained in the table splitting documentation (it should), only a small text to to accompanying example saying
In addition to the required configuration we call Property(o => o.Status).HasColumnName("Status") to map DetailedOrder.Status to the same column as Order.Status.
and then you can see in the sample fluent configuration that Property(o => o.Status).HasColumnName("Status") is called for both Order and DetailedOrder.
Shortly, you must explicitly configure column names for shared columns for both (all if more then one) entities.
In your case, the minimal configuration needed (in addition of what you have currently) is like this (using modelBuilder fluent API directly, but you can put them in separate entity configuration classes if you wish):
modelBuilder.Entity<ContractingCompany>(builder =>
{
builder.Property(c => c.Name).HasColumnName("Name");
});
modelBuilder.Entity<Contractor>(builder =>
{
builder.Property(c => c.ContractorId).HasColumnName("ContractorId");
builder.Property("CompanyId").HasColumnName("CompanyId");
});

EFCore - Seed data only if it doesn't exist in another table

I want to seed some data using EntityFramework in .net core 3.1 and I'm facing an issue:
I've got two SQL tables (so two DbSet<>):
public virtual DbSet<TableA> TableA { get; set; }
public virtual DbSet<TableB> TableB { get; set; }
Table A has this structure:
[Key]
public int Id { get; set; } // PK
public string EnglishText { get; set; } // some value
Table B has this structure:
[Key]
public int Id { get; set; } // PK
public int TableAId { get; set; } // FK to Table A
public string TranslatedText { get; set; } // some value
For seeding the data for table A, I use the OnModelCreating(ModelBuilder modelBuilder) method in the DBContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Seed TableA
modelBuilder.Entity<TableA>().HasData(
new TableA { Id = 1, EnglishText = "first data"}
);
}
I then want to seed table B only if it doesn't contain a record that reference TableA (via the FK), I'm not sure how to do that in the OnModelCreating method.
I guess I'm after something like:
modelBuilder.Entity<TableB>().HasData(var X = new TableB{...}).Where([X.TableAId is not in TableA])
If someone has an idea or can point me in a direction, that would be very much appreciated.
I had the exact same problem. So i decided to extend DataBuilder as follow:
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System;
using System.Collections.Generic;
namespace Infrastructure
{
static class DataBuilderDynamicData
{
private static object asyncObject = new object();
private static Dictionary<Type, List<EntityBase>> dynamicData = new Dictionary<Type, List<EntityBase>>();
public static DataBuilder<TEntity> HasDynamicData<TEntity>(this EntityTypeBuilder<TEntity> builder, Func<Dictionary<Type, List<EntityBase>>, TEntity> Resolver) where TEntity : EntityBase
{
var targetType = typeof(TEntity);
TEntity model;
//Note: Not necessary, but just in case several threads reached the same code scope.
//Note: Lock has some performance hits, but it is negligible due to the fact that Seeding is an In-Design-time process (not run-time).
lock (asyncObject)
{
model = Resolver(dynamicData);
if (dynamicData.ContainsKey(targetType))
dynamicData[targetType].Add(model);
else
dynamicData.Add(targetType, new List<EntityBase> { model });
}
return builder.HasData(model);
}
}
}
Then use it instead of HasData() like following code:
foreach (var model in models)
{
builder.HasDynamicData<ProvinceMap>((dic) =>
{
//Please note that "Province" should be already seeded with "HasDynamicData" method. Otherwise, you get an error here.
var provinces = dic[typeof(Province)];
return new ProvinceMap
{
Id = 1,
ProvinceId = provinces.Cast<Province>().First(item => item.Name=="SOME-PROVINCE"),
//Some other seed data.
};
});
}
Please note that EntityBase is my custom base class. You can use object instead.
public abstract class EntityBase
{
public int Id { get; set; }
}
Every entities e.g. Province and ProvinceMap are inherited from EntityBase.
You can make sure that dictionary already has the base data you want (in my example: Province class) with placing the relevent config code prior to consumer class ()in my example ProvinceMap class:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration<Province>(new ProvinceConfiguration()); //First, declare the base class.
modelBuilder.ApplyConfiguration<ProvinceMap>(new ProvinceMapConfiguration()); //Next, declare the consumer class.
}
You can implement other overloads of the HasData() method to fit your needs.

Using a single Entity Framework Core DbContext to manage multiple database schemas with homonymous tables

In a .NET Core 2.1 library I need to access to a MySQL database organized in multiple schemas with tables that can have the same name across those schemas. I can't make any changes to the DB since it comes from another company.
For most of the tables I need a read-only access and I'd like to use a single EF Core DbContext.
Actually I get this error message during initialization:
InvalidOperationException: Cannot use table 'tbl_panel' for
entity type 'Db2Panels' since it is being used for entity
type 'Db1Panels' and there is no relationship between their
primary keys.
I think that the crux of the matter mainly resides in the configuration methods, which should be called not just once but N times, one for each instance of the entity with different schema (db_machine_1.tbl_panel, db_machine_2.tbl_panel, etc.).
How can I reach my goal?
This is my actual implementation.
Database schemas
// db_machine_1 schema
db_machine_1.tbl_panel
db_machine_1.tbl_basket
db_machine_1.tbl_unit
// db_machine_2 schema
db_machine_2.tbl_panel
db_machine_2.tbl_basket
db_machine_2.tbl_discard
// Other db_machine_X schemas with similar structure...
DbContext configuration
public class MyDbContext : DbContext
{
// Schema: db_machine_1
public DbSet<Panel> Db1Panels { get; set; }
public DbSet<Basket> Db1Baskets { get; set; }
public DbSet<Unit> Db1Units { get; set; }
// Schema: db_machine_2
public DbSet<Panel> Db2Panels { get; set; }
public DbSet<Basket> Db2Baskets { get; set; }
public DbSet<Discard> Db2Discards { get; set; }
// Other schemas DbSet<X> objects...
// Arrays to access the specific DbSet by using the schema number:
// Panels[1] -> Db1Panels, Panels[2] -> Db2Panels, ...
public DbSet<Panel>[] Panels { get; }
public DbSet<Basket>[] Baskets { get; }
// Other arrays for other DbSet<X> objects...
public MyDbContext(DbContextOptions<MyDbContext> options)
: base(options)
{
// Arrays initialization
List<DbSet<Panel>> dbPanelList = new List<DbSet<Panel>>();
dbPanelList.Add(Db1Panels);
dbPanelList.Add(Db2Panels);
Panels = dbPanelList.ToArray();
List<DbSet<Basket>> dbBasketList = new List<DbSet<Basket>>();
dbBasketList.Add(Db1Baskets);
dbBasketList.Add(Db2Baskets);
Baskets = dbBasketList.ToArray();
// Initialization for other DbSet<X> objects...
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyAllConfigurations<MyDbContext>();
modelBuilder.ApplyAllConversions();
}
}
Objects
public class Panel
{
public long Id { get; set; }
public string SN { get; set; }
// Other properties...
}
public class Basket
{
public long Id { get; set; }
public string Description { get; set; }
// Other properties...
}
Configurations
public class PanelConfiguration : IEntityTypeConfiguration<Panel>
{
public void Configure(EntityTypeBuilder<Panel> builder)
{
builder.ToTable("tbl_panel");
builder.HasKey(e => e.Id);
builder.Property(e => e.Id)
.HasColumnName("ID_Record");
builder.Property(e => e.SN)
.HasColumnName("Serial")
.HasMaxLength(20);
// Other properties configuration...
}
}
public class BasketConfiguration : IEntityTypeConfiguration<Basket>
{
public void Configure(EntityTypeBuilder<Basket> builder)
{
builder.ToTable("tbl_basket");
builder.HasKey(e => e.Id);
builder.Property(e => e.Id)
.HasColumnName("ID_Record");
builder.Property(e => e.Description)
.HasColumnName("Desc")
.HasMaxLength(100);
// Other properties configuration...
}
}
// Other IEntityTypeConfiguration implementations for other tables...
// This extension method is used to automatically load all Configurations
// of the various entities
public static class ModelBuilderExtensions
{
public static void ApplyAllConfigurations(this ModelBuilder modelBuilder)
{
var applyConfigurationMethodInfo = modelBuilder
.GetType()
.GetMethods(BindingFlags.Instance | BindingFlags.Public)
.First(m => m.Name.Equals("ApplyConfiguration", StringComparison.OrdinalIgnoreCase));
var ret = typeof(T).Assembly
.GetTypes()
.Select(t => (t, i: t.GetInterfaces().FirstOrDefault(i => i.Name.Equals(typeof(IEntityTypeConfiguration<>).Name, StringComparison.Ordinal))))
.Where(it => it.i != null)
.Select(it => (et: it.i.GetGenericArguments()[0], cfgObj: Activator.CreateInstance(it.t)))
.Select(it => applyConfigurationMethodInfo.MakeGenericMethod(it.et).Invoke(modelBuilder, new[] { it.cfgObj }))
.ToList();
}
}
UPDATE about base class arrays
After creating base abstract classes and derived ones, I'd like to merge all the derived class objects into a single array to be able to access the specific DbSet by using the schema number. See also above code of DbContext constructor.
I'm having problems with casting...
List<DbSet<Panel>> dbPanelList = new List<DbSet<Panel>>();
dbPanelList.Add((DbSet<Panel>)Db1Panels.Select(g => g as Panel)); // NOT WORKING! Cast Exception
dbPanelList.Add((DbSet<Panel>)Db2Panels.Cast<DbSet<Panel>>()); // NOT WORKING! Cast Exception
Panels = dbPanelList.ToArray();
Is this possible somehow?
I think you can't get away from having two different EF objects for the different tables, and you probably shouldn't as they may diverge at some point in the future.
At a minimum you need two classes Db1Panel and Db2Panel . I assume that actually the "Db" prefix is meant to meant a different schema, not actually a different database.
However that shouldn't be a big problem as there are other ways within C# of making them behave in similar fashions. Two options that spring to mind are having them inherit from the same base class, or have them implement an interface:
public abstract class PanelBase
{
public long Id { get; set; }
// other properties
}
[Table("tbl_panel", Schema = "Db1")]
public class Db1Panel : PanelBase{}
[Table("tbl_panel", Schema = "Db2")]
public class Db2Panel : PanelBase{}
If you chose to implement the interface you would need to repeat the properties in each class, but refactoring tools make this quite easy.
public interface IPanel
{
public long Id { get; set; }
}
[Table("tbl_panel", Schema = "Db1")]
public class Db1Panel : IPanel
{
public long Id { get; set; }
}
[Table("tbl_panel", Schema = "Db2")]
public class Db2Panel : IPanel
{
public long Id { get; set; }
}
Or depending on the size of your application you could consider having another namespace of domain objects and just map the database objects into it:
You should be able to use the Table attribute. There's a parameter Schema that allows you to set the schema name. See here for documentation. In your case you'd get something like
[Table("Table1", Schema="Schema1")]
public class Entity1Schema1
{
public string Property1 {get;set;}
}
[Table("Table1", Schema="Schema2")]
public class Entity1Schema2
{
public string Property1 {get;set;}
}
And then of course you can use interfaces or base classes to refactor your code as #ste-fu already mentioned.

How to create tables in codefirst approach

I am working on Login page creating in C# using Code First approach , in this I am getting lot of errors while trying to execute the code. I am a fresher and new to this .
Can you please review my code and help me what I missed in this ?
Rule are not creating and getting the multiple errors. Your help would help me to understand what went wrong in this.
My Class "Class1.cs"
public class Login
{
[Required]
public string username { get; set; }
[Required]
public string password{ get; set; }
}
}
public class LoginContext : DbContext
{
public LoginContext() : base("LoginDBConnectionString")
{
Database.SetInitializer<LoginContext>(new DropCreateDatabaseIfModelChanges<LoginContext>());
}
public DbSet<username> username { get; set; }
public DbSet<password> password { get; set; }
}
Context.cs
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Linq;
using System.Web;
using System.Web.UI.WebControls;
using System.Data.Entity;
using Jquery1.Models;
namespace Jquery1.Models
{
public class Logincontext: DbContext
{
public Logincontext () : base ("LoginDBConnectionString")
{
}
public DbSet<Login> Logins{ get; set; }
}
}
class program
{
static void Main(string[] args)
{
using (var ctx = new Logincontext())
{
ctx.Database.Create();
}`enter code here`
}
}
Hi Let me explain this using fluent api bear with me a little please,
Create Your DbContext First:
public class MyDbContext : DbContext
{
public MyDbContext()
: base("name=MyConnection")
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyDbContext, YourApplication.Migrations.Configuration>("MyConnection"));
}
public DbSet<Users> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//here you can MAP Your Models/Entities, but i am going to show you something more interesting. so keep up.
modelBuilder.Configurations.Add(new UsersMap());
}
}
Create a Migration Folder in your app root And make Configuration class there(Why?! so that everytime you made a change migration of EF will update the Tables for you):
internal sealed class Configuration : DbMigrationsConfiguration<YourApplication.Infrastructure.Data.MyDbContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
//this feature on true can result in unwanted/unexpected dataloss
AutomaticMigrationDataLossAllowed = true;
ContextKey = "YourApplication.Infrastructure.Data.MyDbContext";
}
protected override void Seed(YourApplication.Infrastructure.Data.MyDbContext context)
{
// This method will be called after migrating to the latest version.
// You can use the DbSet<T>.AddOrUpdate() helper extension method
// to avoid creating duplicate seed data. E.g.
//
// context.People.AddOrUpdate(
// p => p.FullName,
// new Person { FullName = "Andrew Peters" },
// new Person { FullName = "Brice Lambson" },
// new Person { FullName = "Rowan Miller" }
// );
//
}
}
Now Go on And Create Your POCO Classes. I try to write my codes very clean. That's why when for example i made a Model like below, i create an EntityBase for every Id:
public class EntityBase
{
public int Id { get; set; }
}
And Implement it to my Model :
public class User: EntityBase
{
public string Example1{ get; set; }
public string Example2{ get; set; }
public string Example3{ get; set; }
}
And For Mapping I Create another Class like below and use Fluent Api:
public class UserMap : EntityTypeConfiguration<User>
{
public UserMap()
{
//declaring the table name
ToTable("TblUser");
//declaring primary key of the table
HasKey(x => x.Id);
//declaring a property from poco class is required
Property(x => x.Example1)
.IsRequired();
//etc
}
}
be aware if you are using fluent api, you Shouldn't use Data Annotations. Happy Coding.
Entity frame-work uses a concept of standards-or-else. If you want your items fairly standard, you don' have to provide a lot of information. If you want your tables to have different names, or your columns to be different than standard you'll have to provide extra information using either Attributes (the method you use) or fluent API.
Every class that should become a table should have an Primary key. The default is to give your class a property Id, or to give the property the name of your class followed by Id:
public class Login
{
public int Id {get; set;}
public string UserName {get; set;}
public string Password {get; set;}
}
public class MyDbContext : DbContext
{
public DbSet<Login> Logins {get; set;}
}
This should be enough to give you a table with the default name, which is the plural of your class name Logins. Each record in the table has three columns:
Id: the primary key
UserName: a string, which in this case may be null
PassWord: a string which may be null.
Your Requires attribute will ensure that your UserName and Property are not null, but they won't prevent them being empty. I'm not sure that's enough for you.
Instead of Id your are free to use LoginId as foreign key:
public int LoginId {get; set;}
You'll see both methods used. Both have their advantages. The use of Id shows you immediately which property is the primary key. LoginId could also be used as foreign key. The name LoginId alone is not enough to see whether it is a primary key or a foreign key.
The defaults are usually plurals for collections of items where the item is the singular form. Here you'll see Login as class, and Logins as a set of objects of this class.
The article that helped me a lot to get on track using Entity Framework was this entity framework tutorial
The tutorial tells you about how to use defaults, how to create one-to-many relations with foreign keys, many-to-many, various inheritance strategies, and what to do if you are not satisfied with a default model.

Fluent NHibernate PropertyNotFoundException for Auto Property

I'm trying to get Fluent NHibernate to map a collection for me. My class definitions are as follows:
public abstract class Team
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
}
public class ClientTeam : Team
{
public virtual IEnumerable<Client> Clients { get; set; }
}
public class Client
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual string Identifiers { get; set; }
}
My mappings:
public class TeamMap : ClassMap<Team>
{
public TeamMap()
{
Table("Team");
Id(x => x.Id).GeneratedBy.Assigned();
Map(t => t.TeamName);
}
}
public class ClientTeamMap : SubclassMap<ClientTeam>
{
public ClientTeamMap()
{
HasMany(t => t.Clients);
}
}
public class ClientMap : ClassMap<Client>
{
public ClientMap()
{
Table("Client");
Id(c => c.Id);
Map(c => c.Name);
Map(c => c.Identifiers);
}
}
I've built a unit test that instantiates a team and then attempts to persist it (the test base has dependency configuration, etc. in it):
public class TeamMapTester : DataTestBase
{
[Test]
public void Should_persist_and_reload_team()
{
var team = new ClientTeamDetail
{
Id = Guid.NewGuid(),
TeamName = "Team Rocket",
Clients = new[]
{
new ClientDetail {ClientName = "Client1", ClientIdentifiers = "1,2,3"}
}
};
using (ISession session = GetSession())
{
session.SaveOrUpdate(team);
session.Flush();
}
AssertObjectWasPersisted(team);
}
}
When I run the test, I get this error:
SetUp : FluentNHibernate.Cfg.FluentConfigurationException : An invalid or incomplete configuration was used while creating a SessionFactory. Check PotentialReasons collection, and InnerException for more detail.
Database was not configured through Database method.
----> NHibernate.MappingException: Could not compile the mapping document: (XmlDocument)
----> NHibernate.PropertyNotFoundException : Could not find field '_clients' in class 'ClientTeam'`
I've looked through the NHibernate documentation and done some google searching, but I can't find anything that appears to address this issue. The documentation for Fluent NHibernate's Referencing methods explicitly uses auto properties, so I'm sure that's not the issue.
Why might NHibernate think that _clients is the field it should map in this case?
And the reason turns out to be: Conventions.
The Fluent mappings were set up to try to enforce read-only collection properties, by requiring a backing field. The ICollectionConvention in question:
public class CollectionAccessConvention : ICollectionConvention
{
public void Apply(ICollectionInstance instance)
{
instance.Fetch.Join();
instance.Not.LazyLoad();
instance.Access.CamelCaseField(CamelCasePrefix.Underscore);
}
}
which requires that collection backing fields be camelCased and start with an underscore.

Categories