ASP.Net WebAPI OData - Composite keys on entities - entity-framework

I have some entities in my EF data model where the entity's key is a composite of two columns.
Made-up (but reasonably close) example... Three entities
Patients
PatientID (PK)
PatientName, etc
Identifiers
PatientID (PK)
IdentifierTypeID (PK)
Code
StartDate, etc
IdentifierTypes
IdentifierTypeID (PK)
Description
The Identifiers table has a composite key. Basically a many-to-many between Patients and IdentifierTypes but with data on the join table. It ought not to matter, but in case it does, the IDs are GUIDs (uniqueidentifier in SQL Server).
I can bend my database a bit to make a new column in Identifiers such as "IdentifierID" that could be the primary key, but we have a legacy code base I'd rather not modify if I can avoid it.
I can see several solutions that avoid modifying the tables although I'm not sure if all are actually possible in WebAPI
a)
Create a view in the database and have this as the basis of my entity. The view is of the form
select
*,
IdentifierID = cast( PatientID as varchar(50) ) + '_' + cast( IdentifierTypeID as varchar(50) )
from Identifiers
This has the problem that seeks on IdentifierID from Entity Framework will be slow. I can rectify this somewhat with an indexed view I suppose.
b)
Same as (a), but in my controller's [Queryable] Get() method I instead use ODataQueryOptions and some expression parsing magic (not very experienced with it at all) to see if we're filtering on IdentifierID and, if so, split that up into filters on PatientID and IdentifierTypeID
c)
I keep things as they are and discover some unknown support for Tuple as entity key in the WebAPI OData stack. That would be awesome :)
I'm inheriting from EntitySetController<>, so it would look like I'm inheriting from EntitySetController>
d)
Some blend of (c) that's not quite as clever but still achieves the same outcome
It seems possible to have composite keys in the OData spec as this other question on StackOverflow says how to address such an entity in OData (using WCF Data Services). How to address entity that uses composite identity key in OData Url?
I don't mind using nightly builds or even using the ASP.Net source - I've spent the weekend stepping through OData requests already to solve another issue (my fault) formatting GUIDs in query strings :P I'm hoping someone's already invested the hours understanding this and I can avoid another day hitting breakpoints in the framework source.
Thanks for your help,
Ian

It is possible to do composite keys with web API OData. You don't even need to use the nightly build to do that. RTM bits should be good. Refer to this great sample by Hongye for details.

Related

Entity Framework: Doing JOINs without having to creating Entities

Just starting out with Entity Framework (Code First) and I have to say I am having a lot of problems with it when loading SQL data that is fairly complex. For example, let's say I have the following tables which stores which animals belongs to which regions in the world and the animal are also categorized.
Table: Region
Id: integer
Name string
Table AnimalCategory
Id integer
Name: string
RegionId: integer -- Refers back Region
Table Animal
Id integer
AnimalCategoryId integer -- Refers back AnimalCategory
Let's say I want to create a query with Entity Framework that would load all Animals for a specific region. The easiest thing to do is to create 3 Entities Region, AnimalCategory, and Animal and use LINQ to load the data.
But let's say I am not interested in loading any AnimalCategory information and define an Entity class just to represent AnimalCategory so that I can do the JOIN. How can I do this with Entity Framework? Even with many of its Mapping functions I still don't think this is possible.
In non Entity Framework solutions this is easy to accomplish by using INNER JOINs in SPs or inline SQL. So what are my options in Entity Framework? Shall I pollute my data model with these useless tables just so I can do a JOIN?
It's a matter of choice I guess. EF choose to support many-to-many associations with transparent junction tables, i.e. where junction tables only have two foreign keys to the associated entities. They simply didn't choose to support this far less common "skipping one-to-many-to-many" scenario in a similar manner.
And I can imagine why.
To start with, in a many-to-many association, the junction table is nothing but that: a junction, an association. However, in a chain of one-to-many (or many-to-one) associations it would be exceptional for any of the involved tables to be just an association. In your example...
Animal → AnimalCategory → Region
...AnimalCategory would only have a primary key (Id) and a foreign key (RegionId). That would be useless though: Animal might just as well have a RegionId itself. There's no reason to support a data model that doesn't make sense.
What you're after though, is a model in which the table in the middle does carry information (AnimalCategory.Name), but where you'd like to map it as a transparent junction table, because a particular class model doesn't need this information.
Your focus seems to be on reading data. But EF has to support all CRUD actions. The problem here would be: how to deal with inserts? Suppose Name is a required field. There would be no way to supply its value.
Another problem would be that a statement like...
region.Animals.Add(animal);
...could mean two things:
add an Animal and a new AnimalCategory, the latter referring to the Region.
Add an Animal referring to an existing AnimalCategory - without being able to choose which one.
EF wouldn't want to choose for some default behavior. You'd have to make the choice yourself, so you can't do without access to AnimalCategory.

