EF 4.1 DbContextGenerator object names - can they be changed? - entity-framework

I am using DB First EF 4.1 and I am adding DbContextGenerator tt template to my model. This is all great, but I end up with classes like this:
public partial class t_city
{
public t_city()
{
this.t_neighborhood = new HashSet<t_neighborhood>();
}
public int city_id { get; set; }
public string city_name { get; set; }
public virtual ICollection<t_neighborhood> t_neighborhood { get; set; }
}
This is super ugly. I modified the template to generate properties in camelcase, but that breaks the mapping onto tables and columns. Is there way to get clean class names and still preserve the mapping?
EDIT
Looks like it's possible by renaming the objects inside the Entity Model file. The only question remains, is it possible to automate the renaming using a function, or does it have to be done manually each time?
Thanks!

You need to do it manually but that's needed only once for each entity / property. These changes are not deleted when you update your model from the database.
The only automation can be implemented as some processing of EDMX file. It is XML with defined schema so you can process that XML in your custom tool or XSLT transformation and automatically change property and entity names in CSDL and MSL.

Related

Entity Framework Core loading self referencing results recursively

I have a table like that,
public class Asset
{
public int id_asset { get; set; }
public int id_user { get; set; }
public int id_parent_asset { get; set; }
public string name { get; set; }
public virtual Asset Parent_Asset { get; set; }
public virtual ICollection<Asset> Child_Assets { get; set; }
public virtual User User { get; set; }
}
I want to get all children assets of an asset. I tried following code,
context.Asset.Where(o => o.name.Contains("Root")).Include(o => o.Child_Assets ).FirstOrDefault();
But it just contains one level children. I need to get all levels. I don't know that how many levels I should get so I need a recursive method or maybe there is already a method that is doing that.
Is there anyone who knows how can I handle this issue.
I had the same problem and was able to solve it using Lazy Loading instead of Eager loading. That may or may not be suitable for you.
With Lazy Loading you don't need to specify which navigation properties to load when you query the database (No need for the .Include calls). EF Core will automagically load in the related entities for you as you access them. ie get the root asset from the context and it's children will be there.
To use lazy loading you need to add Microsoft.EntityFrameworkCore.Proxies nuget package and then call .UseLazyLoadingProxies() when you configure the context. See the Docs for details https://learn.microsoft.com/en-us/ef/core/querying/related-data#lazy-loading
Also note that for the magic to happen the context that loaded the entity needs to know about the related entities (the child assets in your case). It won't go query the database for the child assets but if it has them in memory it will link them up. So in your case you will need to load all the assets then get the root asset and its children and their children will be there.
If you are using multiple contexts you can run into issues. Say the assets are loaded into one context and the users into another context, in that case root.User will be null because the context used to load the root asset doesn't have the users in memory.

Breeze: How to make use of entity constructor code on server?

The question could also look like "Why is my initialization code of object in server not working?".
For example,
public class Order
{
public int Id { get; set; }
public int Quantity { get; set; }
public Order()
{
Quantity = 10;
}
}
From debugger, I can see the contructor is called and Quantity is set, however, it is not taking effect. I have to set Quantity on client side after the entity is created to make it work.
Is there a way to make the initialization on server work? By the way, my project is in Angular/Breeze/EF.
UPDATE: As I dig a little further, I believe, this is the general "issue" with Breeze that the server side change must be added to so-called entityInfo.OriginalValueMap, otherwise, its change is not saved. If true, how can work around this limitation because I have a lot default values I'd like to set on server?
This is how I create my entity:
var manager = new breeze.EntityManager("breeze/breeze");
manager.enableSaveQueuing(true);
function _createEntity(entityName) {
return manager.createEntity(entityName);
}
Setting any initialization code on the server in the model constructor won't work simply because the JavaScript client doesn't know anything about the C# constructor code on the server.
The DefaultValueAttribute is only honored when you're constructing a Model-First metadata. It is unfortunately ignored by EF when constructing a Code-First model metadata.
I suggest that you see Breeze - Create Entity on Server side for how another user solves a similar situation by creating a "create Endpoint" on the server that basically returns a new entity with default values set.
You don't have to create a constructor to set default values.
Just add the default value data annotation to any property you wish to set its default value:
[DefaultValue(10)]
public int Quantity { get; set; }
Also, consider not to initialize the Quantity when creating an entity at the client side.

