Fluent NHibernate提供了一个方法让你不再需要去写NHibernate的标准映射文件(.hbm.xml),而是可以把你的映射文件都使用C#来写。这样做,方便了我们的代码重构,提供了代码的易读性,并精简了项目代码。
开发环境 VS 2019。
让我们开始第一个 Fluent NHibernate 程序。
实体的类如下:
Employee:
public class Employee
{
public virtual int Id { get; protected set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual Store Store { get; set; }
}
类中的属性需要定义为 virtual , Id 是自动递增的。
Location:
public class Location
{
public virtual int Aisle { get; set; }
public virtual int Shelf { get; set; }
}
Product:
public class Product
{
public virtual int Id { get; protected set; }
public virtual string Name { get; set; }
public virtual double Price { get; set; }
public virtual Location Location { get; set; }
public virtual IList<Store> StoresStockedIn { get; set; }
public Product()
{
StoresStockedIn = new List<Store>();
}
}
Store:
public class Store
{
public virtual int Id { get; protected set; }
public virtual string Name { get; set; }
public virtual IList<Product> Products { get; set; }
public virtual IList<Employee> Staff { get; set; }
public Store()
{
Products = new List<Product>();
Staff = new List<Employee>();
}
public virtual void AddProduct(Product product)
{
product.StoresStockedIn.Add(this); // 多对多
Products.Add(product);
}
public virtual void AddEmployee(Employee employee)
{
employee.Store = this; // 一对多
Staff.Add(employee);
}
}
每个实体有一个对应的Mapping文件。
Employee的mapping如下:
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Id(x => x.Id);
Map(x => x.FirstName);
Map(x => x.LastName);
References(x => x.Store);
}
}
很明显这里的Map方法相当于XML配置文件的Property, 而Reference相当于Many-To-One。
Location的Mapping如下:
public class LocationMap : ComponentMap<Location>
{
public LocationMap()
{
Map(x => x.Aisle);
Map(x => x.Shelf);
}
}
Product的Mapping如下:
public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Id(x => x.Id);
Map(x => x.Name);
Map(x => x.Price);
HasManyToMany(x => x.StoresStockedIn)
.Cascade.All()
.Inverse()
.Table("StoreProduct");
Component(x => x.Location);
}
}
这里的HasManyToMany相当于NHibernate中的Many-To-Many。在映射的两端同时添加HasManyToMany的关系这样就形成了双向的关联关系。这里用Inverse(),下面不要用。关联关系的两方都要指定相同的 Table()。
Store 的 Mapping 如下:
public class StoreMap : ClassMap<Store>
{
public StoreMap()
{
Id(x => x.Id);
Map(x => x.Name);
HasManyToMany(x => x.Products)
.Cascade.All()
.Table("StoreProduct");
HasMany(x => x.Staff)
.Cascade.All()
.Inverse();
}
}
这里的HasMany相当于NHibernate中Many-To-One。
下一步是创建数据库和SessionFactory:
SessionFacotry:
private static ISessionFactory CreateSessionFactory()
{
var fluentlyConfigure = Fluently.Configure()
.Database(SQLiteConfiguration.Standard
.UsingFile(DbFile))
.Mappings(m => {
m.FluentMappings.AddFromAssemblyOf<Program>();
});
fluentlyConfigure = fluentlyConfigure.ExposeConfiguration(BuildSchema);
return fluentlyConfigure.BuildSessionFactory();
}
运行程序,数据库中表会自动创建,且数据添加成功。
最后增加一个 First 布尔标识,检查是否存在现有的数据文件, 如果存在就使用现有的,不重新创建及添加数据。
全部工作完成。
这个例子是上个例子的 AutoMapped 版本。主要不同点在于,不用自己创建 Mapping,使用 AutoMapping 自动创建 Examples.FirstAutomappedProject.Entities 名字空间下的实例的Mapping , 其余部分与 上个例子类似。
重要的区别如下:
class ExampleAutomappingConfiguration : DefaultAutomappingConfiguration
{
public override bool ShouldMap(Type type)
{
return type.Namespace.EndsWith(".Entities");
}
public override bool IsComponent(Type type)
{
return type == typeof(Location); // Location 对应 ComponentMap ,其他对应 ClassMap
}
}
注意思考 Location 和 其他实体有什么不同的地方。
class CascadeConvention : IReferenceConvention, IHasManyConvention, IHasManyToManyConvention
{
public void Apply(IManyToOneInstance instance)
{
// FluentNHibernate在映射时有很多种映射方法。
// Cascade它是指该对象在进行操作时关联到的子对象的操作类型,指定All说明所有的操作都会关联到子表
instance.Cascade.All();
}
public void Apply(IOneToManyCollectionInstance instance)
{
instance.Cascade.All();
}
public void Apply(IManyToManyCollectionInstance instance)
{
instance.Cascade.All();
}
}
多对一,一对多,多对多 均在这个类中级联。
以上完整的例子 在 这里。
全部工作完成。
参考资料 :
基本映射详解: https://blog.csdn.net/zhang_xinxiu/article/details/42131907
AutoMapping详解: https://blog.csdn.net/zhang_xinxiu/article/details/42248565