EF Table-per-hierarchy mapping

In trying to normalize a database schema and mapping it in Entity Framework, I've found that there might end up being a bunch of lookup tables. They would end up only containing key and value pairs. I'd like to consolidate them into one table that basically has two columns "Key" and "Value". For example, I'd like to be able to get Addresses.AddressType and Person.Gender to both point to the same table, but ensure that the navigation properties only return the rows applicable to the appropriate entity.
EDIT: Oops. I just realized that I left this paragraph out:
It seems like a TPH type of problem, but all of the reading I've done indicates that you start with fields in the parent entity and migrate fields over to the inherited children. I don't have any fields to move here because there would generally only be two.
There are a lot of domain-specific key-value pairs need to be represented. Some of them will change from time to time, others will not. Rather than pick and choose I want to just make everything editable. Due to the number of these kinds of properties that are going to be used, I'd rather not have to maintain a list enums that require a recompile, or end up with lots of lookup tables. So, I thought that this might be a solution.
Is there a way to represent this kind of structure in EF4? Or, am I barking up the wrong tree?
EDIT: I guess another option would be to build the table structure I want at the database level and then write views on top of that and surface those as EF entities. It just means any maintenance needs to be done at multiple levels. Does that sound more, or less desireable than a pure EF solution?
Table per hiearchy demands that you have one parent entity which is used as base class for child entities. All entities are mapped to the same table and there is special discriminator column to differ type of entity stored in database record. You can generally use it even if your child entities do not define any new properties. You will also have to define primary key for your table otherwise it will be handled as readonly entity in EF. So your table can look like:
CREATE TABLE KeyValuePairs
(
Id INT NOT NULL IDENTITY(1,1),
Key VARCHAR(50) NOT NULL,
Value NVARCHAR(255) NOT NULL,
Discriminator VARCHAR(10) NOT NULL,
Timestamp Timestamp NOT NULL
)
You will define your top level KeyValuePair entity with properties Id, Key, Value and Timestamp (set as concurrency mode fixed). Discriminator column will be used for inheritance mapping.
Be aware that EF mapping is static. If you define AddressType and Gender entities you will be able to use them but you will not be able to dynamically define new type like PhoneType. This will always require modifying your EF model, recompiling and redeploying your application.
From OOP perspective it would be nicer to not model this as object hiearchy and instead use conditional mapping of multiple unrelated entities to the same table. Unfortunatelly even EF supports conditional mapping I have never been able to map two entities to the same table yet.

Trouble inheriting from another entity

