Basic F# / Entity Framework / Generic Functions - entity-framework

I'm a complete beginner when it comes to development, so apologies in advance for my poor grasp of the terminology here...
I'm trying to create a number of very simple unrelated tables in a SQL database just to get my head round things. I'd like to reference these using Entity Framework, and I'd like to minimise repetition where possible. I'm trying to understand whether there is a more elegant way of doing this so that I don't have to re-write the getters and setters each time:
// First open "System; System.Data; System.Data.Entity; System.ComponentModel.DataAnnotations"
// Then define "connectionString"
// Then create two simple classes that have no relation to each other, using CLIMutable
// to help Entity Framework along.
module Model =
[<CLIMutable>] type Customer = {[<Key>]Id: int; FirstName: string; LastName: string;}
[<CLIMutable>] type Item = {[<Key>]Id: int; ItemName: string; ItemDescription: string;}
module Actions =
open Model
type Context() =
inherit DbContext()
[<DefaultValue>] val mutable customers: DbSet<Customer>
member public this.Customers with get() = this.customers
and set v = this.customers <- v
[<DefaultValue>] val mutable items: DbSet<Item>
member public this.Items with get() = this.items
and set v = this.items <- v
// I would like to be able to add several more very simple classes without having to
// repetively type out the getters and setters each time. Ideally I'd like to turn the above
// into a generic function (method? I'm not sure of terminology) that I can call later and
// pass it my desired parameters in terms of table/object names. However, if I move the code
// above the Model and replace DbSet<Customer> with DbSet<'T> this won't work since it won't
// know what type 'T is at this point. I suspect I need a generic function (method?) that
// I can create a new instance of later and replace the generics with actual values (using
// reflection?). Is there any way to do this without making Entity Framework get upset?
let db = new Context()
db.Database.Connection.ConnectionString <- Settings.connectionString
Similarly I'd like to create a generic function that does the Add() and SaveChanges() steps
below, but I'm not sure whether this is possible since I'd need to dynamically replace
'Customers' or 'Items' with a table name that I've passed in - and also the type of 't'
could vary depending on what is calling the function and I don't know whether that's allowed.
let createCustomer id firstName lastName =
let t : Customer = {Id = id;FirstName = firstName; LastName = lastName}
db.Customers.Add(t) |> ignore
db.SaveChanges() |> ignore
let createItem id itemName itemDescription =
let t : Item = {Id = id; ItemName = itemName; ItemDescription = itemDescription}
db.Items.Add(t) |> ignore
db.SaveChanges() |> ignore
open Actions
[<EntryPoint>]
let main argv =
createCustomer 1 "Bob" "Smith"
createItem 1 "First item" "First item test"
Console.ReadLine() |> ignore
0
Thanks for any help, understand my questions are probably very basic so if people can't be bothered to re-write code but are able to point me in the right direction of resources for further reading that'd be great!
[edit] Thanks very much to Fyodor for providing the solution. The key was adding the ": Context" type annotation to the "db" element when referring to it generally. Additionally I had made the silly mistake of not opening the "Model" module in my main argument. Updated and working code it as below:
module Model =
[<CLIMutable>] type Customer = {[<Key>]Id: int; FirstName: string; LastName: string;}
module Actions =
open Model
type Context() =
inherit DbContext()
member val Customers: DbSet<Customer> = null with get, set
let db = new Context()
db.Database.Connection.ConnectionString <- Settings.connectionString
let addAndCommit (db : Context) x =
db.Set().Add x |> ignore
db.SaveChanges() |> ignore
let createCustomer id firstName lastName =
addAndCommit db {Id = id; FirstName = firstName; LastName = lastName}
open Actions
open Model
[<EntryPoint>]
let main argv =
createCustomer 1 "Bob" "Smith"
Console.ReadLine() |> ignore
0

