Version as timestamp in Fluent NHibernate / SQL Server - sql-server

Using FNH w/ SQL Server 2008, I'm trying to add a Version as a timestamp, but running into the SQLDateTime Overflow error because the value is passed as 1/1/0001 12:00:00 AM. I found this (also referenced here), but still experiencing the problem.
// entity base
public abstract class EntityBase
{
public virtual Int64 Id { get; set; }
public virtual DateTime Version { get; set; }
}
// entity base map
public abstract class EntityBaseMap<T> : ClassMap<T> where T : EntityBase
{
public EntityBaseMap()
{
Id(x => x.Id).GeneratedBy.Identity();
OptimisticLock.Version();
Version(x => x.Version)
.CustomType("Timestamp");
}
}
The SQL Server data type is "datetime".
I'm guessing its something small and stupid, but haven't found the cause yet - what am I missing?
EDIT: Action method for the actual "save" code
public ActionResult Create()
{
int currMaxSortOrder = session.CreateCriteria(typeof(Section))
.SetProjection(Projections.ProjectionList().Add(Projections.Max("Sortorder")))
.UniqueResult<int>();
SectionViewModel sectionViewModel = new SectionViewModel();
sectionViewModel.Sortorder = currMaxSortOrder + 1;
return View("Create", "_AdminLayout", sectionViewModel);
}
[HttpPost]
public ActionResult Create(SectionViewModel sectionInputModel)
{
if (ModelState.IsValid)
{
section = new Section();
Mapper.Map(sectionInputModel, section);
using (var tx = session.BeginTransaction())
{
session.SaveOrUpdate(section);
tx.Commit();
}
return RedirectToAction("index", "pages").WithFlash(new { success = "Section '" + section.Name + "' was successfully added." });
}
return View("Create", "_AdminLayout", section);
}
Edit 2: Added section entity & mapping
public class Section : EntityBase
{
public virtual String Name { get; set; }
public virtual int Sortorder { get; set; }
public virtual String RedirectUrl { get; set; }
public virtual IList<Page> Pages { get; set; }
public Section()
{
Pages = new List<Page>();
}
public virtual void AddPage(Page page)
{
page.Section = this;
this.Pages.Add(page);
}
}
public class SectionMap : EntityBaseMap<Section>
{
public SectionMap()
{
Map(x => x.Name);
Map(x => x.Sortorder);
Map(x => x.RedirectUrl);
// one to many relationship
HasMany(x => x.Pages)
.Inverse()
.Cascade.All();
}
}
}

sheepish Doh! moment
(Adding this in case any other n00bs like me run into the same problem)
I finally dug deeper and realized that I had configured it to use AutoMapping while I was creating maps that would only work with FluentMapping. Reverted to use FluentMapping and the Version started working perfectly!
I'm guessing I could possibly use AutoMapping and add a convention that will treat a column named "Version" with CustomType("Timestamp"), but for now am going to use FluentMapping until I get more up to speed.

This might be the classic .NET min datetime != SQL Server min datetime.
The min datetime in .NET is in the year 0001, but in SQL server the min date can only go as low as the year 1753. You're getting an overflow in SQL Server because the SQL datetime type can't store the date you're trying to pass.
You might have better luck with the datetime2 type, but I'm not sure of the compatibility with Hibernate.
See this article for more info: http://blog.malevy.net/2010/01/datetimeminvalue-sql-server-minimum.html

Related

EF Core foreign keys not working with existing database

