Entity Framework 4.1 Code First: Single column foreign-key to multiple entities - entity-framework-4

I've been trying to create model in EF 4.1 to represent a database schema with a single table and column holding foreign keys from two other tables, but have had little luck with both annotations and the fluent API. A sample model is shown here:
public class User
{
...
public virtual ExtendedAttribute ExtendedAttributes { get; set; }
}
public class Account
{
...
public virtual ExtendedAttribute ExtendedAttributes { get; set; }
}
public class ExtendedAttribute
{
public Guid Id {get; set;}
public Guid ItemId {get; set;} // both Account.Id and User.Id stored here
public string Value { get; set; }
}
Currently the configuration for these entities looks something like this for both User and Account modelBuilders:
this.HasOptional(u => u.ExtendedAttributes).WithRequired();
Any thoughts on how to do achieve? Many thanks.

It is even not possible with the database itself and EF will not put any abstraction for that. You must have separate column and navigation property for each entity.

Related

Establish Foreign Key Connection Using Entity Framework With SQL Queries

I have a couple of classes (for this example anyway) that use code first with the entity framework to connect to the database.
public class Customer
{
[Key]
public long CustomerId { get; set; }
public string CompanyName { get; set; }
...
public virtual List<Contact> Contacts { get; set; }
}
public class Contact
{
[Key]
public long ContactId { get; set; }
public string Forename { get; set; }
...
public long CustomerId { get; set; }
public virtual Customer Customer { get; set; }
}
When I hook these up in my context class directly to the db the foreign key relationships hook up fine and I can access the collection of contacts from within the customer class.
class RemoteServerContext : DbContext
{
public DbSet<Customer> Customers { get; set; }
public DbSet<Contact> Contacts { get; set; }
...
}
My problem is that these database tables are used by various different systems and are massive. In order to increase efficiency I have overridden the default behaviour to point at a view (and also a stored proc elsewhere) rather than directly at the table.
public IEnumerable<Customer> Customers ()
{
return Database.SqlQuery<Customer>("SELECT * FROM vw_CustomerList");
}
public IEnumerable<Contact> Contacts()
{
return Database.SqlQuery<Contact>("SELECT * FROM vw_ContactsList");
}
I have made sure that in each of the views I have included the foreign key fields: CustomerId and ContactId.
When I do this however the class joins appear to be lost - there's always a null when I drill into either of the objects where it should be pointing to the other one. I have tried to set up what the foreign key field should point to but this doesn't seem to help either.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Contact>().HasRequired(p => p.Customer)
.WithMany()
.HasForeignKey(k => k.CustomerId);
}
Is there a way to establish the connection when overriding the default behaviour?
There is no overriding in this case. If you removed
public DbSet<Customer> Customers { get; set; }
and replaced it with
public IEnumerable<Customer> Customers ()
{
return Database.SqlQuery<Customer>("SELECT * FROM vw_CustomerList");
}
you have completely changed the behavior. The first uses entities and full power of EF. The second is only helper to execute custom SQL. Second without first or without defining entity in OnModelCreating doesn't use Customer as mapped entity at all - it uses it as any normal class (only mapped entities can use features like lazy loading).
Because your Customer is now mapped to view you cannot use your former Customer class used with table. You must define mapping of Customer to a view by cheating EF:
modelBuilder.Entity<Customer>().ToTable("vw_ContactsList"); // EF code fist has no view mapping
Once you have this you can try again using:
public DbSet<Customer> Customers { get; set; }
Unless your view is updatable you will get exception each time you try to add, update or delete any customer in this set. After mapping relation between Customer and Contact mapped to views your navigation properties should hopefully work.
The problem with SqlQuery is the way how it works. It returns detached entities. Detached entities are not connected to the context and they will not lazy load its navigation properties. You must manually attach each Customer instance back to context and to do that you again need DbSet.

Partial update of entity objects with EF 4

