0

I've read several questions/articles about WCF serialization of Entity Framework generated objects, but all of the solutions I've found involve turning on eager loading, which is exactly the thing I've trying NOT to do.

I'm essentially getting the same error as mentioned here The ObjectContext instance has been disposed - Winforms Entity Framework , the difference being that I'm consuming my WCF service from a Silverlight application.

I have a table User_Notifications that has a foreign key to a table User_Info. (A single User_Info can point to many User_Notifications in case I'm not using the terminology correctly). I'm using Entity Framework 4, which has created a class for both of those. I have a WCF call that returns:

return DBEntity.User_Notifications.Where(w => w.UserGUID == UserGuid && w.IsDismissed == false).ToArray();

This gives me all the User_Notifications that I need, but I get an ObjectContext Instance has been disposed error on the client side, which looks like from an attempt to load the associated User_Info class. I don't want the User_Info data though, I want it to just stay null or whatever, I don't need it for displaying notifications.

So, how can I pass my Entity Object without needing to pass the Associated Object?

My boss says this is just another reason why 'we don't need foreign keys in the database' and I'd really like to not go down that path.

8
  • 2
    Sounds like your boss should stick to things that aren't programming related. Commented Nov 25, 2013 at 2:46
  • @Matthew My boss is the Framework developer, and has been for the past 10 years. A few months ago I showed him this cool new thing called a .net dictionary. Commented Nov 25, 2013 at 2:48
  • Might be useful: geekswithblogs.net/danemorgridge/archive/2010/05/04/… (Disable lazy-loading) Commented Nov 25, 2013 at 2:55
  • @Matthew But then won't that by default load both User_Notifications and User_Info? Commented Nov 25, 2013 at 3:01
  • No, it will only load User_Notifications, I'm going to post an answer, so hold onto your hat. Commented Nov 25, 2013 at 3:02

2 Answers 2

0

For what it's worth, it's nuts not to have foreign keys in the DB. They should be there, period.

Insofar as your question... it sounds like you want to return a collection of User_Notifications where the User_Info object is not loaded on the initial query, right? By default, a query to a table should not eager load associated objects.

In your service function, just do:

return (from n in DBEntity.User_Notifications
        where n.UserGUID == UserGuid && n.IsDismissed == false
        select n).ToArray();

If you find that you need the User_Info object on the client side, then just change the above to:

return (from n in DBEntity.User_Notifications.Include("User_Info")
....
Sign up to request clarification or add additional context in comments.

8 Comments

I believe that the problem is that the navigational properties are being lazy-loaded by the serialization mechanism in WCF.
@Matthew Yea that's the problem. My client is erroring out trying to load the associated User_Info object, which isn't there.
You can configure your DB model to disable lazy-loading in the Entity model designer (which I always do). The option is in the properties window if you click on the background. WCF shouldn't lazy-load anything then.
@Ryan But then won't that by default load both User_Notifications and User_Info?
The second version with .Include("User_Info") would load both objects. The first option without the Include() wouldn't eager load the User_Info object at all. Disabling lazy-loading does not enable eager loading. To my knowledge, there is no option to default to eager load all object properties. Otherwise, you'd always download your entire database with any DB call, right?
|
0

My recommendation would be to not expose your EF entities directly through WCF, and instead expose DTOs through your service.

This has two advantages, firstly, you're dealing with real POCO's (not lazy loaded proxies), which do not have persistent specific details in them. The second is that you only expose what the consumer actually needs, which means you can change the underlying implementation without forcing clients to recompile, and you don't have to expose your entire data model to them.

In your example, you could have:

// I'm not a fan of the Dto suffix, but it makes this example easier
public class UserNotificationDto 
{
    // the properties you want to expose through WCF 
    public Guid UserGuid { get; set; }
    public string Message { get; set; }
    // ...        
}

// your service method
IEnumerable<UserNotificationDto> GetNotifications(Guid userGuid)
{
    using (var ctx = new Context())
    {
        return ctx.User_Notifications
            .Where(x => !x.IsDismissed && x.UserGuid == userGuid)
            .Select(
                x => new UserNotificationDto { 
                    UserGuid = x.UserGuid, 
                    Message = x.Message 
                }
            ).ToList();
    }
}

You may use automapper to make the conversion between EF entities and your DTOs easier, but this is just a contrived example.

3 Comments

I'll agree that this is the better way to do this, but in this case it comes down to I have a lot of code to write and a very short deadline to do it (why I'm working on a Sunday night when I'm a M-F programmer). The entity objects themselves are exposed in some cases because it allows the client to make a change and then send them back to the Webservice to save changes. It isn't ideal, but at least these web services won't be called by anything except but what I'm writing here (external web services work like you describe)
I had been doing it like what you described, but it was taking too long to code up everything. Thus the change. In the end we're all slaves to deadlines. But hey, at least I got the framework developer to let me add a few foreign keys to the database finally.
Perhaps your best option is to decorate the properties you don't want serialized. IgnoreDataMember attribute: msdn.microsoft.com/en-us/library/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.