Closed humhei closed 6 years ago
Serializing arbitrary functions into Bson (or any persistence mechanism) doesn’t really make sense, what do you expect the library to do?
Currently i use it as property expression
to support dynamic ORM in fsx
I need to transform fsharp type bson datas to csv
so it can be invoked by other UI program
Seq.csvWithProp (Expr.prop (fun (it:Item) ->
it.Brand,it.Art,it.Size,it.Barcode,it.Image,it.Material)) items
items is retrived from litedb so i think
Expr.prop (fun (it:Item) ->
it.Brand,it.Art,it.Size,it.Barcode,it.Image,it.Material
should also be retrived from litedb
For me there are two solutions
it.Brand,it.Art,it.Size,it.Barcode,it.Image,it.Material
as a text and then create a property expression from this textI think i will take some time to see if the first solution is feasible or not
Another simpler solution is to persist the fields as a list of strings such as [“Brand”; “Art”] and then:
dynamicallyRead item [“Brand”; “Size”] => [“Play ...”; “35” ]
Where dynamicallyRead uses reflection on the fields of items to retrieve their values
Nested property values can have a path i.e. [“NestedField.PropName”]
A general solution to persisting arbitrary logic in functions is not a good fit for this particular problem, there is always a simpler solution
Thanks for answer 😀
I still encounter this problem 😓 follow is my AST
[<CLIMutable>]
type Item =
{
Id: int
Template: string option
Art: string
Brand: string option
Color: ColorEnum option
Material: Material option
Image: string option
Barcode: string option
Sex: Sex option
Fraction: int option
Code: int option
SizeRange: string option
EUR: float
US: float option
UK: float option
CM: float option
Price: string option
Number: int
}
[<CLIMutable>]
type Order =
{
Id: int
Name: string
Items: Item list
Date: DateTime
}
[<CLIMutable>]
type Product =
{
Id: int
Name: string
Kind: ProductKind
Orders: Order list
UnitPrice: float
Paper: Paper option
}
Now i want to add a Filter
to Product
to conditionally determine whether this product take effect
The logic of product taking effect is arbitrary ,Because customs 's logic is always not the some :)
For example
Art
contains "CROSS DOCK" doesn't has this productBrand
named with "PLAY URBAN" should use different product but with "LIP LOVER" must use this product
[<CLIMutable>]
type Product =
{
Filter: Item -> bool
Id: int
Name: string
Kind: ProductKind
Orders: Order list
UnitPrice: float
Paper: Paper option
}
QA: How about to alter this AST to make it more logic? I think this is logic enough .And alter it will generate more complex problems
What i want to do ? Take a chance to introduce FsPickler to this repository as a option to resolve complex serializing and deserializing problems
Please tell me what you think @Zaid-Ajaj
Your business logic should be in your code, not in your storage system. The only reason you would store arbitrary filters inside the database, is to be able to update them by hand (without the application) which I don't think you actually want.
You would put this kind of logic in place where you are reading the values from the database in a where
query:
// order that contains any item with "CROSS DOCK" is invalid
let orderHasInvalidItems (order : Order) =
Seq.forall (fun item -> item.Art.Contains("CROSS DOCK")) order.Items)
// get all products of which the orders are valid (their items do not contain "CROSS DOCK")
let getActiveProducts (db: ILiteDatabase) =
let products = db.GetCollection<Product> "products"
products.where
<@ fun prod -> prod.Orders @>
(fun orders -> Seq.forall (fun order -> not (orderHasInvalidItems order) orders)
otherwise if you really needed to store F# filters, I suggest you use a simple embedded DSL that you can serialize to the database (then you will be doing what LiteDB is doing for you now):
type FilterExpr =
| NoFilter
| Equals of propName:string * value:obj
| LessThan of propName:string * value:float
| GreaterThan of propName:string * value:float
| Contains of propName:string * value:string
| And of left:FilterExpr * right:FilterExpr
| Or of left:FilterExpr * right:FilterExpr
| Not of expr:FilterExpr
| NestedFilter of propName:string * filter:FilterExpr
You will also need to evaulate the expr yourself against dynamic record values from the database
I am assuming you solved your issue, if it happened to be the case that you still need to serialize or deserialize functions, then just use a byte[]
as the data format to store the function with FsPickler for converting between the bytes and functions. This way you don't need to integrate FsPickler with this library internally
then just use a byte[] as the data format to store the function with FsPickler
I like this way 👍 ,It's flexible.
I may finally directly write function in .fsx
file (do everything in it,assum it a middleware to database and my ui program)
Thanks for your answer again! 😄
@humhei No problem, happy to help ;)
Also find a alternative way want to share you @Zaid-Ajaj Using excel UDF by Excel DNA https://github.com/Excel-DNA to store functions Moreever, With Dynamic array formulas is really cool
Today i tried to do make a model something like this
When i deserialized it from database, below exception was thrown