#entity-framework #f# #ef-code-first
#entity-framework #f# #ef-code-first
Вопрос:
Каков наилучший способ сделать библиотеку c # более функциональной, если у вас нет доступа к исходному коду? С моей точки зрения, кажется, что у вас есть два варианта: методы расширения или функции, обернутые в модули
… или есть более аккуратный и менее «хакерский» способ выполнить такую задачу?
Примером может быть EF Code First.
Прежде чем я начал создавать функции, упакованные в модули:
override x.OnModelCreating(modelBuilder:DbModelBuilder) =
// ----------- FileUpload Configuration ----------- //
// General
modelBuilder.Entity<FileUpload>()
.ToTable("Some")
|> ignore
// Key
modelBuilder.Entity<FileUpload>()
.HasKey(ToLinq(<@ fun z -> z.ID @>))
|> ignore
// Properties
modelBuilder.Entity<FileUpload>()
.Property(ToLinq(<@ fun z -> z.Path @>))
|> ignore
modelBuilder.Entity<FileUpload>()
.Property(ToLinq(<@ fun z -> z.Extension @>))
|> ignore
и после:
override x.OnModelCreating(modelBuilder:DbModelBuilder) =
let finished = ignore
// ----------- FileUpload Configuration ----------- //
let entity = modelBuilder.Entity<FileUpload>()
// General
entity
|> ETC.toTable "Some"
// Key
entity
|> ETC.hasKey(fun z -> z.ID)
|> finished
// Properties
entity
|> ETC.property(fun z -> z.Path)
|> finished
entity
|> ETC.property(fun z -> z.Extension)
|> finished
Модуль, используемый в последнем примере:
module ETC =
let property (expr:'a -> string) (cfg:EntityTypeConfiguration<'a>) =
cfg.Property(ToLinq(<@ expr @>))
let hasKey (expr:'a -> 'b) (cfg:EntityTypeConfiguration<'a>) =
cfg.HasKey(% expr)
let hasForeignKey (expr: 'a -> 'b) (cfg:DependentNavigationPropertyConfiguration<'a>) =
cfg.HasForeignKey(% expr)
let hasMany (expr: 'a -> ICollection<'b>) (cfg:EntityTypeConfiguration<'a>) =
cfg.HasMany(% expr)
let hasRequired (expr: 'a -> 'b) (cfg:EntityTypeConfiguration<'a>) = cfg.HasRequired(ToLinq(<@ expr @>))
let withRequired (expr: 'a -> 'a) (cfg:ManyNavigationPropertyConfiguration<'a,'a>) =
cfg.WithRequired(% expr)
let willCascadeOnDelete (cfg:CascadableNavigationPropertyConfiguration) =
cfg.WillCascadeOnDelete()
let isMaxLength(cfg:StringPropertyConfiguration) =
cfg.IsMaxLength()
let toTable (s:string) (cfg:EntityTypeConfiguration<'a>) =
cfg.ToTable s
Возможно, это не выглядит заметным улучшением, поскольку я не составил полный пример (ленивый я) — Но, как вы видите, последний намного более «функционален» и выглядит более чистым, чем первый.. Но мой вопрос в том, является ли плохой практикой просто переносить методы в функции и переносить эти функции в модули, чтобы обеспечить более функциональный способ использования этих библиотек c #?
Комментарии:
1. Почему бы не написать это на F #, предоставить его программам C # и не беспокоиться об этом.
Ответ №1:
Прежде всего, я не думаю, что ваша оболочка будет работать — в вашей ETC.property
вы не можете принять обычную функцию в качестве аргумента, а затем заключить ее в кавычки внутри ETC.property
. Вам нужно передать лямбда-функцию как уже заключенную в кавычки (и тип аргумента должен быть Expr<'a -> string>
:
entity |> ETC.property <@ fun z -> z.Path @>
|> finished
К вашему первоначальному вопросу — я думаю, что использование модуля с функциями-оболочками является хорошим вариантом. Вы могли бы немного улучшить синтаксис, передав представление сущности через конвейер, который задает свойства. С соответствующими определениями вы могли бы получить что-то вроде:
EF.entity<FileUpload> modelBuilder
|> EF.hasKey <@ fun z -> z.ID @>
|> EF.property <@ fun z -> z.Path @>
|> EF.property <@ fun z -> z.Extension @>
|> EF.toTable "Some"
Идея в том, что у вас должен быть какой-то тип, EntityInfo<'T>
который обертывает то, что вы получаете от вызова modelBuilder.Entity<'T>()
. Тогда функции будут иметь такие типы, как:
EF.hasKey : Expr<'a -> 'b> -> EntityInfo<'T> -> EntityInfo<'T>
EF.toTable : string -> EntityInfo<'T> -> unit
Я намеренно использовал unit
как результат toTable
, потому что это, вероятно, то, что всегда нужно вызывать в любом случае (чтобы мы могли перенести это в конец и избежать явного ignore
). Другие функции просто указывают свойства, а затем возвращают исходный EntityInfo<'T>
объект.
Вы могли бы сделать ее еще более навороченной и записать всю спецификацию в виде цитаты. Например:
modelBuilder |> EF.entity<FileUpload> <@ fun z ->
EF.hasKey z.ID
EF.property z.Path
EF.property z.Extension
EF.toTable "Some" @>
Это не исполняемый код — функции были бы просто фиктивными функциями. Хитрость в том, что вы могли бы обработать цитату и вызвать методы EF на основе этого. Однако это сложнее — вам пришлось бы выполнить довольно много обработки цитат, и это не слишком приятно.