How can I use Entity Framework to use an existing database, and how can I use migrations to switch between DB versions?

Problem:
You have an existing database that you want to use with EntityFrameworks so that you can make database changes via classes. You also want to be able to use the migration features to switch between versions of the database.
Unfortunately this doesn't work out of the box with an existing database, but you can use the Reverse POCO generator to reverse-engineer your db as if you wrote it from scratch.
Hopefully my step-by-step solution will benefit others, I'm sure I'll be referring to it again in 6 months.
Create new project called MyEF (class library project)
Install the EntityFramework Reverse POCO generator either from here, or within Visual Studio menu Tools|Extensions and Updates menu. select Online|Visual Studio Gallery|Templates|Visual C#|Database, install the EntityFramework Reverse POCO generator.
Using Package Manager console, install EntityFramework by Install-Package EntityFramework
Add a new C# item called MyDB.tt using the template: EntityFramework Rever POCO Code First Generator
Add a connectionStrings section to the app.Config class and point to your database.
e.g.
<connectionStrings>
<add name="DefaultConnection" connectionString="Data Source=CP8;Initial Catalog=TestDB;Integrated Security=True;Connect Timeout=15;Encrypt=False;TrustServerCertificate=False;" providerName="System.Data.SqlClient" />
</connectionStrings>
In the file MyDB.tt, change "MyDbContext" to "DefaultConnection" or whatever your connection name is. Save the TT file and the Reverse POCO generator will reverse engineer the database and build your code-first classs for you.
If your database has a _MigrationHistory table, delete it!
In the package manager console (PMC) issue the following
Enable-Migrations –EnableAutomaticMigrations -Force
add-migration Initial
Go to your migrations folder and replace the contents of your XXX_Initial.cs class with
namespace MyEf.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class Initial : DbMigration
{
public override void Up()
{
}
public override void Down()
{
}
}
}
From the PMC, issue: update-database
Change your Model by editing the adding a new property in MyDb.cs to the MyTable class:
// MyTable
public class MyTable
{
public int Id { get; set; } // Id (Primary key)
public string Name { get; set; } // Name
public string Sex { get; set; } // Sex
public int? Age { get; set; } // Age
public bool AmIAwesomeOrWhat { get; set; }
}
Rebuild the solution
Issue a command to save your new migration, and update the database
add-migration MyNewProperty
update-database
The following commands will switch to whatever Migration you want.
update-database -targetmigration:Initial
update-database -targetmigration:MyNewProperty

Breeze Expand not working on WebAPI with EF

I have published a WebAPI service which returns a list of items. I am implementing Breeze and have managed to get it basically working with filtering/sorting. However, the Expand is not working.
http://www.ftter.com/desktopmodules/framework/api/dare/dares?$expand=ToUser
You can see the ToUserId ForeignKey in the response above, but the ToUser properties are NULL (the user definitely exists)
You can see the ToUser EF navigation property in the metadata.
When I use .Include on the server side I can populate it with EF, but I don't want to do this.
I checked the Breeze Tutorial 2 here on Expand: http://learn.breezejs.com/
Here is it without expand: http://learn.breezejs.com/api/northwind/Products
and here it is with Expand (And you can see the additional Category info): http://learn.breezejs.com/api/northwind/Products?$expand=Category
This is what I am trying to do but mine does not fill it...
UPDATE:
I downloaded the Breeze 1.3.6 Samples and loaded the DocCode solution in VS2011.
I ran it and saw that the client-side expand works;
e.g.
http://localhost:47595/breeze/Northwind/Orders?$top=1 (no expand)
http://localhost:47595/breeze/Northwind/Orders?$top=1&$expand=Customer (expands customer correctly).
I checked the WebAPI controller code and it looks the same, except they use EF Code First instead of Model First. The Foreign key is decorated with a property:
Breeze Sample that Works
[ForeignKey("CustomerID")]
[InverseProperty("Orders")]
public Customer Customer {get; set;}
It just doesn't make sense... it is something to do with my WebAPI controller or EntityFramework relationship...
UPDATE 2
I downloaded the most basic ToDo Knockout Breeze sample and added this line to the ToDoItem class: public User ToUser { get; set; }
I am then able to Expand the WebAPI call with http://localhost:63030/breeze/todos/Todos?$expand=ToUser
So I have come to the conclusion that it is something to do with the fact that I am using EntityFramework DB First and not Code First. It definitely does seem possible to do in the current version of the WebAPI with Breeze and EF.
UPDATE 3
I have narrowed it down to my database, EF Database First and Code First differences, but still not identified the issue. I have changed from a Model to a Code First approach with the exact same result (ie. no expand).
For example: if you look at this Expand on the Breeze site that works, http://learn.breezejs.com/api/northwind/Products?%24expand=Category, try change the last param to an invalid field and it throws an error, e.g. :
http://learn.breezejs.com/api/northwind/Products?%24expand=Category1
However, in my code, it always ignores this param and returns ALL the records, and never throws an exception if the Expand param is incorrect:
http://www.ftter.com/desktopmodules/framework/api/dare/dares?$expand=To4657657User
Hence I am stumped.. I have no idea why this is not working.
My Code
[HttpGet]
[Queryable(AllowedQueryOptions = AllowedQueryOptions.All)]
public HttpResponseMessage Dares()
{
var response = Request.CreateResponse(HttpStatusCode.OK, (IQueryable<Dare>)contextProvider.Context.Dares);
return ControllerUtilities.GetResponseWithCorsHeader(response);
}
and here is the generated class from my EF model (using Database First)
public partial class Dare
{
public int DareId { get; set; }
public int ToUserId { get; set; }
public virtual User ToUser { get; set; }
}
Your URL seems to be missing the $ for the expand query option...should be $expand.
I think I have found the problem - the IQueryable with the HttpResponseMessage return type does not behave the same as a pure IQueryable return type. expand seems to work when I do not wrap it.
I have raised a new question here:
How to use Breeze IQueryable with CORS?