I'm implementing DAL and BL layers of application.
It is hosted as WCF service, and EF 4 is used as ORM.
We have role based security layer and business rule that only part of object can be updated by some particular role.
Here is simplified example of problem:
We have such DTOs:
MainType
{
public Guid ID { get; set; }
public String DoctorField1 { get; set; }
public String DoctorField2 { get; set; }
public String NurseField1 { get; set; }
public String NurseField2 { get; set; }
public DateTime Created {get; set;}
public DateTime Updated {get; set;}
public Guid LastUpdateBy {get; set;}
public List<DetailsType> Details { get; set; }
}
DetailsType
{
public Guid MainTypeID { get; set; }
public Guid SomeIdentityID { get; set; }
public String DoctorDetail { get; set; }
public String NurseDetail { get; set; }
public DateTime Created {get; set;}
public DateTime Updated {get; set;}
public Guid LastUpdateBy {get; set;}
}
This entities are mapped to corresponding DB tables with the same fields.
ID field of MainType is Primary Key;
MainTypeID of DetailsType is Foreign Key to MainType table.
SomeIdentityID of DetailsType is FK to some other entity that is not important for this sample.
MainTypeID SomeIdentityID is complex primary key for DetailsType table.
I have graph of such objects (1 main and list details), and determined role of user who performs update operation.
My task is:
IF current user has role Doctor - update Doctor fields in Main object and all Details objects, insert new details objects.
IF current user has role Nurse - update Nurse fields in Main object and all Details objects.
save current date to Updated field
save current user id to LastUpdateBy field
do not modify Created field and any other field that are not updated by this role.
So for example if I have user with role Doctor I should do following:
Update DoctorField1, DoctorField2, Updated, LastUpdateBy in MainObject
Update DoctorDetail, Updated, LastUpdateBy in every details object
DO NOT modify any other fields.
Currently we have implementation that reads full graph for MainObject, makes necessary modifications and saves in back to DB.
This solution works too slow and I need to find a way to improve it.
Currently I know clearly how to do that by RAW SQL, but this will be my solution in case nothing else will help.
How can I make Entity Framework to update only needed fields and ignore another.
I have some successful results with ApplyOriginalValues and ApplyCurrentValues methods for String fields.
Idea was to assign some fictive value to property in both objects, for example string "##$%&##$%&##$%&##$%&##$%&", and EF then treats them as not modified properties during saving changes.
However this trick does not work with Boolean, Int32 and Decimal values.
I should use some simple approach to all objects.
I will appreciate any ideas and thoughts about this problem.
If you have such specific requirement you should start by modifying your WCF service to not accept fields which user cannot modify. That leads to two simple DTOs:
public class MainTypeUpdateDto
{
public Guid ID { get; set; }
public String Field1 { get; set; }
public String Field2 { get; set; }
public List<DetailsTypeUpdateDto> Details { get; set; }
}
public class DetailsTypeUpdateDto
{
public Guid MainTypeID { get; set; }
public Guid SomeIdentityID { get; set; }
public String Detail { get; set; }
}
All other fields either cannot be updated or should be handled by your server side logic.
Now when you receive dtos you can map them back to real entity objects. Based on user role you will know which fields and details you must set. You have two options to force EF to save only fields you want:
First create object graph with MainType and related details. Set only Ids in these entities and attach MainType entity to context. After that set all updatable fields to current values. Do not change state of any entity.
Create object graph with MainType and all related details and set all Ids and all updatable fields. After that attachMainType` entity to the context and manually set state for each modified property (on each entity).
You can need some additional logic if user can also remove or add details.

Representing IList<Guid> property in Entity Framework 4.0

We have a model class defined that I want to produce from our EF 4.0 edmx for persistence. The class looks roughly as follows:
[DataContract]
public class Schedule
{
[DataMember]
public string Name { get; set; }
[DataMember]
public Guid Id { get; set; }
[DataMember]
public DateTime RunDate { get; set; }
[DataMember]
public IList<Guid> Routes { get; set; }
[DataMember]
public IList<Guid> Paths { get; set; }
}
How do I represent Routes and Paths on the edmx design surface? I can't see anyway of doing this other than creating two entities with a single Guid Id field then setting a 1-* Association to Schedule. I'd rather not have to do that as we'll then have a Route and Path class that isn't what we want at the moment.
We haven't had chance to look at Code First yet and don't really have time to figure it out for this project but would it support our needs?
Thanks for any assistance.
You must either use related entities or you musn't map them directly. You can for example map another fields called RoutesSerialized and PathsSerialized which will be of type string and contains all Guids stored as strings and separated by semicolon. Your current properties will use return IEnumerable and use internally use functions like String.Join, String.Split, ToString and Guid.Parse.

managing lookup in MVC2 and persisting object with Nhibernate

My simplified domain model looks something like this:
public abstract class Entity<IdK>
{
public virtual IdK Code { get; protected set; }
}
public class Contact : Entity
{
public virtual string Name { get; set; }
public virtual Company Company { get; set; }
}
public class Company : Entity
{
public virtual string Name { get; set; }
}
and I've defined a viewmodel:
public ContactViewModel()
{
public Guid Code { get; set; }
public int Version { get; set; }
public string Name { get; set; }
public string Company { get; set; }
public List<SelectListItem> Companies { get; set; }
}
to manage my contacts in a view.
Since I want the user to be able to choose from a list of companies I've added a list of SelectedListItem which will be rendered in my view like this:
<%=Html.ListBoxFor(m => m.Company, (List<System.Web.Mvc.SelectListItem>)Model.Companies)%>
Now, when the user submits my form I remap my viewmodel with my model before I save it.
I populate my Contact and use the id of the ContactViewModel.Company to create an object of type Company to associate with the property of the Contact class.
Since I don't want to fetch the whole company from the database I just fill the id.
When I persist my contact, though, I get an exception: "not-null property references a null or transient Domain.Contact.Company".
What is the best solution to manage lookups and persistence with MVC + Nhibernate?
Do you have any suggestions from your experience?
Unfortunately with NHibernate and lookups you can't just assign the ID property to a new instance of the Company object and then assign that Company object to the Contact.
Generally what I would do is in my repository, assuming that you can't change the Company information when saving a contact is something like this:
public Contact Save(Contact contact)
{
if(contact.Company.Id > 0)
contact.Company = Session.Load<Company>(contact.Company.Id);
Session.SaveOrUpdate(contact);
}
I generally find this allows you to encapsulate the logic of loading the Company and also allows you to keep it all wrapped up nicely in a single session.
Using Session.Load in this manner avoids hitting the database as described here
If you don't do this, what you're essentially saying to NHibernate is that you have a company object which you have assigned an ID and now want to save it with all the properties set to Null or empty string values or whatever and that is not what you want.
Alternatively you could create a Save specific Domain Object that looks like this:
public abstract class Entity<IdK>
{
public virtual IdK Code { get; protected set; }
}
public class SavableContact : Entity
{
public virtual string Name { get; set; }
public virtual IdK CompanyId { get; set; }
}
Which maps directly to the Contact table in your database so that when you Save this entity you can literally just map back the CompanyId from your view model and NHibernate will only save that value back and not care at all about the company objects.
It's a case of working out what works best for you. I personally prefer the first option as the extra bit of logic helps simplify the domain model, however if you're creating and exposing a public API then the second method might make more sense.

how do i create a composite key that comprises a foreign key with code first?

I am using EF4 code first and want to generate a composite key which is made of a class property and foreign key. I have two classes: Order and Company. The Order class holds a reference but this will not necessarily be unique between companies. So I intend to use a composite key made up of Reference and Company.CompanyId.
I have tried using the following to set it but I get an error message "Key expression is not valid".
modelBuilder.Entity<Order>().HasKey(o => new { o.Reference, o.Company.CompanyId });
I have also tried
modelBuilder.Entity<Order>().HasKey(o => new { o.Reference, o.Company });
and this fails.
these are my classes:
public class Order
{
public string Reference { get; set; }
public Company Company { get; set; }
}
public class Company
{
public int CompanyId { get; set; }
public virtual ICollection Orders { get; set; }
}
Any help would be greatly appreciated.
As Antony Highsky mentioned, you can only use scalar properties in the key.
So, you will need to add a foreign key (scalar property) to the Order class and associate it with the navigation property Company as shown below:
public class Order
{
public string Reference { get; set; }
public int CompanyId { get; set; }
[RelatedTo(ForeignKey = "CompanyId")]
public Company Company { get; set; }
}
And then create the composite key using the model builder:
modelBuilder.Entity<Order>().HasKey(o => new { o.Reference, o.CompanyId });
Note that data annotations (RelatedTo attribute) were introduced with the Entity Framework CTP 3. For another option that only uses data annotations instead of HasKey method, see this post:
http://www.luisrocha.net/2010/11/creating-composite-keys-using-code.html
One thing that doesn't look quite right is your use of the non-generic version of ICollection. Try this:
public virtual ICollection<Order> Orders { get; set; }
Did you try this?
modelBuilder.Entity().HasKey(o =>o.Reference );
modelBuilder.Entity().HasKey(o =>o.CompanyId );
According to this source, only scalar properties are allowed in the key. Navigation properties are not.

Resources