Welcome to MSDN Blogs Sign in | Join | Help
Tip 34 – How to work with Updatable Views

UPDATE: thanks Zeeshan for pointing out that by default only non-nullable columns end up in the key for view backed entities.

Imagine this situation, you have a view in your database, and it is updatable.

Next you decide to use this view with the Entity Framework, so you go ahead and import it.

The resulting entity will look something like this:

Model

As you can see every property icon has a ‘key’ overlay.

Because the entity is based on a view, the EF doesn’t know which columns make up the primary key, so it assumes every non nullable column is part of the primary key.

Fixing the Key

The first step is to change the key. In this case the ID is really the key.

You can do this by opening the EDMX in the XML editor and changing the EntityType so that rather than looking like this:

EntityKey

Where every property is referenced in the <Key>, change it to this:

EntityKeyUpdated 

It is important to note that you have to make this change in both the <edmx:StorageModels> and the <edmx:ConceptualModels> sections of the EDMX, because both models must agree about the shape of the primary key.

Treat the view as a table

At this point you can happily query for Employees use the Entity Framework.

But the Entity Framework won’t allow you to do updates.

The normal way around this is to create stored procedures and use them as modification functions.

But given that the view is already updatable that is obviously not ideal.

Luckily there is a workaround: Simply convince the EF that the view is a table.

To do this you have to change the definition of the EntitySet in the StorageModel. Generally it will start off looking like this:

<EntitySet Name="Employees" 
          
EntityType="Tip34Model.Store.Employees"
           store:Type="Views"
           store:Schema="dbo"
           store:Name="Employees">
  <DefiningQuery>SELECT
  [Employees].[ID] AS [ID],
  [Employees].[Firstname] AS [Firstname],
  [Employees].[Surname] AS [Surname],
  [Employees].[Email] AS [Email]
  FROM [dbo].[Employees] AS [Employees]
  </DefiningQuery>
</EntitySet>

In order to treat it as a table replace that with this:

<EntitySet Name="Employees" 
           EntityType="Tip34Model.Store.Employees" 
           store:Type="Tables"
           Schema="dbo" />

Now you can perform every CRUD operation.

Pretty easy if you ask me.

Posted: Tuesday, September 01, 2009 9:54 PM by AlexJ
Filed under: , ,

Comments

Barbaros said:

Thank you so much, I was wondering about how to get through this all week :)

# September 2, 2009 5:56 AM

AlexJ said:

Barbaros, you are welcome, I'm just glad this was timely for you!

# September 2, 2009 11:45 AM

Craig Stuntz said:

Since the designer always replaces the SSDL, I presume one would have to re-do this every time one updates from the DB in the designer? The solution is easy -- once. Is there a way to not have to do this on every update?

BTW, I'm giving a presentation on the EF next week at the online CodeRage conference:

"How to Think Like the Entity Framework (and why you might want to bother learning to do so in the first place)"

http://conferences.embarcadero.com/coderage/sessions

# September 2, 2009 1:53 PM

AlexJ said:

Craig,

I hear you, the way the designer blows away your SSDL really is unfortunate.

I'm not aware of any elegant solution to this problem. Of course that doesn't stop someone writing something to help by using the low-level model inference APIs, and some sort of merging algorithm.

But as you and I both know that is not going to a trivial exercise. Maybe an ISV can fill the gap here, someone like Kristofer (Huagati Tools). We really should pester him!

Good luck with your presentation!

Alex

# September 2, 2009 2:10 PM

Zeeshan Hirani said:

I thought not every column is marked as primary key.In fact every non nullable column is marked as primary key.

# September 2, 2009 2:27 PM

Craig Stuntz said:

A general solution to the problem of merging custom SSDL with designer-generated SSDL is indeed difficult.

On the other hand, I don't see anything to stop the designer from getting the results correct in the first place. In the specific case in your blog post. The designer is clearly aware of the view definition, since it ends up in the DefiningQuery. For simpler views, it should be able to get the primary key information from the provider metadata interfaces. Of course there are views (defined by much more complicated SQL SELECTs) where this is not possible, and for those, it will probably have to give up and do what it currently does. But right now it doesn't even seem to try.

Also, I'm not sure who to report this to, but for the past two days posting comments to any msdn blog has silently failed if I'm signed into blogs.msdn.com. I get redirected to the blog home page, instead of seeing my comment posted. If I sign out, on the other hand, posting a comment works just fine.

# September 2, 2009 3:08 PM

AlexJ said:

Craig,

I agree the designer should ideally do a better job. When you say the designer is clearly aware of the view generation I think you are wrong there. All the T-SQL in the EntitySet does is reproject columns from the view, it isn't the view definition per say.

Per your comment about the commenting problems I'll forward that to someone internally.

-Alex

# September 2, 2009 4:17 PM

AlexJ said:

Zeeshan,

Yeah you are right. Nice catch, I'll update the post.

Alex

# September 2, 2009 4:18 PM

Craig Stuntz said:

OK, that makes sense. Getting the definition, then, would require asking the DB server. Most DB servers can do this, but I don't know if ADO.NET exposes it in a provider-independent way.

# September 2, 2009 4:54 PM

Fayssal El Moufatich said:

Thanks for the detailed description. Unfortunately, one would have to do this whenever the edmx file is regenerated :(.

# October 7, 2009 10:43 AM

Hannes Preishuber said:

that trick didnt it for me

{"__type":"DataServiceResult:DomainServices","IsDomainServiceException":false,"ErrorCode":500,"ErrorMessage":"Fehler beim Ausführen der Befehlsdefinition. Details finden Sie in der internen Ausnahme.","StackTrace":" bei System.Data.EntityClient.EntityCommandDefinition.ExecuteStoreCommands(EntityCommand entityCommand, CommandBehavior behavior)\r\n bei System.Data.Objects.Internal.ObjectQueryExecutionPlan.Execute[TResultType](ObjectContext context, ObjectParameterCollection parameterValues)\r\n bei System.Data.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption)\r\n bei System.Data.Objects.ObjectQuery`1.System.Collections.Generic.IEnumerable\u003cT\u003e.GetEnumerator()\r\n bei System.Data.Objects.ObjectQuery`1.GetEnumeratorInternal()\r\n bei System.Data.Objects.ObjectQuery.System.Collections.IEnumerable.GetEnumerator()\r\n bei System.Web.Ria.DataServiceQueryRequest.FlattenGraph(IEnumerable list, List`1 result, HashSet`1 visited, DomainServiceDescription domainServiceDescription)\r\n bei System.Web.Ria.DataServiceQueryRequest.Invoke(DomainService domainService)\r\n bei System.Web.Ria.DataService.System.Web.IHttpHandler.ProcessRequest(HttpContext context)"}

# October 22, 2009 10:45 PM

petrux said:

Uhm... sorry but... what happens if you try to delete something?

# November 26, 2009 10:04 AM

AlexJ said:

@Hannes

Not sure what went wrong for you.  Sorry.

@Petrux

So long as the view is an updatable view delete should work just fine. The key is making it updatable.

Alex

# November 27, 2009 8:45 AM
Leave a Comment

(required) 

(required) 

(optional)

(required) 

  
Enter Code Here: Required

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Page view tracker