Map sql view to existing entity in EF 6? - entity-framework

I have a table dbo.Tasks, class for Task, a TaskMap, and add it to the DB context no problem. I also have a database view dbo.vwComplexTaskQuery that returns Tasks. How do I map this view to my task class? I would like to be able to do:
List<Task> Tasks = db.vwComplexTaskQuery.ToList();
Without having to create a vwComplextTaskQuery class that has the same properties and same data annotations.
Simplified Task Class
public class Task
{
public int TaskId { get; set; }
public string Title { get; set; }
}
Simplified Task Map Class
public TaskMap()
{
// Primary Key
this.HasKey(t => t.TaskId);
this.Property(t => t.Title)
.IsRequired()
.HasMaxLength(50);
// Table & Column Mappings
this.ToTable("Tasks");
this.Property(t => t.TaskId).HasColumnName("TaskId");
this.Property(t => t.Title).HasColumnName("Title");
}
Is this possible, if so an example or link to documentation would be great.

I found one solution:
List<Task> tasks = db.Tasks.SqlQuery("Select * from vw_AllTasks").ToList<Task>();

Almost 4 years later... (EF6 has been out that long already? wow...)
EF does not handle VIEWs very well by default: both the EDMX ("Database first") and "Code first" reverse-engineering stuff makes some silly assumptions about views (e.g. that a View can't have the same type as an Entity, a View's Entity doesn't have relationships with other Entities, all of a View's NOT NULL columns must be a composite primary key, etc...
...but if you manually override whatever EF generates such that it thinks a VIEW is a table then it works perfectly correctly (navigation properties, etc). But doing this is kinda difficult and needs to be re-done whenever you update your Model from the database.
Fortunately there's a solution - if you don't mind getting your hands just a bit dirty: There's a third-party, open-source, Entity Framework model generator T4 template (no VS Extensions required: just include 3 files in your project (also available as a NuGet package) called ReversePOCO: https://github.com/sjh37/EntityFramework-Reverse-POCO-Code-First-Generator (disclaimer: I contributed some patches a few months ago specifically to support VIEW handling).
By default, the T4 template in ReversePOCO generates code similar to what EF6's Code First generator creates, but handling VIEWs is straightforward:
In your Database.tt file (where you configure ReversePOCO's Settings object) ensure that Settings.IncludeViews = true.
Look for the Settings.ViewProcessing callback (around like 310 in version 2.37.1).
Uncomment the existing code and modify it to suit your preferences. You will need to list which columns in the view are primary keys.
Look for the Settings.AddForeignKeys callback (around line 326 in version 2.37.1).
Uncomment the existing code and modify it to match the foreign-key relationships you'd like. This will ensure the Navigation properties are created correctly (composite foreign-keys are fully supported).

Related

How to properly Delete fields of an Entity?

I am having trouble when trying to delete a field of an Entity using Entity Framework (version 6.1.3).
Let's say I have two Entities: Person and Work.
I can change the work of a person without any issue, but when I try to express that the person is unemployed it does not work properly:
person.Work = null;
db.SaveChanges();
After running this code the person still will have the previous work, but if I use the debugger and check the Work property of person before running
person.Work = null;, everything will behave as expected.
Can someone please explain why reading the value first makes the code work properly and how to correctly delete the field?
var work = person.Work; \\ with this line here everything works as expected
person.Work = null;
db.SaveChanges();
Two things that are contributing to your issue:
Entity Framework determines what needs to updated during SaveChanges by tracking changes to property values.
You probably have lazy loading enabled (both in general and for the Work property), which means that if the person has an associated Work, that associated entity doesn't get loaded until the first time you access that property.
Putting those together, when you set person.Work = null without accessing person.Work (which would trigger a load), the context thinks nothing has changed. But if you load the property first, setting the property to null tells EF to remove that association. Edit: According to the page that octavioccl linked, this is true for .NET 4.0., but for .NET 4.5+ (and EF 5+), loading first is unneeded.
Possible solutions
If you want to remove the association without loading the related entity, you'll need to add a foreign key property to your Person entity, then you can set that to null instead of setting the navigation property to null. For example:
public class Person
{
// other properties...
public int? WorkId { get; set; }
public virtual Work { get; set; }
}
person.WorkId = null;
db.SaveChanges();
octavioccl's answer quoted another option:
context.Entry(person).Reference(p => p.Work).CurrentValue = null;
From this msdn page:
To delete the relationship, set the navigation property to null. If
you are working with the Entity Framework that is based on .NET 4.0,
then the related end needs to be loaded before you set it to null. For
example:
context.Entry(person).Reference(p => p.Work).Load();
person.Work = null;
Starting with the Entity Framework 5.0, that is based on .NET 4.5, you
can set the relationship to null without loading the related end. You
can also set the current value to null using the following method:
context.Entry(person).Reference(p => p.Work).CurrentValue = null;

Unique Constraint with Entity Framework using Code First Migrations

I am doing entity framework (v 5) code first migrations, on an MVC 4 application. I would like to add a unique constraint at the database level.
I know this can be done when creating the table, but I already have a table.
http://msdn.microsoft.com/en-US/data/jj591621
I have tried the following, marked as answer answer: Unique constraint with EFCodeFirst and SqlCe4
My database context differes slightly, I supply the connection name is as follows
public AppDatabaseContext() : base("MyConnectionDBContext")
When I use the Package Management Console to update the database, the overridden seed method is not called:
protected override void Seed(AppDatabaseContext context)
I have also tried the following: http://romiller.com/2010/07/31/ef-ctp4-tips-tricks-running-additional-ddl/
I did not use a nested class, this is because it seemed as if I had to registere the initializer via the app.config. I could not get it working while initializing it in code. The InitializeDatabase is called, but the following condition is never true:
(!context.Database.Exists() || !context.Database.ModelMatchesDatabase())
This is because this happens after the migrations have been run...
I also tried this at one stage: Unique Constraint in Entity Framework Code First, it was the same problem as before, this condition was never returning true.
Ideally, I would like to include some standard SQL in my migration file. Is there a way to do that? If not, where can I see how to achieve this with using code first migrations?
Thanks!
UPDATE:
Is there any reason why I can't use the SQL function?
public override void Up()
{
AddColumn("Posts", "Abstract", c => c.String());
Sql("UPDATE Posts SET Abstract = LEFT(Content, 100) WHERE Abstract IS NULL");
}
Obviously using the correct SQL...
With code first migrations, I've just used this in the Up() method to add a unique index on a single column:
CreateIndex(table: "Organisations",
column: "Name",
unique: true, // unique index
name: "MyIndex");
...and then in the Down() method:
DropIndex(table: "Organisations",
name: "MyIndex");
Is that what you're after?
Since EF 6.1 you can now do this in your model with either
an attribute
[Index("IX_UniqueName", IsUnique = true)]
public string Name {get;set;}
or fluent
Property(s => s.Name).HasColumnAnnotation(IndexAnnotation.AnnotationName,
new IndexAnnotation(
new IndexAttribute("IX_UniqueName") { IsUnique = true }));
The fluent method isn't perfect as its crazy verbose IMO but at least its possible now.
More deets on Arthur Vickers blog http://blog.oneunicorn.com/2014/02/15/ef-6-1-creating-indexes-with-indexattribute/
You can just scaffold a new migration using the Add-Migration command from your package manager console. Once the empty migration is created, in the Up method, you can use the CreateIndex method of the DbMigration class to create your new index. It would look like something like this: CreateIndex("dbo.Accounts", "AccessKey", unique: true);

Entity Framework RIA Services How do I include entities within an included entity

I have the following hierarchy:
AccountCatagory
AccountType
AccoungGroup
GeneralLedger
SubsidiaryLedger
so each AccountCatagory has AccountTypes, each AccountType has AccountGroups...
Which needs to be loaded in to a tree view:
I need to load everything when the account category is loaded and I wrote it like this:
public IQueryable<AccountCatagory> GetAccountCatagories()
{
return this.ObjectContext.AccountCatagories.Include("AccountTypes");
}
Which works fine but only loads AccountTypes. within each AccountCatagory.
Writing an Include on each of the entities GetQuery doesn’t work.
How do I tell RIA services to Include entity when the Included entity also has an include/entity to load?
Here is an example of the way I got mine to work, assuming that all of your foreign keys are set up properly and the proper changes have been made in the metadata file of your Domain Service:
return ObjectContext.Users.Include("AccessRole")
.Include("AccessRole.AccessRoleReports")
I have never tried including objects with so many subtypes, but I assume this may work:
return this.ObjectContext.AccountCatagories.Include("AccountTypes")
.Include("AccountTypes.AccountGroups")
.Include("AccountTypes.AccountGroups.GeneralLedger")
.Include("AccountTypes.AccountGroups.GeneralLedger.SubsidiaryLedger");

EF Code first problem - related entity not loaded

this is really annoying
I have something like this:
class Person {
..properties id, name etc..
}
class Task {
..properties id, name etc..
Person Moderator {get;set}
}
public class DataModel : DbContext {
public DbSet<Task> Tasks { get; set; }
public DbSet<Person> People { get; set; }
}
I can then create new tasks and add People objects to the task and save, and I can see the data correctly saved in the sql backend - each Task saved has the correct Person id saved
with it and the Person with that id is saved back as well.
But when I try and get back a task, the person object is always null.
using (DataModel db = new DataModel()) {
Task t = db.Tasks.SingleOrDefault(p => p.Id == 22);
assert(t.Name.Lenght>0)
assert(t.Moderator != null) // always null!!!!!!
....
}
What do I have to do to get the whole object graph bought back? Do I have to do a join in the SingleorDefault call? seems a bit wrong somehow.
Did I mention this is really annoying.
TIA,
Two options for you. By default the code first / dbContext model returns a proxy object that derives from your model (this is important to understand when you run into JSON serialization issues). The proxy object uses lazy loading of associations but only under certain circumstances. The Moderator property has to be declared as virtual so that the proxy can override it and do the lazy loading for you.
However lazy loading can create a problem called Select N+1. If in most cases you only need the Task and not the Moderator, this won't be a problem. However if you frequently display a list of tasks and their associated moderators, you will effectively have to run an extra round trip to the database for every task in that list in addition to the 1 for the original list(e.g. for a list of 100 tasks you would do 101 queries to display the tasks and their moderators).
To get around this, EF provides the Include operator, this forces the relation to load. Use it as such
Task t = db.Tasks.Include(t=>t.Moderator).SingleOrDefault(p => p.Id ==
22);
Hope this helps.
You have lazy loading turned off for your Moderator property, so it will only be loaded if you explicitly do so using Load().
You can force EF to eagerly load your related Person entity by using the Include() method in your query like this:
Task t = db.Tasks.Include(x => x.Moderator).SingleOrDefault(p => p.Id == 22)
There is a pretty good overview in this article.

Preventing Validation in Entity Framework 4

I'm using Entity Framework 4 and a Dynamic Data site to expose a bare-bones admin interface to a few users. Working pretty well in general, but I have run into this one problem on a couple of fields on my model.
Several tables have some audit-related fields - CreatedBy, CreatedDate, ModifiedBy, and ModifiedDate. These fields are required in the database and the associated models are marking the properties as non-nullable (all as it should be). However I am handing setting the values for these fields in code - the field templates for the field types mark these specific fields as disabled on the page, and in the SavingChanges event I set these fields to the appropriate values. All works great when I'm updating an existing item.
The problem comes in when I try to create a new item. I want these fields to remain empty on the page and be auto-populated by my code when submitted, but the Field Templates set up RequiredFieldValidators for these fields and won't let me submit them without a value. Normally this would be great, except that I want to prevent EF from validating these fields at the point of page submission.
I realize that I could mark the fields as nullable in the database and that would resolve the issue - it would probably even be just fine from the data standpoint, but I'm not comfortable with doing so - for one thing it's not unlikely that some of the models these fields appear on will be bulk loaded, possibly by someone else, at a later date. I would rather still have the database enforce the non-nullability of these fields. In the field templates I've tried moving the built-in SetUpValidator() call for the RequiredFieldValidator not to run when these specific fields are being loaded, and I've also tried disabling the RequiredFieldValidators and forcing their IsValid property to true. None of these actions allows me to submit the page.
Is there a way to tell EF/Dynamic Data to skip the validation for some fields?
EDIT
As noted below, I also tried marking them nullable in the model and not in the database, which caused an error: Problem in mapping fragments...Non-nullable column...in table...is mapped to a nullable entity property.
EDIT #2
I have found a solution that works, but requires modifying the auto-generated designer file for the entity set, which is fragile at best. I would love to know a "righter" way to do it, but if nothing becomes apparent in the next couple of days I'll post my own answer.
So here are the edits I found I had to make. When allowing the tool to create the entities in the edmx Designer.cs file I get properties like these:
for a datetime on the server side
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)]
[DataMemberAttribute()]
public global::System.DateTime CreatedDate
{
get
{
return _CreatedDate;
}
set
{
OnCreatedDateChanging(value);
ReportPropertyChanging("CreatedDate");
_CreatedDate = StructuralObject.SetValidValue(value);
ReportPropertyChanged("CreatedDate");
OnCreatedDateChanged();
}
}
for a varchar
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)]
[DataMemberAttribute()]
public global::System.String CreatedBy
{
get
{
return _CreatedBy;
}
set
{
OnCreatedByChanging(value);
ReportPropertyChanging("CreatedBy");
_CreatedBy = StructuralObject.SetValidValue(value, false);
ReportPropertyChanged("CreatedBy");
OnCreatedByChanged();
}
}
To make it work without validation for a DateTime property setting the IsNullable parameter of the EdmScalarPropertyAttribute to true is sufficient to avoid the issue. For the String property you also have to change the 2nd parameter of the SetValidValue method call to "true."
All of this said, the only reason that I'm leaving this as it is is because I don't expect to have to regenerated the entities more than once or twice before we move to a different platform for this site. And in this case, merging the version in I have checked in to git with the version generated by the tool allows me to avoid most of the headaches,
Here is my meta information for a read-only auto generated date field. I don't get validation controls validating these fields. Hope this helps.
[ReadOnly(true)]
[DataType(DataType.Date)]
[Column(IsDbGenerated = true, UpdateCheck = UpdateCheck.Never, AutoSync = AutoSync.Never)]
[UIHint("DateTime")]
[Display(Name = "Modified", Order = 1000)]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:d}")]
public object DateModified { get; private set; }