I'm having trouble configuring entity relationships when one entity inherits from another. I'm new to ADO Entity Framework -- perhaps someone more experienced has some tips for how this is best done. I'm using .net 4.
Database tables with fields:
Products (int ID, nvarchar Description)
FoodProducts (int ProductID, bit IsHuge)
Flavors (int ID, int FoodProductID, nvarchar Description)
There are constraints between Products and FoodProducts as well as FoodProducts and Flavors.
Using the designer I create a model from the database. The designer seems to get it right, with a 1:0..1 association between Product and FoodProduct entities, and 1:* association between Flavor and FoodProduct. No errors when I save or build.
Next I set FoodProduct entity to inherit from Product entity. Then I get errors concerning relationship between Product and FoodProduct. Ok, starting fresh, I first delete the relationship between Product and FoodProduct before setting the inheritance. But now I get errors about the relationship between FoodProduct and Flavor. So I delete and then recreate that relationship, connecting Flavor.ID to FoodProduct.ProductID. Now I get other errors.
My question is this: Should I instead be creating relationship between Flavor.FoodProductID and Product.ID? If so, I assume I then could (or should) delete the FoodProduct.ProductID property. Since my database will have many of these types of relationships, am I better off first creating the entity model and exporting the tables to SQL, or importing the database schema and then making many tweaks?
My intent is that there will be several types of products, some of which require many additional fields, some of which do not. So there may be zero or one FoodProducts records associated with each Product record. At least by my thinking, the table for each sub-type (FoodProducts) should be able to "borrow" the primary key from Products (as a FK) to uniquely identify each of its records.
You can find a screen capture here: http://img218.imageshack.us/img218/9720/entityframework.jpg (I'd embed the img but haven't earned the requisite rep' yet!)
Well, I deleted the FoodProduct.ProductID field, as it should always return the same value as Product.ID anyway. Then, as you hinted, I had to manually map the Products.ID field to FoodProducts.ProductID field. Errors resolved. I'll write a little code to test functionality. Thanks for the "observations"!
Couple of observations:
FoodProducts needs a primary key (e,g identity - FoodProductID). Are you sure it should be a 1:0..1 between Food and FoodProducts? I would have thought it should be 1:0..*. For this cardinality to work you need a unique PK on this table.
When you setup inheritance for entities, the parent entity's properties are inherited. So FoodProducts will inherit ID from the Product table.
BUT, on the physical model (database), this field still needs to be mapped to a column on the FoodProducts table - which is why you need the identity field.
After you setup inheritance, you still need to map all the columns on the derived tables. My money is on you have not mapped "ID" on FoodProducts to any column.
If you screencapped your model and show the errors you are getting it would be much easier to diagnose the issue.

GUID or int entity key with SQL Compact/EF4?

This is a follow-up to an earlier question I posted on EF4 entity keys with SQL Compact. SQL Compact doesn't allow server-generated identity keys, so I am left with creating my own keys as objects are added to the ObjectContext. My first choice would be an integer key, and the previous answer linked to a blog post that shows an extension method that uses the Max operator with a selector expression to find the next available key:
public static TResult NextId<TSource, TResult>(this ObjectSet<TSource> table, Expression<Func<TSource, TResult>> selector)
where TSource : class
{
TResult lastId = table.Any() ? table.Max(selector) : default(TResult);
if (lastId is int)
{
lastId = (TResult)(object)(((int)(object)lastId) + 1);
}
return lastId;
}
Here's my take on the extension method: It will work fine if the ObjectContext that I am working with has an unfiltered entity set. In that case, the ObjectContext will contain all rows from the data table, and I will get an accurate result. But if the entity set is the result of a query filter, the method will return the last entity key in the filtered entity set, which will not necessarily be the last key in the data table. So I think the extension method won't really work.
At this point, the obvious solution seems to be to simply use a GUID as the entity key. That way, I only need to call Guid.NewGuid() method to set the ID property before I add a new entity to my ObjectContext.
Here is my question: Is there a simple way of getting the last primary key in the data store from EF4 (without having to create a second ObjectContext for that purpose)? Any other reason not to take the easy way out and simply use a GUID? Thanks for your help.
I ended up going with a GUID.
The size/performance issues aren't
critical (or even noticeable) with SQL Compact, since
it is a local, single-user system.
It's not like the app will be
managing an airline reservation
system.
And at least at this point, there
seems to be no way around the "no
server-generated keys" limitation of
the SQL Compact/EF4 stack. If someone has a clever hack, I'm still open to it.
That doesn't mean I would take the same approach in SQL Server or SQL Express. I still have a definite preference for integer keys, and SQL Compact's bigger siblings allow them in conjunction with EF4.
Use a Guid. AutoIncrement is not supported on Compact Framework with Entity Framework.
Also, if you ever want to create a application which uses multiple data sources, int PK's are going to fall apart on you very, very quickly.
With Guid's, you can juse call Guid.NewGuid() to get a new key.
With int's, you have to hit the database to get a valid key.
If you store data in multiple databases, int PK's will cause conflicts.
What I've done for SQL CE before, and I assume we have a single application accessing the database, is to calculate the MAX value on startup and put it in a static variable. You can now hand out sequential values easily and you can make the code to generate them thread safe very easily.
One reason to avoid Guids would be size = memory and storage space consumption.
You could also query SQL Compact metadata like so:
SELECT AUTOINC_NEXT FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'Categories' AND AUTOINC_NEXT IS NOT NULL

