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