I've got an application with a working Entity model generated from an existing database. I have to point my application at a new database, with the same schema, except that the table and column names are different.
For example, my current schema has tables named like "Answer". My new schema that I need to point to has the exact same table, except it is named "tblAnswer".
My columns have also changed. Where as a column used to be called "AnswerId", it's now "zAnswerId". Don't ask about the "z" prefix, it's a long story, but it's on every column.
So, what options do I have to point this existing Entity Model (generated from the database) to a new database and adjust the mappings? I've been experimenting with some of the techniques that are used for "Code First" mappings, as outlined in this guide, but haven't had any luck. I simply don't know if this is the right approach, or if there is something that makes more sense.
Suggestions? Thanks in advance.
You can change the database in the web.config file.
Use data annotations to use the different table and column names.
For example:
[Table("tblAnswer")]
class Answer
{
[Column("zAnswerId")]
public int AnswerId { get; set; }
}
Related
I have an application using EF as ORM. The database used to have one schema, dbo and everything was working fine. I recently organized my tables into 4 different schemas. Some tables of one schema have dependencies on tables that reside on a different schema. All seems to be valid on the SQL side.
On the app side all db interactions through EF are not working anymore. The code compiles, the schemas are visible in the solution, the model mappings point to the right schemas, but once I try to insert a row to a table it does not work.
I have seen a few posts about using multiple schemas will require using multiple DBContexts but I would rather use one DBContext. All my schemas have the same owner dbo and I do not see a reason of using multiple DBContexts.
Does anyone know if there is a way to achieve this?
You can map each table to its own schema by fluent mapping only. In your DbContext subtype you should override OnModelCreating (if you haven't done so already) and add statements like this:
modelBuilder.Entity<Department>()
.ToTable("t_Department", "school");
Entities that you don't map like this explicitly will be placed in the default dbo schema, or you can provide your own default by
modelBuilder.HasDefaultSchema("sales");
(summarized from here)
In addition to the responce of Gert Arnold, you can also use Table attribute in your entity:
using System.ComponentModel.DataAnnotations.Schema;
[Table("t_Department", Schema = "school")]
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
}
#GertArnold is spot on with his answer. However for pure syntactic candy you can also do this via a convention to pull the schema from the namespace of your models. We found this useful dealing with multiple schemas
modelBuilder.Types().Configure(e => {
var schema = e.ClrType.Namespace.Split('.').Last().ToLower();
var name = entity.ClrType.Name;
return entity.ToTable(name, schema);
});
the above will take the final component of the namespace and use it as the schema name. This avoids the need for customising the table binding for every entity.
In my case, it's possibly that I use DB First to generate my EDMX file, so it doesn't invoke OnModelCreating method.
I finally remove all store:Schema="YourSchema" and Schema="YourSchema" in EDMX file, and I do it by write a bat file with powershell command as below, and execute the bat in the Projec Pre-Build Event:
powershell -Command "$varStr='store:Schema=""abcd""""'; $filePath='%~dp0SomeFolder\SomeFile.edmx'; (gc $filePath) -replace $varStr, '' | Out-File $filePath"
I am experimenting with a Web API 2 project in Visual Studio 2012. I used the code first from existing DB option with EF6 to select one table and one view. I then tried to create a controller for the simple table using the profile for Web API 2 OData. The scaffolding of the controller fails telling me that "the item with identity 'Client Last Reveiwed On' already exists in the metadata collection". The problem is not only am I sure that field is unique for this project but that field is part of the view and not the table. Below is the model generated for the simple table (t_Client) that I was trying to create the controller for. As you can see the offending column is not part of the class. I will add below the definition for the column that VS/EF doesn't like which is in the class for the view.
Any ideas why this won't work?
Partial Public Class t_Client
<Key>
<DatabaseGenerated(DatabaseGeneratedOption.None)>
Public Property ClientID As Integer
<Required>
<StringLength(255)>
Public Property ClientName As String
Public Property isActive As Boolean
End Class
Here is the column that is defined in a separate view.
<Column("Client Last Reviewed On", TypeName:="date")>
Public Property Client_Last_Reviewed_On As Date?
I am not sure which of these steps fixed the issue but here are some notes on the topic.
Removing references to the model based on the SQL view eliminated errors.
I went into SQL and updated the view to contain a row number column.
Even with the row number column, EF tagged multiple columns as the key.
I manually edited the model to make the row number column the key.
I also had to update a cast the data type of a few columns in the SQL view to match reality, mainly bigint that was really just integer.
My guess is the fix was the well defined key.
I have a legacy database with a particular table -- I will call it ItemTable -- that can have billions of rows of data. To overcome database restrictions, we have decided to split the table into "silos" whenever the number of rows reaches 100,000,000. So, ItemTable will exist, then a procedure will run in the middle of the night to check the number of rows. If numberOfRows is > 100,000,000 then silo1_ItemTable will be created. Any Items added to the database from now on will be added to silo1_ItemTable (until it grows to big, then silo2_ItemTable will exist...)
ItemTable and silo1_ItemTable can be mapped to the same Item entity because the table structures are identical, but I am not sure how to set this mapping up at runtime, or how to specify the table name for my queries. All inserts should be added to the latest siloX_ItemTable, and all Reads should be from a specified siloX_ItemTable.
I have a separate siloTracker table that will give me the table name to insert/read the data from, but I am not sure how I can use this with entity framework...
Thoughts?
You could try to use the Entity Inheritance to get this. So you have a base class which has all the fields mapped to ItemTable and then you have descendant classes that inherit from ItemTable entity and is mapped to the silo tables in the db. Every time you create a new silo you create a new entity mapped to that silo table.
[Table("ItemTable")]
public class Item
{
//All the fields in the table goes here
}
[Table("silo1_ItemTable")]
public class Silo1Item : Item
{
}
[Table("silo2_ItemTable")]
public class Silo2Item : Item
{
}
You can find more information on this here
Other option is to create a view that creates a union of all those table and map your entity to that view.
As mentioned in my comment, to solve this problem I am using the SQLQuery method that is exposed by DBSet. Since all my item tables have the exact same schema, I can use the SQLQuery to define my own query and I can pass in the name of the table to the query. Tested on my system and it is working well.
See this link for an explanation of running raw queries with entity framework:
EF raw query documentation
If anyone has a better way to solve my question, please leave a comment.
[UPDATE]
I agree that stored procedures are also a great option, but for some reason my management is very resistant to make any changes to our database. It is easier for me (and our customers) to put the sql in code and acknowledge the fact that there is raw sql. At least I can hide it from the other layers rather easily.
[/UPDATE]
Possible solution for this problem may be using context initialization with DbCompiledModel param:
var builder = new DbModelBuilder(DbModelBuilderVersion.V6_0);
builder.Configurations.Add(new EntityTypeConfiguration<EntityName>());
builder.Entity<EntityName>().ToTable("TableNameDefinedInRuntime");
var dynamicContext = new MyDbContext(builder.Build(context.Database.Connection).Compile());
For some reason in EF6 it fails on second table request, but mapping inside context looks correct on the moment of execution.
Say my object has a Name field, and I wish to split it into FirstName and LastName fields. Or maybe it has an address string and I'm adding Lat and Lng fields that require geocoding. Etc etc.
I expected to have access to my DbContext in the Up() and Down() methods, but all I've been able to find (besides the builtin functions) is the .Sql() call. This is enough for adding and removing columns, but not for transforming existing data into new formats.
Is it safe to reference my DbContext inside an Up() invocation? Or is there another recommended pattern for implement migrations that require more than trivial SQL?
No you cannot use DbContext inside Up method because it already refers new model but your database still targets the old model.
Edit:
All data migrations must be done through Sql. You can for example create temporary table, move old data to temporary table, use migration of table structure and move data from temporary table back to the original with using some transformation directly in SQL - splitting varchar values should not be a big deal.
Rather than trying to split the Name into two different fields, rethink your migration. Sometimes it might be best staged. I can think of two ways to perform your transformation.
Migration path #1: New Fields, then Delete old
Create migration for new field for FirstName and LastName, and in the Up() method, you still have the Name field, split it, insert into First and Last fields.
Create another migration to remove the old Name field.
Migration path #2: Repurpose and Rename
Create a migration adding the LastName field, and renaming Name to FirstName, move the last name data, modify the renamed First/Name field to only hold the first name.
Both paths have advantages and disadvantages. And regardless of the complexity of your transformation, you should be able to break it out into logical stages to accomplish the goal.
I have a method for inserting in database.
I have 1:n relationship between the tables Point (n) and Line (1). Point has foreign key idLine.
However, the class Point in Entity Framework doesn't have the idLine property.
Now, in my method I have object of type Point, as parameter and here I have a problem because I don't know to what to assign the idSection of the new Point object which is inserted in the table.
How to add the idLine property in the Point class?
I'll assume you're using EF4.0, since this is not possible in previous version (EF1.0 does not include foreigh key properties in model).
In your generation wizard you need to make sure that "Include foreign key columns in the model" checkbox is checked when you're generating data model. Otherwise foreign key columns will be omitted when generating data.
If I'm remembering correctly you cannot simply add forign keys to classes that are already generated. You would need to manually edit all the mappings (not impossible, might be little bit troublesome if you are new to EF). Or you can simply remove this class from design area and add it again (making sure appropriate checkbox is checked this time). This might be simpler if you haven't customized generated classes.
add a foreign key in your model like following :-
public int? LineId { get; set; }
public virtual LineId LineId {get; set; } // this for one to one