First, to declare a property with a backing field, use member val:
type Context() =
...
member val Customers: DbSet<Customer> = null with get, set
...
Second, to access a DbSet of a specific type, you don't need a property. You can get it via DbContext.Set<'t> method:
let addAndCommit (db: Context) (x: 't) =
db.Set<'t>().Add x |> ignore
db.SaveChanges() |> ignore
let createCustomer id firstName lastName =
addAndCommit db {Id = id; FirstName = firstName; LastName = lastName}
But the explicit 't annotation is not really required: F# can infer it for you.
let addAndCommit (db: Context) x =
db.Set().Add x |> ignore
db.SaveChanges() |> ignore

Related

How to get the maximum length of a string from an EDMX model in code?

I've created an EDMX object from a database I'm programming against.
I need to get input from a user and save it to a row in the database table. The problem is that I need to limit the length of input strings to the width of the corresponding VARCHAR column in the database.
When I browse the model, I can clearly see in the properties window that the model knows the max length of the string, but I don't know how to access this data in code.
If I want to write something like this:
Entities entities = new Entities();
myTable = entities.myTable.First();
if (userInput.length > myTable.columnA.MaxLength)
{
// tell the user that the input is too long.
}
else
{
myTable.columnA = userInput;
}
How do I write it?
Update: I would like to point out that the IObjectContextAdapater mentioned in the answers below is in the System.Data.Entity.Infrastructure namespace.
Here are two methods by which you can read the meta data:
int? GetMaxLength(DbContext context, string tableName, string propertyName)
{
var oc = ((IObjectContextAdapter)context).ObjectContext;
return oc.MetadataWorkspace.GetItems(DataSpace.CSpace).OfType<EntityType>()
.Where(et => et.Name == tableName)
.SelectMany(et => et.Properties.Where(p => p.Name == propertyName))
.Select (p => p.MaxLength)
.FirstOrDefault();
}
int? GetMaxLength<T>(DbContext context, Expression<Func<T, object>> property)
{
var memberExpression = (MemberExpression)property.Body;
string propertyName = memberExpression.Member.Name;
return GetMaxLength(context, typeof(T).Name, propertyName);
}
So you can either enter the table name and property name, or an expression that specifies the property you're interested in.
Another approach could be to create a MetaData class and use the MaxLength attribute.
It's not very pretty; reading edmx properties at runtime is not something Microsoft exposed easily or documented well (or in some cases, at all). context is your DBContext.
var objectContext = ((IObjectContextAdapter)context).ObjectContext;
var entityType = objectContext.MetadataWorkspace.GetItems<EntityType>(DataSpace.CSpace).Where(e => e.Name == "your entity name").First();
var facets = entityType.Properties["your property name"].TypeUsage.Facets;
facets will look something like this, so you'll need to look for the MaxLength Name(may not exist, depending on the underlying field type) and get the Value:
Count = 5
[0]: Nullable=false
[1]: DefaultValue=null
[2]: MaxLength=250
[3]: Unicode=false
[4]: FixedLength=false
If you modify the T4 template you can add your own attribute to the properties that have MaxLength set.
If you can find the right place to add it, it's something as simple as this:
var lengthAttributeText = edmProperty.MaxLength.HasValue
? string.Format("[MaxLength({0})] ", edmProperty.MaxLength.Value)
: "";
And then add this into the text for the property line. (Tricky to be more detailed since I've already modified my .tt file a lot; also the lack of proper IDE support for .tt files makes this a lot harder than it could be.)

Can I use CLIMutable records with EntityFramework in an update scenario?

I am trying to use F# record types with the CLIMutable attribute as entities with Entity Framework. Selecting entities, and adding new entities works fine, but there is a problem with updating entities. Apparently I cannot use the assignment operator (<-) to assign a value to the field of a mutable record type.
Is there any way to make an assignment to the field of an existing record?
[<CLIMutable>]
[<Table("UserProfile")>]
type UserProfile = {
[<Key>]
Id : int
Username : string }
type Context() =
inherit DbContext(connectionString)
[<DefaultValue>]
val mutable private _users : DbSet<UserProfile>
member this.Users
with get() = this._users and set(v) = this._users <- v
let updateUsername id username =
use context = new Context()
let user = context.Users.Find(id : int)
user.Username <- username //This line does not compile
context.SaveChanges()
From the documentation:
type Car = {
Make : string
Model : string
mutable Odometer : int
}
let myCar = { Make = "Fabrikam"; Model = "Coupe"; Odometer = 108112 }
myCar.Odometer <- myCar.Odometer + 21
The CLIMutable attribute is there so that frameworks etc can populate records. If you need to modify the record yourself, you simply need to inform the F# compiler that you want to be able to update that field.

Linq To Entities 5 re-use Expression<TEntity, TResult> in another entity

I've been trying to find this, but I've not had luck. Say I have a database with 2 tables, person and address.
table person
id int
name varchar(50)
addressId int
table address
id int
street varchar(50)
country varchar(50)
In my data layer, I have a business object for Address, which is exposed to external callers. I found an expression that I could use to centralize my creation code at the SQL level. This way I don't have to write:
db.Address.Select( x => new Biz.Address{ street = x.street} ).ToList(); //and all the other properties etc
everywhere. Instead I can now do:
db.Address.Select(AddressDto.ToDto).ToList();
Using this code:
internal static class AddressDto
{
internal static readonly Expression<Func<Address, Biz.Address>> ToDto =
src => new Biz.Address
{
Id = src.id,
Street = src.street,
Country = src.country
};
}
The problem is now that I am trying to do the same thing for the Person object, and I want to re-use this method to fill in the address. However I can't seem to utilize an expression in it.
class Person
{
int Id;
string Name;
Address address;
}
internal static class PersonDto
{
internal static readonly Expression<Func<Person, Biz.Person>> ToDto =
src => new Biz.Person
{
Id = src.id,
Name = src.name,
address = src.Address //How do i set this to an expression?
};
}
The reason I ask for the expression, is because while it compiles fine if I use a normal method, it blows up at runtime, because it can't translate that to the object store. However, if I do:
address = AddressDto.ToDto(src.Address)
the compiler rejects that, as it wants a method, delegate, or event. I'd love to find a way to do this. the idea I'm trying to implement is to basically centralize the code that maps the Entity to the business object, so that my other code is kept clean and maintenance is easier when the schema changes. If there is a different method signature I have to create and maintain, that'd be fine, as I'd place it in the same file and live with it. I just can't seem to find the magic combination that'll make this work.
Thanks!
Update from khan's suggestion.
That just moves this to a linq to objects area. I am trying to do this in the SQL end because of how some queries are built up, we don't want to call .ToList till later.
if I do:
internal static class PersonDto
{
internal static readonly Func<Person, Biz.Person> ToDto =
src => new Biz.Person
{
Id = src.id,
Name = src.name,
address = new Biz.Address { id = src.Address.Id }
};
}
Then that works. i just want to replace the new call.
The compiler doesn't like AddressDto.ToDto because you're passing in an Func<> wrapped in an Expression<> when it is just expecting a Func<>
address = AddressDto.ToDto(src.Address)
Remove the Expression from the signatures and see if it works:
internal static class AddressDto
{
internal static readonly Func<Address, Biz.Address> ToDto =
src => new Biz.Address
{
Id = src.id,
Street = src.street,
Country = src.country
};
}
internal static class PersonDto
{
internal static readonly Func<Person, Biz.Person> ToDto =
src => new Biz.Person
{
Id = src.id,
Name = src.name,
address = AddressDto.ToDto(src.Address)
};
}

Grails transient property not validating error?

I have a domain class:
class Book {
String id
Date dateCreated
Date lastUpdated
String title
boolean acceptPolicy // defines if the user accepts the terms and privacy poicy
String uploadImageId
static transients = [ "acceptPolicy" ]
static constraints = {
acceptPolicy( validator: { val, obj -> val == true} )
}
}
I choose "acceptPolicy" as a field which should not be stored in the database. When I create a book object everything works find. The problem occurs when I update the book instance. Every time I try to save I get a validation error for the "acceptPolicy". This should not be the case because "acceptPolicy" is not stored in the database. I also tried to set the
acceptPolicy = true
in a hidden input field but still this problem occurs. How can I fix it?
Change the validator and make it behave so that it only validates the acceptPolicy field when the entity is not yet persistent:
acceptPolicy( validator: { val, obj -> obj.id || val } )

How can I cast to subclass when using LINQ and TPT EF?

I have the following class hierarchy:
public class RealPeople { }
public class Users : RealPeople { }
public class People : RealPeople { }
In my dbContext, I defined a dbSet for RealPeople and on the OnModelCreating procedure, I specified separated tables for People and Users:
modelBuilder.Entity<Users>().ToTable("Users");
modelBuilder.Entity<People>().ToTable("People");
This creates the corresponding full hierarchy in my DB, with the 3 corresponding tables.
The problem comes when I want to retrieve the list of Users in my DB.
This:
List = (from Reg in PersistentMgr.RealPeople select (Users)Reg)
.ToList();
or this:
List = (from Reg in PersistentMgr.RealPeople select (Users)((RealPeople)Reg))
.ToList();
Throws an exception:
LINQ only being able to cast primitive model types.
So the thing is, I can't cast RealPeople to the corresponding subclass Users.
Any ideas on this one?
The way to get a collection of subclasses is using OfType:
var users = (from p in PersistentMgr.RealPeople select p).OfType<User>();
Try this instead:
var list = PersistentMgr.RealPeople.Select(reg => reg as Users).ToList();
better:
var list = PersistentMgr.RealPeople.Select(reg => (reg is Users) ? reg as Users : null).ToList();
You will get the same error if you try this:
var realperson = new RealPeople();
var user = (Users) realperson;
The reason is because the compiler doesn't know how to convert complex types into their subtypes by simple casting - so you need to use the as keyword instead. This will either return null, or the supertype casted into the subtype.
var realperson = new RealPeople();
var user = realperson as Users; // user is realperson converted into a Users object
var aString = "this is a string";
var otheruser = aString as Users; // otheruser is null, because aString was not a valid supertype for Users