Can I get decode an EntityFramework Model from a specified migration?

Apparently IMigrationMetadata.Target encodes the state of the EF model.
Can I use this to reconstruct the model for a particular migration?
Yes, it is possible. I was myself curious what exactly those magic resource strings were storing. By digging into the Entity Framework source (see the DbMigrator.GetLastModel() method), I found out that the IMigrationMetadata.Target just stores a base-64 string containing gzipped XML data. To test this, I created a new console application containing a simple code-first model defined as follows:
public class ContactContext : DbContext
{
public virtual IDbSet<Contact> Contacts { get; set; }
}
public class Contact
{
public int Id {get; set;}
public string FirstName { get; set; }
public string LastName { get; set; }
}
Then I created a migration using the NuGet Package Manager Console:
PM> Enable-Migrations
PM> Add-Migration MyMigration
Next I added the following code to my application's Main() method to decode the value in that string and dump it to the console:
var migration = new MyMigration();
var metadata = (IMigrationMetadata)migration;
var compressedBytes = Convert.FromBase64String(metadata.Target);
var memoryStream = new MemoryStream(compressedBytes);
var gzip = new GZipStream(memoryStream, CompressionMode.Decompress);
var reader = new StreamReader(gzip);
Console.WriteLine(reader.ReadToEnd());
This outputs an EDMX file representing the Entity Data Model associated with my DbContext that created the migration. If I write this output to a file with the .edmx extension, I'm able to open it with Visual Studio and view it in the Entity Designer.
Then if for some reason I wanted to regenerate the DbContext and entity classes that produced the model, I would need only do the following:
Add the .edmx file to a Visual Studio project.
Install the EF 5.x DbContext Generator for C# if I don't already have it.
Add the related T4 templates by selecting Add -> New Item from project node context menu.
Modify the newly added .tt files, replacing $edmxInputFile$ with the name of my .edmx file.
Watch as the two templates magically regenerate my code-first types to their respective .cs files.
Hope that answers your question! :-D
I created a small console app to export EDMX from the Model column of the __MigrationHistory table https://github.com/andreydil/EfMigrationModelDecoder
You can choose specific migration using /migration parameter, i.e:
EfMigrationModelDecoder.Cli.exe "<connectionString here>" /migration:Init
I created a PowerShell script to extract the latest migration from a DB to a edmx-file.
https://gist.github.com/otto-gebb/93d021c8fd300646dba0073a77585a94
You can also use SQL...
SELECT CONVERT(xml, DECOMPRESS(Model)) FROM [dbo].[__MigrationHistory] WHERE MigrationId = 'NameOfMigration'