Exposing enum typed properties for entity framework entities fields in ADO.NET Data Service client context - entity-framework

I have an entity with fields that are typed int
and i want to exposed those fields as properties which get and recieve enum type values for working strongly typed.
so i've created a new partial class for the entity and added the two properties.
when i try to create a new instance of the TestEntity and add it to the context ,
and call save changes i get the following exception:
An error occurred while processing this request.
at System.Data.Services.Client.DataServiceContext.SaveAsyncResult.HandleBatchResponse()
at System.Data.Services.Client.DataServiceContext.SaveAsyncResult.EndRequest()
at System.Data.Services.Client.DataServiceContext.SaveChanges(SaveChangesOptions options)
at System.Data.Services.Client.DataServiceContext.SaveChanges()
the inner exception is:
System.InvalidOperationException: The type 'enum1' has no settable properties.
at System.Data.Services.Client.ClientType..ctor(Type type, String typeName, Boolean skipSettableCheck)
at System.Data.Services.Client.ClientType.Create(Type type, Boolean expectModelType)
at System.Data.Services.Client.DataServiceContext.WriteContentProperties(XmlWriter writer, ClientType type, Object resource)
at System.Data.Services.Client.DataServiceContext.CreateRequestData(ResourceBox box, Boolean newline)
at System.Data.Services.Client.DataServiceContext.SaveAsyncResult.CreateChangeData(Int32 index, Boolean newline)
at System.Data.Services.Client.DataServiceContext.SaveAsyncResult.BeginNextChange(Boolean replaceOnUpdate)
so i figured it tries to reflect the enum properties as classes properties.
how can i make the context ignore those properties when it tries to reflect on them.
i am using VS 2008 team suite sp1 , SQL Server 2008 , .Net 3.5 Sp1.
Help.
the partial class code:
public partial class TestEntity
{
public enum1 Field1
{
get
{
return (enum1)field1;
}
set
{
field1 = (Int16)value;
}
}
public enum2 Field2
{
get
{
return (enum2)field2;
}
set
{
field2 = (Int16)value;
}
}
}

I don't think that you can. In ADO.Net Data Services, you can't have enums on your proxy objects that get sent up to the server. Try changing your object around to use an int (or short) instead.

Related

f#: Only parameterless constructors and initializers are supported in LINQ to Entities

