这个问题是非常技术性的,它深深地介于F#/ C#之间。我很可能错过了一些东西。如果您发现概念错误,请发表评论,我将更新问题。
让我们从C#世界开始。假设我有一个简单的业务对象,将其称为Person
(但是请记住,有100多个对象远比我们使用的业务领域中的对象复杂):
public class Person : IPerson
{
public int PersonId { get; set; }
public string Name { get; set; }
public string LastName { get; set; }
}
,并且我使用DI / IOC,所以我实际上从未传递过Person
。相反,我将始终使用接口(如上所述),将其称为IPerson
:
public interface IPerson
{
int PersonId { get; set; }
string Name { get; set; }
string LastName { get; set; }
}
业务需求是可以将该人序列化到数据库或从数据库反序列化。假设我选择为此使用Entity Framework,但实际的实现似乎与问题无关。在这一点上,我可以选择引入与“数据库”相关的类,例如EFPerson
:
public class EFPerson : IPerson
{
public int PersonId { get; set; }
public string Name { get; set; }
public string LastName { get; set; }
}
连同相关的数据库相关属性和代码,为了简洁起见,我将跳过这些,然后使用Reflection在IPerson
和Person
之间复制EFPerson
接口的属性,或者仅使用{ {1}}(作为EFPerson
传递)直接执行其他操作。这是完全无关紧要的,因为消费者将始终看到IPerson
,因此可以在不了解消费者任何情况的情况下随时更改实现。
如果我需要添加属性,那么我将首先更新接口IPerson
(假设我添加了属性IPerson
),然后编译器会告诉我要解决的问题。但是,如果我从界面中删除了该属性(假设我不再需要DateTime DateOfBirth { get; set; }
),那么编译器将无济于事。但是,我可以编写一个基于反射的测试,该测试将确保LastName
,IPerson
,Person
等的属性相同。确实不是必需的,但是可以做到这一点,然后它将像魔术一样工作(是的,我们确实有这样的测试,它们确实像魔术一样工作。)
现在,让我们进入F#世界。这里我们有类型提供程序,它完全不需要在代码中创建数据库对象:它们是由类型提供程序自动创建的!
酷!但是吗?
首先,必须有人创建/更新数据库对象,如果涉及的开发人员不止一个,那么很自然地可以并且将在不同分支中升级/降级数据库。到目前为止,根据我的经验,当涉及到F#类型提供程序时,这是一个极大的痛苦。即使使用C#EF Code First来处理迁移,也需要一些“大量的萨满舞”才能使F#类型的提供者“高兴”。
第二,默认情况下,F#世界中的所有内容都是不可变的(除非我们使其可变),因此我们显然不希望将可变数据库对象传递给上游。这意味着一旦我们从数据库加载了可变行,就希望尽快将其转换为“本地” F#不可变结构,以便仅在上游使用纯函数。毕竟,根据域的不同,使用纯函数会减少5至50倍所需的测试次数。
让我们回到我们的EFPerson
。我现在将跳过任何可能的重新映射(例如,将数据库整数转换为F#DU大小写和类似内容)。因此,我们的F#Person
如下所示:
Person
因此,如果“明天”我需要在此类型上添加type Person =
{
personId : int
name : string
lastName : string
}
,则编译器将告诉我所有需要修复此问题的地方。这很棒,因为C#编译器不会告诉我除数据库之外我需要在哪里添加该出生日期。 F#编译器不会告诉我需要去往表dateOfBirth : DateTime
中添加数据库列。但是,在C#中,由于我必须先更新接口,因此编译器会告诉我必须修复哪些对象,包括数据库一个。
显然,我想要F#中的两全其美。尽管可以使用接口来实现这一点,但是感觉不到F#的方式。毕竟,DI / IOC的模拟在F#中的实现方式非常不同,通常是通过传递函数而不是接口来实现的。
所以,这是两个问题。
如何在F#世界中轻松管理数据库的上/下迁移?从头开始,当涉及到许多开发人员时,在F#世界中实际进行数据库迁移的正确方法是什么?
如上所述,实现“最佳C#世界”的F#方法是什么:当我更新F#时,键入Person
,然后修复所有需要在记录中添加/删除属性的地方,在我没有更新数据库以匹配业务对象时,在编译时或至少在测试时“失败”的最合适的F#方法是什么?
答案 0 :(得分:3)
如何在F#世界中轻松管理数据库上/下迁移? 并且,首先,实际做数据库的正确方法是什么 涉及许多开发人员时在F#世界中迁移?
管理Db迁移的最自然的方法是使用数据库固有的工具,即普通SQL。在我们的团队中,我们使用dbup软件包,对于每个解决方案,我们都会创建一个小型控制台项目,以在开发和部署期间汇总数据库迁移。消费者应用程序同时在F#(类型提供程序)和C#(EF)中,有时具有相同的数据库。奇迹般有效。
您首先提到了EF代码。 F#SQL提供程序本质上都是“ Db First”,因为它们基于外部数据源(数据库)生成类型,而不是基于其他方式。我认为混合两种方法不是一个好主意。实际上,我不会向任何人推荐EF Code First来管理迁移:纯SQL更简单,不需要“广泛的萨满舞”,它无限灵活并且为更多的人所了解。 如果您对手动SQL脚本不满意,并考虑将EF Code First仅仅用于自动生成迁移脚本,那么甚至可以使用MS SQL Server Management Studio designer can generate migration scripts for you
实现上述“最佳C#世界”的F#方法是什么 上图:当我更新F#时,键入Person,然后修复我所有的位置 需要在记录中添加/删除属性,最多的是 在编译时或至少在编译时“失败”的适当F#方式 我没有更新数据库以匹配业务时的测试时间 个对象?
我的食谱如下:
界面,只是感觉不到F#方式
简而言之
更短
坚持单一责任原则,享受好处。