Entity Framework and associations between string keys

I am new to Entity Framework, and ORM's for that mather.
In the project that I'm involed in we have a legacy database,
with all its keys as strings, case-insensitive.
We are converting to MSSQL and want to use EF as ORM,
but have run in to a problem.
Here is an example that illustrates our problem:
TableA has a primary string key,
TableB has a reference to this primary key.
In LINQ we write something like:
var result = from t in context.TableB select t.TableA;
foreach( var r in result )
Console.WriteLine( r.someFieldInTableA );
if TableA contains a primary key that reads "A", and TableB contains two rows that references TableA but with different cases in the referenceing field, "a" and "A".
In our project we want both of the rows to endup in the result, but only the one
with the matching case will end up there.
Using the SQL Profiler, I have noticed that both of the rows are selected.
Is there a way to tell Entity Framework that the keys are case insensitive?
Edit:We have now tested this with NHibernate and come to the conclution that NHibernate works with case-insensitive keys. So NHibernate might be a better choice for us.I am however still interested in finding out if there is any way to change the behaviour of Entity Framework.
Thanks for your answer!
Problem is that if we add that constraint to the database now,
the legacy application might stop working because of how it is built.
Best for us would be, if possible, to change the behavior of EF.
I'm guessing it is not possible, but I'm giving it a shot.
Regards,
Fredrik
edit: The reason why I added an answer to my own question was that I added this question before I was a registerd user, and when I had registred my account I couldn't add comments or edit my post. Now the accounts are merged.
I think you need to make the change to the schema in SQL Server, not in EF. This post's answer, on how to make a column case-sensitive, looks like it will do the trick: T-SQL: How do I create a unique key that is case sensitive?
I know this isn't a perfect solution, but in LINQ why not do the join yourself. EF doesn't work because the .Designer.cs file returns objA.Equals(objB) when doing the join. .Equals is case sensitive.
var result = from t1 in context.TableB
join t2 in context.TableA on t1.someFieldInTableB.ToUpper() equals t2.someFieldInTableA.ToUpper();
Hackish I know, but LINQ to Entities is still in its infancy and the object classes that are designed are designed for specific reasons that do not handle exceptional cases in a design such as this.
Another alternative is that you can create your own code generator using T4 templates. Since everything is a public partial class you can create a navigation property that actually does the case insensitive comparisson that you are looking for.
To answer your question truthfully though, there is no "out of the box" way to get EF to do a navigation using case insensitive searching.
I came up with a workaround that "stitches up" the string based association in memory after the context has retrieved the rows from the database (hint: making using of the context.[EntityTypeCollection].Local property. You can see my answer at https://stackoverflow.com/a/12557796/62278
I know this isn't a perfect solution, but in LINQ why not do the join yourself. EF
doesn't work because the .Designer.cs file returns objA.Equals(objB) when doing the >> join. .Equals is case sensitive.
Well, not if you override the Equals method
The generated domain classes in EF are partial no? So it's fairly easy to replace the default Equals implementation of these classes by your own implementations (which of course would render it case insensitive )
BTW : a technique dat dates back from .NET 1.0
With all this .NET 3.5/4.0, Linq and Lambda violence, people tend to forget about the basics
As an alternative to the Entity Framework, you can use LINQ to SQL, which works well with relations involving case sensitive collations. Although this ORM does not offer all the flexibility of EF or NHibernate, it can be sufficient in many cases.
I've recently posted a thread on the official Microsoft Entity Framework forum:
http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/d4aa6880-31b3-4ff2-b7f5-e2694d76772e