I am building a NET Core MVC app that consumes an existing MS SQL database. Primary keys and foreign keys are already established and working correctly at the database level.
I followed the example in this article and used package manager console to reverse engineer the models and database context from the database. This seemed to work well. It resulted in all models being added to my app's Models folder including a robust database context class. The problem I'm having is that relational information about these entities isn't being populated at runtime. I'm getting nulls for related entities for which foreign keys are established both in the database and in the fluent API code generated by the scaffolding process.
I have two tables, Mode and Submode, that are related via foreign key.
Scaffolding generated these two classes for the above two tables:
public partial class Submode
{
public Submode()
{
Contact = new HashSet<Contact>();
}
public int Id { get; set; }
public int ModeId { get; set; }
public string Code { get; set; }
public bool Visible { get; set; }
public bool IsDefault { get; set; }
public Mode Mode { get; set; }
public ICollection<Contact> Contact { get; set; }
}
public partial class Mode
{
public Mode()
{
Contact = new HashSet<Contact>();
Submode = new HashSet<Submode>();
}
public int Id { get; set; }
public string Code { get; set; }
public bool Visible { get; set; }
public bool IsDefault { get; set; }
public ICollection<Contact> Contact { get; set; }
public ICollection<Submode> Submode { get; set; }
}
Scaffolding also generated this fluent API snippet in the database context:
modelBuilder.Entity<Submode>(entity =>
{
entity.HasIndex(e => e.Code)
.HasName("UQ__Submode__A25C5AA75D2A9AE7")
.IsUnique();
entity.Property(e => e.Code)
.IsRequired()
.HasMaxLength(100)
.IsUnicode(false);
entity.HasOne(d => d.Mode)
.WithMany(p => p.Submode)
.HasForeignKey(d => d.ModeId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_Submode_ModeId");
});
Every example I've read on setting foreign keys with fluent API show a similar pattern to the above snippets. But Mode comes back null for Submode.
Null at runtime
And I get a null reference exception in the returned view because I'm trying to display properties of the related Mode object. Am I missing some configuration or is there a problem with the scaffolded code?
UDPATE - as requested, here's the implementation that's fetching data from the database context.
public class SQLSubModeData : ISubModeData
{
private w4lkrContext _context;
public SQLSubModeData(w4lkrContext context)
{
_context = context;
}
public IQueryable<Submode> Get()
{
return _context.Submode.OrderBy(p => p.Id);
}
public Submode Get(int id)
{
return _context.Submode.FirstOrDefault(p => p.Id == id);
}
}
UPDATE (SOLVED) - Enabling lazy loading fixed the problem. Three steps got me there:
Installed Microsoft.EntityFrameworkCore.Proxies(2.1.2) via NuGet
Updated Startup.cs -> AddDbContext() method, as follows:
services.AddDbContext(options => options.UseLazyLoadingProxies().UseSqlServer(_configuration.GetConnectionString("W4LKR")));
Made all navigation properties virtual. This had to be done on every model in the app, not just the one being called in my example above. Errors are thrown if even one is left out.
But Mode comes back null for Submode.
Since your Navigation Properties aren't declared as virtual, you have disabled Lazy Loading, so EF will only populate your Navigation Properties if you do Eager Loading, or Explicit Loading.
See Loading Related Data

saving data from mvc to a database

Hi there everyone I have a Mvc and i am trying to register a user to a sql sever express database using localdb this is the error i am getting
"
An exception of type 'System.Data.Entity.ModelConfiguration.ModelValidationException' occurred in EntityFramework.dll but was not handled in user code
Additional information: One or more validation errors were detected during model generation:
AfterCareWebsite.Patient: : EntityType 'Patient' has no key defined. Define the key for this EntityType.
Patient: EntityType: EntitySet 'Patient' is based on type 'Patient' that has no keys defined."
The view
Registration
#Html.ValidationSummary(true)
#using (Html.BeginForm())
{
#Html.LabelFor(u => u.Email)<br/>
#Html.TextBoxFor(u => u.Email)<br/>
#Html.LabelFor(u => u.Password)<br>
#Html.TextBoxFor(u => u.Password)<br/>
#Html.LabelFor(u => u.F_Name)<br/>
#Html.TextBoxFor(u => u.F_Name)<br/>
#Html.LabelFor(u => u.L_Name)<br/>
#Html.TextBoxFor(u => u.L_Name)<br/>
#Html.LabelFor(u => u.Address_Line_1)<br/>
#Html.TextBoxFor(u => u.Address_Line_1)<br/>
The Controller
public ActionResult Registration () { return View() }
[HttpPost]
public ActionResult Registration(Patient model)
{
if (ModelState.IsValid)
{
using (var db = new MainDbContext())
{
var encryptedPassword = CustomEncrypt.Encrypt(model.Password); /*encrypting our password CustomLibrary*/
//replaced with below line var Patient = db.Patient.Create();
var patient = new Patient();
patient.Email = model.Email;
patient.Password = encryptedPassword;
patient.F_Name = model.F_Name;
patient.L_Name = model.L_Name;
patient.Address_Line_1 = model.Address_Line_1;
db.Patient.Add(patient); <--- error highlights this line
db.SaveChanges();
}
}
else
{
ModelState.AddModelError("","Error with one or more fields,Please check fields");
}
return View();
}
The error highlights this line
var Patient = db.Patient.Create();
Here is the model
public class Patient
{
[Required]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
//[HiddenInput(DisplayValue =false)]
//public string ReturnUrl { get; set; }
public string F_Name{ get; set; }
public string L_Name { get; set; }
public string Address_Line_1 { get; set; }
}
Here is a link to an image of the table i am trying to save it to in sql server http://i.imgur.com/LgUUwjS.jpg
[Editing - I did not see the picture showing the table]
First of all
You WILL bump into more complex problems right after you solve this one, minutes after :-(
I sugest you read a good book on MVC and EF before going back to coding.
Or try one of the thousands tutorials available on the Net, or even from the start page of Visual Studio.
And... your problem is EntityFramework related, not a MVC one.
The solution
You have to make the key explicit at the model code (I assume it is a Identity one):
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
That's it. Your code will succesfully INSERT one patient.
BUT, it will not work for UPDATING, since your controller does not provide an action expecting and Id...
Make sure your id field is auto increment enabled and just change the line that gives the error, like this:
var patient = new Patient();
give a new instance to your patient object and change your variable name.

Showing specific data based on the currently logged in user

I have a question that deals with the logistics of returning rows of data in a SQL database (Entity Framework) based on the user that is logged in; I have mainly focused on desktop C# applications and while making the switch to ASP.NET MVC 4 I'm having a bit of difficulty when it comes to figuring this out (I've searched around and none of the answers seem to provide exactly what I'm looking for):
I would like to use the authorization built in to ASP.NET (MVC4), and allow users to post data about their websites (site category, url, age, etc.) with a form, and have the form store the data (using Entity Framework) to a database (called PrimaryDomainsDb) that is tied to their Id in the UserProfile table.
When the user clicks a button to show their list of domains, how can I make the application pull their list of domains (relevant rows of data) while ignoring other users rows?
Again, I'm mainly looking for the logistics and concepts (using foreign keys, for example) and psuedocode rather than actually spoonfeeding me a bunch of code.
If anyone has any best practice ideas (i.e. link the UserProfile to the PrimaryDomainDb this way, and use EF to call the rows matching their Id this way to return the rows to the View), it would be much appreciated.
Some sample code:
I currently have my PrimaryDomain code first set up like this (this doesn't have the decorators that specify min/max length, etc.):
public class PrimaryDomain
{
public virtual int Id { get; set; }
public virtual string SiteName { get; set; }
public virtual string SiteURL { get; set; }
public virtual SitePlatforms SitePlatform { get; set; }
public virtual decimal? SiteDA { get; set; }
public virtual decimal? SitePA { get; set; }
public virtual string SiteAge { get; set; }
public virtual DateTime? LastStatusUpdate { get; set; }
public virtual string SiteIP { get; set; }
}
And I have a User class that is different than the one provided by ASP.NET WebSecurity, that looks like this: (also, I know that "password" should not be in string formatting, this is just for initial set-up purposes - and password should probably be removed altogether and handled by WebSecurity, I think).
public class User
{
public virtual int Id { get; set; }
public virtual string Username { get; set; }
public virtual string Password { get; set; }
public virtual string Email { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual string MozAccessID { get; set; }
public virtual string MozKey { get; set; }
public virtual decimal AccuountBalance { get; set; }
public virtual PrivateProxy PrivateProxies { get; set; }
public virtual PrimaryDomain PrimaryDomains { get; set; }
}
When pulling the data for Views I run everything through a repository using direct injection:
public interface IUserDataSource
{
IQueryable<User> Users { get; }
IQueryable<PrimaryDomain> PrimaryDomains { get; }
void Save();
}
This is my UserDb class, which is fed in whenever the code calls for an IUserDataSource (via direct injection):
public class UserDb : DbContext, IUserDataSource
{
public UserDb()
: base("DefaultConnection")
{
}
public DbSet<User> Users { get; set; }
public DbSet<PrimaryDomain> PrimaryDomains { get; set; }
IQueryable<User> IUserDataSource.Users
{
get { return Users; }
}
IQueryable<PrimaryDomain> IUserDataSource.PrimaryDomains
{
get { return PrimaryDomains; }
}
void IUserDataSource.Save()
{
SaveChanges();
}
}
And this is, for example, how I would pass the PrimaryDomains model to the View:
public class NetworkController : Controller
{
//
// GET: /Network/
private IUserDataSource _db;
public NetworkController(IUserDataSource db)
{
_db = db;
}
public ActionResult ListDomains()
{
var allDomains = _db.PrimaryDomains;
return View(allDomains);
}
}
But instead of pulling the entire PrimaryDomains list from the data source, I would like to add a way to reference the currently logged in user id to make the application only show the domains for that specific user, not all domains, and when adding a new domain via the form to reference the User Id and add it into the table as well.
My original question may have caused some confusion as to what I'm trying to achieve; It's my fault for posing the wrong way of going about what I'm trying to do. After much research and learning, I've found that exactly what I'm looking for is a multi-tenant data architecture approach.
This is probably what you are looking for. If I understood you correctly you want to use WebSecurity to login or register users but you want to use entity framework to store some user-specific data. Code below connects WebSecurity tables with your database CodeFirst created using EntityFramework.
You create class below (from tutorial).
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class InitializeSimpleMembershipAttribute : ActionFilterAttribute
{
private static SimpleMembershipInitializer _initializer;
private static object _initializerLock = new object();
private static bool _isInitialized;
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Ensure ASP.NET Simple Membership is initialized only once per app start
LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock);
}
private class SimpleMembershipInitializer
{
public SimpleMembershipInitializer()
{
try
{
if(!WebSecurity.Initialized)
WebSecurity.InitializeDatabaseConnection("ConnectionString", "DbUsers", "UserId", "Email", autoCreateTables: true);
}
catch (Exception ex)
{
throw new InvalidOperationException("The ASP.NET Simple Membership database could not be initialized. For more information, please see http://go.microsoft.com/fwlink/?LinkId=256588", ex);
}
}
}
}
It creates necessary tables for registering and logging your users. The magic is in second, third and fourth parameter. It is respectively table, userId column and userName column from YOUR database that you can create by EntityFramework. WebSecurity uses that table along with other self-generated tables to manage your users and let them register, login and so on.
Then in your code first you simply create table
public class DbUser
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
[MaxLength(40)]
public string Email { get; set; }
[MinLength(3)]
[MaxLength(30)]
[Required]
public string FirstName { get; set; }
[MinLength(3)]
[MaxLength(50)]
[Required]
public string LastName { get; set; }
}
Then you can simply query data from controller. In example below I use UserId stored by WebSecurity membership to retrieve account info from database.
public ActionResult AccountInfo()
{
if (FormsAuthentication.CookiesSupported == true && Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
var userId = WebSecurity.CurrentUserId;
var userInfo = context.Users.FirstOrDefault(x => x.UserId == userId);
userInfo.Password = "";
return View(userInfo);
}
else
{
ModelState.AddModelError("", "Wystąpił bląd autoryzacji, zaloguj się jeszcze raz.");
return RedirectToAction("Login", "Account");
}
}
EDIT:
Regarding your edited question as I understand besides the fact that you need to integrate WebSecurity with EF as above (I also forgot to mention that after creating InitializeSimpleMmebershipAttribute class as above you need to decorate your controller with that attribute) you also have problems with implementing generic repository. If that line is a problem:
var allDomains = _db.PrimaryDomains;
Then i suggest to read this article about implementing generic repository:
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
If you want thing realy simple all you need is just add to your interface method
GetDomainByUserId(int userId)
and just implement that interface like that:
public class UserDb : DbContext, IUserDataSource
{
public UserDb()
: base("DefaultConnection")
{
}
public DbSet<User> Users { get; set; }
public DbSet<PrimaryDomain> PrimaryDomains { get; set; }
IQueryable<User> IUserDataSource.Users
{
get { return Users; }
}
IQueryable<PrimaryDomain> IUserDataSource.PrimaryDomains
{
get { return PrimaryDomains; }
}
IQueryable<PrimaryDomain> GetDomainByUserId(int userId)
{
return PrimaryDomains.Where(x => x.Id == userId).ToQueryable();
}
void IUserDataSource.Save()
{
SaveChanges();
}
}
But this is very bad approach and I strongly recommend reading that article.

Storing search parameters

I'm currently building a website in .NET MVC 4, using Entity Framework to access SQL Server.
The website should have a complex search with multiple choices for the user, create a new search (free search), choose from the last 5 searches (history search), choose from stored search parameters.
What I'm having trouble with is the concept of saving the search parameters/sql string, because it's not sessional/cache based and should be stored somewhere (SQL Server / MongoDB / XML) I'm having the hard time in taking the most optimized path, if it's the SQL way then maybe create an entity that stores the search parameters as entities and afterward converting it into a SQL string for the search, or store it in XML and than serialize it with JSON.
Some fields of the search are not an exact db/entity match and requires summing/converting (like hours that would be calculated into certain time).
I'm more inclined to take out the best of Entity Framework abilities for the cause.
Would like to hear some expert thoughts if possible, Thank you.
Not sure if this is the "most optimized" path, but thought it seemed simple to implement:
//POCO class of item you want to search from database
public class SearchableItem
{
public string Name { get; set; }
public int Age { get; set; }
}
//MVC View Model for search page
public class SearchParamaters
{
public int? MinAge { get; set; }
public int? MaxAge { get; set; }
}
//Storable version for database
public class SavedSearchParameters : SearchParamters
{
public int SavedSearchParametersId { get; set; }
}
//Use SearchParameters from MVC, or SavedSearchParamaters from EF
public IQueryable<SearchableItem> DoSearch(SearchParamaters sp)
{
IQueryable<SearchableItem> query = db.SearchableItems;
if (sp.MinAge.HasValue) query = query.Where(x => x.Age >= sp.MinAge.Value);
if (sp.MaxAge.HasValue) query = query.Where(x => x.Age <= sp.MaxAge.Value);
return query;
}
You could also serialize the SearchParameters class as XML/JSON and save it wherever, then deserialize it and pass it to the DoSearch method as normal, then you wouldn't have to change the DB schema every time you wanted to add search parameters
EDIT: Full example using serialization
\Domain\Person.cs
namespace YourApp.Domain
{
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
}
\Domain\SavedPersonSearch.cs
namespace YourApp.Domain
{
//Entity object with serialized PersonSearchParameters
public class SavedPersonSearch
{
public int Id { get; set; }
public string Name { get; set; }
public string Parameters { get; set; }
}
}
\Models\PersonSearchParameters.cs
namespace YourApp.Models
{
//MVC View Model for search page
public class PersonSearchParameters
{
public int? MinAge { get; set; }
public int? MaxAge { get; set; }
}
}
\Helpers\SearchProvider.cs
using YourApp.Domain;
using YourApp.Models;
namespace YourApp.Helpers
{
public class SearchProvider
{
private YourAppDbContext _context;
public SearchProvider(YourAppDbContext context)
{
//This example uses the DbContext directly
//but you could use a Unit of Work, repository, or whatever
//design pattern you've decided on
_context = context;
}
public IQueryable<Person> SearchPersons(int savedPersonSearchId)
{
var savedSearch = _context.SavedPersonSearches.Find(savedPersonSearchId);
//deserialize (example assumes Newtonsoft.Json)
var searchParams = JsonConvert.Deserialize<PersonSearchParameters>(savedSearch.Parameters);
return SearchPersons(searchParams);
}
public IQueryable<Person> SearchPersons(PersonSearchParameters sp)
{
IQueryable<Person> query = _context.Persons;
if (sp.MinAge.HasValue) query = query.Where(x => x.Age >= sp.MinAge.Value);
if (sp.MaxAge.HasValue) query = query.Where(x => x.Age <= sp.MaxAge.Value);
return query;
}
public void SavePersonSearch(PersonSearchParameters sp, string name)
{
var savedSearch = new SavedPersonSearch { Name = name };
savedSearch.Parameters = JsonConvert.Serialize(sp);
_context.SavedPersonSearches.Add(savedSearch);
_context.SaveChanges();
}
}
}
\Controllers\PersonController.cs
namespace YourApp.Controllers
{
public class PersonsController : Controller
{
private SearchProvider _provider;
private YourAppDbContext _context;
public PersonsController()
{
_context = new YourAppDbContext();
_provider = new SearchProvider(_context);
}
//Manual search using form
// GET: /Persons/Search?minAge=25&maxAge=30
public ActionResult Search(PersonSearchParameters sp)
{
var results = _provider.SearchPersons(sp);
return View("SearchResults", results);
}
//Saved search
// GET: /Persons/SavedSearch/1
public ActionResult SavedSearch(int id)
{
var results = _provider.SearchPersons(id);
return View("SearchResults", results);
}
[HttpPost]
public ActionResult SaveMySearch(PersonSearchParameters sp, name)
{
_provider.SavePersonSearch(sp, name);
//Show success
return View();
}
}
}
conver your parameters to Base64 string. It would help you to create any hard queries for example, http://www.jobs24.co.uk/SearchResults.aspx?query=djAuMXxQUzoxMHx2MC4x&params=cXVlcnlmaWx0ZXI6 decode base64 use this service http://www.opinionatedgeek.com/DotNet/Tools/Base64Decode/default.aspx
you can also take a look on http://aws.amazon.com/cloudsearch/ it may be give you an idea about worh with parameters in your project
Something like this could work:
Store the search parameters in json/xml and persist in DB table.
1. When you want to edit the search parameters (if you even allow this), use the json/xml to pre-fill the selected parameters so user can edit criteria.
2. When user wants to run search, take parameters from json and create/run the query.
OR
Store the search parameters in json/xml and persist in DB table and also create the sql query and store the sql string (after validating parameters)
1. When you want to edit the search parameters (if you even allow this), use the json/xml to pre-fill the selected parameters so user can edit criteria.
2. When user wants to run search, simply take the saved query string and execute it.

How to tell Fluent hibernate what to name a foreign key?

I have something like this
public class AppointmentReminder
{
public virtual int ReminderId { get; private set; }
public virtual CalendarAppointment CalendarAppointment { get; set; }
}
public class CalendarAppointment
{
public virtual int AppointmentId { get; private set; }
public virtual IList<AppointmentReminder> AppointmentReminders { get; set; }
public CalendarAppointment()
{
AppointmentReminders = new List<AppointmentReminder>();
}
}
public class AppointmentReminderMap : ClassMap<AppointmentReminder>
{
public AppointmentReminderMap()
{
Table("AppointmentReminders");
Id(x => x.ReminderId);
References(x => x.CalendarAppointment).ForeignKey("AppointmentId").Column("AppointmentId").Not.Nullable();
}
}
public class CalendarAppointmentMap : ClassMap<CalendarAppointment>
{
public CalendarAppointmentMap()
{
Table("CalendarAppointments");
Id(x => x.AppointmentId);
HasMany(x => x.AppointmentReminders);
}
}
As you can see I try to tell AppointmentReminderMap what the name of the fk is by trying ForiegnKey and Column yet when I get this error
Server Error in '/' Application.
Invalid column name 'CalendarAppointmentId'.
Invalid column name 'CalendarAppointmentId'.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.Data.SqlClient.SqlException: Invalid column name 'CalendarAppointmentId'.
Invalid column name 'CalendarAppointmentId'.
Source Error:
It looking for CalendarAppointmentId. I don't why it repeats it twice. So I let fluent nhibernate generate my database to see what was going on. When I look at the appointmentReminder table it has a fk of CalendarAppointmentId.
Why does it not use the name that I specified?
Here is my config
public ISessionFactory GetSessionFactory()
{
ISessionFactory fluentConfiguration = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("ConnectionString")))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<Framework.Data.Mapping.MyMap>().Conventions.Add(ForeignKey.EndsWith("Id")))
//.ExposeConfiguration(BuidSchema)
.BuildSessionFactory();
return fluentConfiguration;
}
private static void BuidSchema(NHibernate.Cfg.Configuration config)
{
new NHibernate.Tool.hbm2ddl.SchemaExport(config).Create(false, true);
}
Try:
HasMany(x => x.AppointmentReminders).KeyColumn("AppointmentId");
ForeignKey is the name of the fk constraint, not the column. You probably need to make sure the HasMany is using the same column name..."AppointmentId". That convention you're using is making it default to CalendarAppointmentId which conflicts with what you've specified on the one-to-many side. So..another option would be to remove the Column("AppointmentId") on the one-to-many and let the convention do it's thing.

Resources