I am trying to get Envelope's back from a query. Envelope is defined as follows.
[<CLIMutable>]
type Envelope<'T> = {
Id : Guid
StreamId: Guid
Created : DateTimeOffset
Item : 'T }
MyLibAAS.DataStore.MyLibAASDbContext is a EF DbContext written in c#. When I extend it in f# as follows, I get the error: Only parameterless constructors and initializers are supported in LINQ to Entities.
type MyLibAAS.DataStore.MyLibAASDbContext with
member this.GetEvents streamId =
query {
for event in this.Events do
where (event.StreamId = streamId)
select {
Id = event.Id;
StreamId = streamId;
Created = event.Timestamp;
Item = (JsonConvert.DeserializeObject<QuestionnaireEvent> event.Payload)
}
}
If I return the event and map it to Envelope after the fact, it works fine.
type MyLibAAS.DataStore.MyLibAASDbContext with
member this.GetEvents streamId =
query {
for event in this.Events do
where (event.StreamId = streamId)
select event
} |> Seq.map (fun event ->
{
Id = event.Id
StreamId = streamId
Created = event.Timestamp
Item = (JsonConvert.DeserializeObject<QuestionnaireEvent> event.Payload)
}
)
Why does this make a difference? The Envelope type is not even a EF type.
How F# records work
F# records get compiled into .NET classes with read-only properties and a constructor that takes values of all fields as parameters (plus a few interfaces).
For example, your record would be expressed in C# roughly as follows:
public class Envelope<T> : IComparable<Envelope<T>>, IEquatable<Envelope<T>>, ...
{
public Guid Id { get; private set; }
public Guid StreamId { get; private set; }
public DateTimeOffset Created { get; private set; }
public T Item { get; private set; }
public Envelope( Guid id, Guid streamId, DateTimeOffset created, T item ) {
this.Id = id;
this.StreamId = streamId;
this.Created = created;
this.Item = item;
}
// Plus implementations of IComparable, IEquatable, etc.
}
When you want to create an F# record, the F# compiler emits a call to this constructor, supplying values for all fields.
For example, the select part of your query would look in C# like this:
select new Envelope<QuestionnaireEvent>(
event.Id, streamId, event.Timestamp,
JsonConvert.DeserializeObject<QuestionnaireEvent>(event.Payload) )
Entity Framework limitations
It so happens that Entity Framework does not allow calling non-default constructors in queries. There is a good reason: if it did allow it, you could, in principle, construct a query like this:
from e in ...
let env = new Envelope<E>( e.Id, ... )
where env.Id > 0
select env
Entity Framework wouldn't know how to compile this query, because it doesn't know that the value of e.Id passed to the constructor becomes the value of the property env.Id. This is always true for F# records, but not for other .NET classes.
Entity Framework could, in principle, recognize that Envelope is an F# record and apply the knowledge of the connection between constructor arguments and record properties. But it doesn't. Unfortunately, the designers of Entity Framework did not think of F# as a valid use case.
(fun fact: C# anonymous types work the same way, and EF does make an exception for them)
How to fix this
In order to make this work, you need to declare Envelope as a type with default constructor. The only way to do this is to make it a class, not a record:
type Envelope<'T>() =
member val Id : Guid = Guid.Empty with get, set
member val StreamId : Guid = Guid.Empty with get, set
member val Created : DateTimeOffset = DateTimeOffset.MinValue with get, set
member val Item : 'T = Unchecked.defaultof<'T> with get, set
And then create it using property initialization syntax:
select Envelope<_>( Id = event.Id, StreamId = streamId, ... )
Why does moving the select to a Seq.map work
The Seq.map call is not part of the query expression. It does not end up as part of the IQueryable, so it does not end up compiled to SQL by Entity Framework. Instead, EF compiles just what's inside query and returns you the resulting sequence, after fetching it from SQL Server. And only after that you apply Seq.map to that sequence.
The code inside Seq.map is executed on CLR, not compiled to SQL, so it can call anything it wants, including non-default constructors.
This "fix" comes with a cost though: instead of just the fields you need, the whole Event entity gets fetched from DB and materialized. If this entity is heavy, this may have a performance impact.
Another thing to watch out for
Even if you fix the problem by making Envelope a type with default constructor (as suggested above), you'll still hit the next problem: the method JsonConvert.DeserializeObject can't be compiled to SQL, so Entity Framework will complain about it, too. The way you should do it is fetch all fields to the CLR side, then apply whatever non-SQL-compilable transformations you need.
Using LINQ to Entities, everything that happens in the query computational expression is actually executed within the database engine, which may reside on a remote server. Everything outside of it is executed in the running application on the client.
So, in your first snippet, Entity Framework refuses to execute Envelope<'T>'s constructor because, in order to do so, it would need to translate that into SQL commands for the server. This is plainly not something it can guarantee, because the constructor could potentially contain any sort of side effects and .NET framework code - it could request user input, read files from the client's hard disk, whatever.
What EF can do, in your second snippet, is sending its own POCO event objects back to the client, which is then tasked with Seq.mapping them to your fancy Envelopes, which it can do because it's running on your client machine with access to the full .NET framework.
Addendum: So why are parameterless constructors ok? What if I were to call MsgBox() in a parameterless constructor? I think that parameterless constructors work by having the client construct the objects (without knowing the query results), sending them to the server in serialised form, and having the server just fill the object's properties with the query results.
I haven't actually tested that. But F# record types have no parameterless constructors anyway, so the point is moot in your case.

Entity Framework Object not allowing Connection String to be passed as parameter

I am trying to initialize an Entity object (ADO.NET EF Object), but it does not allow me to choose what connection string I want to use. I need to change connection string in order to give different access levels to users.
There are no overrides in the Entities Object, just a parameterless constructor.
If anyone can give me any pointers, it is appreciated.
If you have used the designer to generate an .edmx file for you, you will have something like below:
public MyEntities() : base("name=MyEntities", "MyEntities")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
This will by default, get the connection string from your configuration file.
What you could do in this case is set the connection string
public partial class MyEntities
{
partial void OnContextCreated()
{
//Dynamically Building a Connection String
this.Connection.ConnectionString = "myconnectionstring";
}
}
Bear in mind though that this will first use the base constructor to pull the connection string from config, then set it with your custom version, basically overriding the connection string. This is typically good when you always want a default connection string.
Another option if you want a bit more control, is pass the connection string in via the constructor as shown below:
public partial class MyEntities
{
public MyEntities(string connectionString) :
base(connectionString,"MyEntities")
{
this.OnContextCreated();
}
}
Now you are passing in the connection string down to the base class and this is the only one it will use. This does mean however that you will most often need to supply this each time.
Hope this helps...

Adding custom property to object returned from WCF RIA Services

I have a stored procedure in my Entity Framework Model. I've added a Function Import and mapped the results to a Complex Type.
I want to add an extra property to this Complex type, that I'll populate in my Domain Service, not coming back from the stored procedure. I added a myClass.shared.cs file and implemented added the property like so:
//myClass.shared.cs
public partial class myClass
{
public string myProperty {get;set;}
}
I populate this in my domain service when I return the object, e.g.:
public myClass GetMyClass(int myClassID)
{
myClass theClass= this.ObjectContext.StoredProc(myClassID).FirstOrDefault();
class.myProperty = 12345;
return theClass;
}
When I get the return values of this method on the client side theClass.myProperty is always null but all values from the stored procedure are populated, am I missing something?
I've tried decorating the myProperty with the [DataMember] attribute but this throws the error:
"The type 'myClass' already contains a
definition for 'myProperty'"
How can I get this to return the value set in the Domain Service to the client?
There was no need to put this in the shared.cs class. The shared.cs class copies the actual code over to the client side and is useful for adding methods etc. but to add a new property, all I had to do was add a partial class (NOT in myClass.shared.cs) and decorate it with DataMember.
public partial class myClass
{
[DataMember]
public string myProperty {get;set;}
}

WCF with Entity Framework Error

Error: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
I am trying to create a WCF service with Entity Framework (VS 2010, .NET 4). When I run it, I get the above error.
I read something about editing the T4 template, but it appears that it already has
[DataContractAttribute(IsReference=true)]
public partial class Person : EntityObject
and
[DataMemberAttribute()]
public global::System.Int32 ID
{
get
{
return _ID;
}
I am not sure what the difference is between
[DataMemberAttribute()] and [DataMember]
or
[DataContractAttribute(IsReference=true)] and [DataContract]
either.
public Person GetPersonByID(int id)
{
using (var ctx = new MyEntities())
{
return (from p in ctx.Person
where p.ID == id
select p).FirstOrDefault();
}
}
How does WCF and EF work together, properly?
Do you have navigation properties in your Person class? Did you disable lazy loading? Otherwise it will probably try to load content for navigation properties during serialization and it fails because of closed context.
To your other questions:
[DataMemberAttribute()] and [DataMember] are same. It is just shorter name.
[DataContractAttribute(IsReference=true)] and [DataContract] are not same. IsRefrence allows tracking circular references in navigation properties. Without this parameter circular reference causes never ending recursion.

Entity Framework 4 POCO entities in separate assembly, Dynamic Data Website?

Basically I want to use a dynamic data website to maintain data in an EF4 model where the entities are in their own assembly. Model and context are in another assembly.
I tried this Entity Framework 4 + Self-Tracking Entities + ASP.NET Dynamic Data = Error
but get an "ambiguous match" error from reflection:
System.Reflection.AmbiguousMatchException was unhandled by user code
Message=Ambiguous match found.
Source=mscorlib
StackTrace:
at System.RuntimeType.GetPropertyImpl(String name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers)
at System.Type.GetProperty(String name)
at System.Web.DynamicData.ModelProviders.EFTableProvider..ctor(EFDataModelProvider dataModel, EntitySet entitySet, EntityType entityType, Type entityClrType, Type parentEntityClrType, Type rootEntityClrType, String name)
at System.Web.DynamicData.ModelProviders.EFDataModelProvider.CreateTableProvider(EntitySet entitySet, EntityType entityType)
at System.Web.DynamicData.ModelProviders.EFDataModelProvider..ctor(Object contextInstance, Func1 contextFactory)
at System.Web.DynamicData.ModelProviders.SchemaCreator.CreateDataModel(Object contextInstance, Func1 contextFactory)
at System.Web.DynamicData.MetaModel.RegisterContext(Func`1 contextFactory, ContextConfiguration configuration)
at WebApplication1.Global.RegisterRoutes(RouteCollection routes) in C:\dev\Puffin\Puffin.Prototype.Web\Global.asax.cs:line 42
at WebApplication1.Global.Application_Start(Object sender, EventArgs e) in C:\dev\Puffin\Puffin.Prototype.Web\Global.asax.cs:line 78
InnerException:
I came across a similar problem to this recently. It had to do with inheritance in my model. I had a Resource entity that had derived types of Person, Equipment, etc. and in those I had overridden a couple properties, but by mistake gave them different signatures. I'll describe my scenario and hopefully it will help.
To be able to debug deep enough into the framework, and see all the variable values, you will have to disable optimizations:
Link
I was seeing the Ambiguous Match error when registering the Context in Global.asax as you were:
public static void RegisterRoutes(RouteCollection routes)
{
// IMPORTANT: DATA MODEL REGISTRATION
// Uncomment this line to register an ADO.NET Entity Framework model for ASP.NET Dynamic Data.
// Set ScaffoldAllTables = true only if you are sure that you want all tables in the
// data model to support a scaffold (i.e. templates) view. To control scaffolding for
// individual tables, create a partial class for the table and apply the
// [ScaffoldTable(true)] attribute to the partial class.
// Note: Make sure that you change "YourDataContextType" to the name of the data context
// class in your application.
DefaultModel.RegisterContext(typeof(EntityModelContainer), new ContextConfiguration() { ScaffoldAllTables = true });
Stepping into the RegisterContext method, I got to System.Web.DynamicData.ModelProviders.EFDataModelProvider there is section of code that loads all the Entities in the model by traversing the inheritance hierarchy in the constuctor for EFDataModelProvider.
while (objectStack.Any()) {
EntityType entityType = objectStack.Pop();
if (entityType != null) {
// Update the entity set when we are at another root type (a type without a base type).
if (entityType.BaseType == null) {
currentEntitySet = entitySetLookup[entityType];
}
var table = CreateTableProvider(currentEntitySet, entityType);
tables.Add(table);
}
foreach (EntityType derivedEntityType in derivedTypesLookup[entityType]) {
// Push the derived entity types on the stack
objectStack.Push(derivedEntityType);
}
}
I put a breakpoint in here and was able to see that Ambiguous Match was occurring for me when calling CreateTableProvider on my Equipment entity (which was derived from Resource).
Looking back at the Stack Trace from the original exception (which I should have done in the first place!) I put a breakpoint in the constructor for System.Web.DynamicData.ModelProviders.EFTableProvider.IsPublicProperty and watched to see which property/method/whatever was causing the ambiguous match -- for me this ended up being a navigation property called Resources (Resources are themselves a hierarchy) that I had overridden in Equipment.
private static bool IsPublicProperty(Type entityClrType, string propertyName) {
var property = entityClrType.GetProperty(propertyName);
return property != null && property.GetGetMethod() != null;
}
In the partial class for Equipment, I had:
public partial class Equipment
{
public new IEnumerable<Resource> Resources
{
but in the parent class, Resource, Resources was defined as:
public virtual ICollection<Resource> Resources
{
When these Properties are being loaded by the .GetProperty(propertyName) in IsPublicProperty, they have the same name but different signatures (because I had given them different return type by mistake) so it isn't clear which shoudl be loaded based on name alone. I corrected my mistake and made Resources in my Equipment class return an ICollection, and boom -- no more ambiguous match.
Not sure if this will help or not, but if you step through in a similar way you should be able to find exactly what is causing the ambiguous match.