I am trying my hand at NHibernate's built-in mapping by code. I've got it mostly working now. My problem is how do I configure which properties on my objects are required in the database within the convention? I'm guessing this would be some sort of attribute markup?
I know if I do the mappings by hand, I can configure them as required, but how do I do this with conventions?
Simply use nullable types, for example in the class,
public class Foo
{
public int Bar { get; set; }
public int? baz { get; set; }
}
Bar will not be nullable, while Baz will be nullable in the db when use mapping by code.
Related
As you know, C# 9.0 (.Net 5) now allows Covariant Returns. I need help applying this to a set of classes having Auto-Implemented properties.
I have two abstract classes that represent financial bank accounts and transactions. I made them abstract since I will pull data from various data sources and while the main properties will be common across all sources, each source may have additional fields I want to keep. A 1 to Many relationship exists between both classes (1 account has many transactions AND 1 transaction belongs to only 1 account).
public abstract class BankAccount
{
public string Name { get; set; }
public IList<Transaction> Transactions { get; set; } = new List<Transaction>();
...
}
public abstract class Transaction
{
public string Name { get; set; }
public virtual BankAccount BankAccount { get; set; } // This doesn't work unless I remove set;
...
}
And here is an example of the concrete implementations
public class PlaidBankAccount : BankAccount
{
public string PlaidId { get; set; }
...
}
public class PlaidTransaction : Transaction
{
public string PlaidId { get; set; }
public override PlaidBankAccount BankAccount { get; set; } // This doesn't work unless I remove set;
...
}
What I want to do is to override the base class getters and setters so that they use derived classes. For example:
If I create an instance of the concrete transaction and call the BankAccount getter, I want to get an instance of the derived PlaidBankAccount not the base BankAccount.
What I've found is that when I only define virtual getter in the base class and override it in the derived class, it works. But just as I add both properties {get;set;}, I get the same error as in previous C# versions:
error CS1715: 'PlaidTransaction.BankAccount': type must be 'BankAccount' to match overridden member 'Transaction.BankAccount'
How could I fix this?
In C# 9 properties are only able to have co-variant returns when they are readonly, so unfortunately, no set; is possible.
An overriding property declaration must specify exactly the same access modifier, type, and name as the inherited property. Beginning with C# 9.0, read-only overriding properties support covariant return types. The overridden property must be virtual, abstract, or override.
From the Microsoft Docs - Override keyword
I have an entity like this. Let's say that name is the only :
public class MyEntity
{
public Guid Id { get; set; } // immutable
public string Name { get; set; } // can be changed
// These are not exposed on the domain layer. They're just bookkeeping fields.
public string CreatedBy { get; set; }
public DateTime DateCreated { get; set; }
public string UpdatedBy { get; set; }
public DateTime DateUpdated { get; set; }
}
If I populate the 2 "updated" fields before calling SaveChanges(), my entity will be erroneously marked changed if Name hasn't changed. Therefore, I need an event on the DbContext to hook into in order to populate those 2 fields just before committing the unit of work, but only on entities that have actually changed.
Does such an event exist? Can nHibernate do this?
In ObjectContext API you have event SavingChanges directly on ObjectContext. You can handle this event and use ObjectStateManager to find all modified / added entities and deal with them as you need.
In both ObjectContext API and DbContext API you can override SaveChanges method directly and do the same.
Fluent NHibernate is just NHibernate's extension for fluent mapping in code. NHibernate has much better support for such scenarios - it offers custom listeners to deal with this.
I've seen this (unanswered) question asked once before, but in a different context. I'm looking to have two domain objects map to the same table, WITHOUT a discriminator. The two classes are:
public class Category
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual ReadOnlyCategory ParentCategory { get; private set; }
}
and
public class ReadOnlyCategory
{
public virtual int Id { get; private set; }
public virtual string Name { get; private set; }
public virtual ReadOnlyCategory ParentCategory { get; private set; }
}
The main difference is that all public properties of ReadOnlyCategory are read-only. My idea here is that I want all users of this class to know that they should only mess with the category they are currently 'looking' at, and not any other categories in the hierarchy. (I've left off other properties regarding the subcategories.)
Clearly, in the database, Category and ReadOnlyCategory are the same thing, and NHibernate should treat them very similarly when persisting them. There are three problems wrapped into one here:
1) How do I do the mapping?
2) When instantiating the objects, how do I control whether I instantiate Category or ReadOnlyCategory?
3) When persisting the objects, will the mapping be smart enough, or do I need to use an extensibility point here?
Any pointers on how I can get this to happen?
(Or am I crazy?)
This looks like wrong object model design to me. I don't see a good reason to introduce a new class just for authorisation reasons (whether user allowed to modify a given category object?). You may as well use one class and throw for example InvalidOperationException if an end user is not supposed to modify a category.
Does NHibernate always generate update for all columns?
public class Person
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string Address { get; set; }
}
Person p = Session.Load(1);
p.Name = "New Name";
Session.Flush();//Update for all columns, but I change only Name
Is it normal behavior for NHibernate or my mistake? I use Fluent NHibernate and AutoMapping.
That is the default behavior, but you can make NH update modified columns only by adding dynamic-update="true" to your <class> mapping.
NHibernate always updates all mapped columns. This should be no trouble if the other columns didn't change, since on update the data has been previously pumped from the underlying datastore, so basically, it only reset the column values to their own orginal values. No problem about it.
If you want to override this behaviour, you need to implement the IInterceptor interface.
Which entity FluentNHibernate uses as entity
I create some entity in Domain(or BLL), such as the following:
public class Role
{
public long ID { get; protected set; }
public string Name { get; set; }
public string Description { get; set; }
public List<User> Users { get; set; }
public Role()
{
Users = new List<User>();
}
}
And I want make use of FlunetNHibernate to map them, but get errors:
The following types may not be used as proxies:
Freeflying.Domain.Core.Profile: method get_ID should be 'public/protected virtual' or 'protected internal virtual'
Yes, I recall the programmer requirement when use FluentNHibernate, the entity should be like this:
public class Role
{
public virtual long ID { get; protected set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
}
But It looks wired. Do you think so? How do you do when using FluentNHibernate? I don't want go back to Domain layer and add virtual for every property.
This is a basic requirement for using NHibernate; It allows NHibernate to generate a proxy class that descends from your class for lazy loading and such.
I have not seen a method of removing this requirement, though if such a thing is possible it would mean that you could not use lazy loading of objects and/or properties.
Here's a blog post that explains this a bit more; It also offers a way to avoid marking your properties as virtual, although I would really recommend that you do not use this method (marking classes to avoid lazy loading) as the benefits of lazy loading in most circumstances far outweigh the cost of making your properties virtual.