I use Entity Framework 6.2 Code First (.net framework 4.6.1) and I map few entities to view via Table Attribute. It works for select operations and I handle Insert/Update/Delete with writing trigger to view at sql server side. It works as expected, however when I add a new migration, Entity Framework generate RenameTable scripts for used Table Attribute (actuallyis expected behavior for EF). But I want to intercept migration generation and change these entities tableName to original name.
my code like;
[MapToView("Users","UsersView")]
public class User
{
...
}
I wrote MapToView Attribute, this attribute inherited by TableAttribute and pass to second parameter to TableAttribute. I create this Attribute because if I intercept migration generation, return original table name with this attribute parameters.
In this case when I run "add-migration migrationName" it creates migration scripts like this;
RenameTable(name: "dbo.Users", newName: "UsersView");
but i want to create empty migration when I run "add-migration migrationName" script.
anyone can help me?
I solve the problem.
First: Problem is; When I Map Entity to View EF Code-first generate migration with ViewName. This is problem because I want to use View Instead of Table. So I solve problem with this instructions;
1- I Create BaseEntityConfiguration that Inherited from EntityTypeConfiguration and all entity configuration classes are inherited by.
for example:
public class UserConfig: BaseEntityConfiguration<User> //Generic Type is Entity
{
public UserConfig()
{
}
}
2- I Create MapToViewAttribute that inherited by TableAttribute
public class MapToViewAttribute : TableAttribute
{
public string TableName { get; }
public string ViewName { get; }
public MapToViewAttribute(string tableName, string viewName) : base(viewName)
{
TableName = tableName;
ViewName = viewName;
}
}
3- I Use MapToViewAttribute for example User Entity to use View.
[MapToView("User","UserView")]
public class User
{
...
}
And in BaseEntityConfiguration's Constructor I Get Generic Type and custom attributes. If any entity has MapToView Attribute, I pass to TableName parameter to ToTable Method. So at runtime EF uses View for these entities but doesn't create migration with RenameTable for these entities.
protected BaseEntityConfiguration()
{
var baseType = typeof(TEntityType);
var attributes = baseType.GetCustomAttributes(true);
foreach (var attr in attributes)
{
if (attr.GetType() == typeof(MapToViewAttribute))
{
var tableName = ((MapToViewAttribute)attr).TableName;
ToTable(tableName);
}
}
}
Last EF don't use your configuration files, so you must tell the EF to use this in DbContext class's InternalModelCreate method.
My implementation like this;
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var typesToRegister = Assembly.GetExecutingAssembly()
.GetTypes().Where(IsConfigurationType);
foreach (var type in typesToRegister)
{
dynamic configurationInstance = type.BaseType != null
&& type.BaseType.IsGenericType
&& type.BaseType.GetGenericTypeDefinition() == typeof(BaseEntityConfiguration<>)
? Activator.CreateInstance(type, culture)
: Activator.CreateInstance(type);
modelBuilder.Configurations.Add(configurationInstance);
}
modelBuilder.Types().Configure(t => t.ToTable(t.ClrType.Name));
BaseDbContext.InternalModelCreate(modelBuilder);
}
But if you use this approach you must create Insert, Update and Delete Triggers/Rule (if you use SQLServer trigger is an option but if you use postgresql rule is better option) because EF uses this view for insert, update and delete operations.
Related
I'm using EF6 code first to create my database. I understand the syntax, the DbContext, and the modelbuilder. I use LINQ for several exhaustive queries and everything works fine.
But now I need to do something that can't be done in one query using linq. I need to perform a Merge statement using a stored procedure.
I've seen several questions on how to create a stored procedure, like:
Create Stored Procedures using Entity Framework Code First?
Most answers are talking about creating a derived class for DbMigrations and overriding the Up() function. I understand what I should write in the Up function to make sure the stored procedure is created.
But what should I do to make that during database creation this Up function is called?
Should I do something in DbContext.OnModelCreating?
I don't think I should instantiate the subclass of DbMigrations and call Up().
The link mentioned above is talking about "Open the Package Manager Control". What is that? Or do you really use this method when migrating from an older version to a newer one?
After some investigation I found how to make sure that a stored procedure is created whenever the database is created.. I found two methods, each with their own advantages and disadvantages. Hence I describe them both. Sorry if this makse the answer fairly long.
The two methods described here are:
Create a DataBase Initializer, a class that implements IDataBaseInitializer. This will probably be a class derived from DropCreateDatabaseIfModelChanges or similar. Override the Seed function and create in this function the stored procedure using context.Database.ExecuteSqlCommand(...).
Use Entity Framework migrations for the creation of stored procedures.
The first method is simpler. Whenever the database is created, the Seed is called and the stored procedure is created. However this method has the disadvantage that whenever the name or the type of the parameters of the stored procedure change, this is not detected until runtime.
The DbMigration method matches the parameters of the stored procedure using a lambda expression, so whenever the type or the name of the parameter changes, the compiler detects if the definition of the remote procedure matches the parameter.
I'll describe both methods. Both examples have the same simple Hello World! procedure and a big Merge procedure with a lot of parameters.
The definition of the merge statement is not really important. What it
does is that it checks if there is already a record matching several
properties, and if so it adds costs to the existing costs. If not it
creates a record and initializes the costs with costs. This is a
typical example where using linq statement and IQueryable wouldn't suffice.
Using linq, one would have to retrieve the record, update it and call
SaveChanges, with the problems (1) that in
the meantime someone else might have added a value and (2) it needs at
least two roundtrips. Hence the need for a stored procedure.
Method IDatabaseInitializer
In your project you create the entity classes and a class derived form DbContext with DbSet properties for the database tables you want to access.
For example:
public class UsageCosts
{
public int Id {get; set; }
public DateTime InvoicePeriod { get; set; }
public long CustomerContractId { get; set; }
public string TypeA { get; set; }
public string TypeB { get; set; }
public decimal VatValue { get; set; }
// the value to invoice
public decimal PurchaseCosts { get; set; }
public decimal RetailCosts { get; set; }
}
public class DemoContext : DbContext
{
public DemoContext(string nameOrConnectionString) : base(nameOrConnectionString) {}
public DbSet<UsageCosts> UsageCosts { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// add entity framework fluent api statements here
}
}
Apart from your database classes, create a database initializer, It has a function Seed that will be called when the database is created.
internal class DataBaseInitializer : DropCreateDatabaseIfModelChanges<DemoContext>
{
protected override void Seed(DemoContext context)
{
base.Seed(context);
// create stored procedures here
this.CreateStoredProcedureHelloWorld(context)
this.CreateStoredProcedureUpdateUsageCosts(context)
}
Simple example that shows how to create a Stored Procedure (Hello World!)
private void CreateStoredProcedureHelloWorld(DemoContext context)
{
context.Database.ExecuteSqlCommand("create procedure HelloWorld as begin Select 'Hello World' end;");
}
Create a stored procedure with input parameters:
private void CreateStoredProcedureUpdateUsageCosts(DemoContext context)
{
var x = new StringBuilder();
x.AppendLine(#"create procedure updateusagecosts");
x.AppendLine(#"#InvoicePeriod datetime,");
x.AppendLine(#"#CustomerContractId bigint,");
x.AppendLine(#"#TypeA nvarChar(80),");
x.AppendLine(#"#TypeB nvarChar(80),");
x.AppendLine(#"#VatValue decimal(18, 2),");
x.AppendLine(#"#PurchaseCosts decimal(18, 2),");
x.AppendLine(#"#RetailCosts decimal(18, 2)");
x.AppendLine(#"as");
x.AppendLine(#"begin");
x.AppendLine(#"Merge [usagecosts]");
x.AppendLine(#"Using (Select #InvoicePeriod as invoicePeriod,");
x.AppendLine(#" #CustomerContractId as customercontractId,");
x.AppendLine(#" #TypeA as typeA,");
x.AppendLine(#" #TypeB as typeB,");
x.AppendLine(#" #VatValue as vatvalue)");
x.AppendLine(#" As tmp ");
x.AppendLine(#"On ([usagecosts].[invoiceperiod] = tmp.invoiceperiod");
x.AppendLine(#"AND [usagecosts].[customercontractId] = tmp.customercontractid");
x.AppendLine(#"AND [usagecosts].[typeA] = tmp.typeA");
x.AppendLine(#"AND [usagecosts].[typeB] = tmp.typeB");
x.AppendLine(#"AND [usagecosts].[vatvalue] = tmp.Vatvalue)");
x.AppendLine(#"When Matched Then ");
x.AppendLine(#" Update Set [usagecosts].[purchasecosts] = [usagecosts].[purchasecosts] + #purchasecosts,");
x.AppendLine(#" [usagecosts].[retailcosts] = [usagecosts].[retailcosts] + #retailcosts");
x.AppendLine(#"When Not Matched Then");
x.AppendLine(#" Insert (InvoicePeriod, CustomerContractId, typea, typeb, vatvalue, purchasecosts, retailcosts)");
x.AppendLine(#" Values (#invoiceperiod, #CustomerContractId, #TypeA, #TypeB, #VatValue, #PurchaseCosts, #RetailCosts);");
x.AppendLine(#"end");
context.Database.ExecuteSqlCommand(x.ToString());
}
}
The hello world example can be found here on StackOverflow
The method with the StringBuilder can also be found somewhere on StackOverflow, but alas I can't find it.
During creation of the database DatabaseInitializer.Seed(...) is called. Here the context is ordered to perform an SQL statement. This statement is a string.
That's why the compiler won't notice changes in the name or the type of the parameters of the functions.
DbMigration methods
For migrations see:
MSDN: Enabling Migrations
Creating and Calling Stored Procedure from Entity Framework 6 Code First
The idea is to let the visual studio package manager create a derived class of DbManager which has an Up() function. This function will be called whenever the database is migrated upwards to the version for the derived class.
Inside the Up() you can call the base class DbMigration.CreateStoredProcedure. The nice thing about this method would be that the translation from Entity type to parameters is done using delegates (with a lambda expression) and thus checked at compile time: do the properties still exist and do they have the correct type?
Alas it is not enough to construct the derived class from DbMigration, and call the Up() functions from within your Seed() function.
To make sure that the Up() function is called it is the easiest to let visual studio do this.
Create your project
Add Nuget package for entity framework
Create your entity classes and your DbContext with DbSet properties for the entity classes
In visual studio start the Nuget Package Manager Console via the Tools menu
Using the Nuget Package Manager Console enable migrations using the command Enable-Migrations
Using the Nuget Package Manager Console add one migration and give if a name, for instance InitialCreation using the command add-Migration InitialCreation
You'll notice that several classes are added to your project.
Configuration derived from DbMigratinConfiguration with a function Seed()
InitialCreation derived from DbMigration with a function Up() (and a function Down(). In this Up you'll see one or more CreateTable functions
If you still have a database seeder class as described in the previous example, and you use DataBase.SetInitializer to initialize it, then whenever the database needs to be re-created the various Up() and Seed() functions are called in the following order:
Constructor of Configuration
InitialCreation.Up()
DatabaseSeeder.Seed()
For some reason Configuration.Seed() is not called.
This gives us the opportunity to create the stored procedures in InitialCraeation.Up()
public override void Up()
{
CreateTable("dbo.UsageCosts",
c => new
{
Id = c.Int(nullable: false, identity: true),
InvoicePeriod = c.DateTime(nullable: false),
CustomerContractId = c.Long(nullable: false),
TypeA = c.String(),
TypeB = c.String(),
VatValue = c.Decimal(nullable: false, precision: 18, scale: 2),
PurchaseCosts = c.Decimal(nullable: false, precision: 18, scale: 2),
RetailCosts = c.Decimal(nullable: false, precision: 18, scale: 2),
})
.PrimaryKey(t => t.Id);
}
The "Hello World" Stored procedure is created as follows:
base.CreateStoredProcedure("dbo.HelloWorld3", "begin Select 'Hello World' end;");
The stored procedure with input parameters:
base.CreateStoredProcedure("dbo.update2", p => new
{
InvoicePeriod = p.DateTime(),
CustomerContractId = p.Long(),
TypeA = p.String(maxLength: 80),
TypeB = p.String(maxLength: 80),
VatValue = p.Decimal(10, 8),
PurchaseCosts = p.Decimal(10, 8),
RetailCosts = p.Decimal(10, 8),
},
#"begin
Merge [usagecosts]
Using (Select
#InvoicePeriod as invoicePeriod,
#CustomerContractId as customercontractId,
#TypeA as typeA,
#TypeB as typeB,
#VatValue as vatvalue)
As tmp
On ([usagecosts].[invoiceperiod] = tmp.invoiceperiod
AND [usagecosts].[customercontractId] = tmp.customercontractid
AND [usagecosts].[typeA] = tmp.typeA
AND [usagecosts].[typeB] = tmp.typeB
AND [usagecosts].[vatvalue] = tmp.Vatvalue)
When Matched Then
Update Set [usagecosts].[purchasecosts] = [usagecosts].[purchasecosts] + #purchasecosts, [usagecosts].[retailcosts] = [usagecosts].[retailcosts] + #retailcosts
When Not Matched Then
Insert (InvoicePeriod, CustomerContractId, typea, typeb, vatvalue, purchasecosts, retailcosts)
Values (#invoiceperiod, #CustomerContractId, #TypeA, #TypeB, #VatValue, #PurchaseCosts, #RetailCosts);
end;");
}
Remember the Down() method:
public override void Down()
{
this.DropStoredProcedure("dbo.update2");
}
For completeness: the remote procedure call
using (var dbContext = new DemoContext())
{
object[] functionParameters = new object[]
{
new SqlParameter(#"InvoicePeriod", usageCosts.InvoicePeriod),
new SqlParameter(#"CustomerContractId", usageCosts.CustomerContractId),
new SqlParameter(#"TypeA", usageCosts.TypeA),
new SqlParameter(#"TypeB", usageCosts.TypeB),
new SqlParameter(#"VatValue", usageCosts.VatValue),
new SqlParameter(#"PurchaseCosts", 20M),
new SqlParameter(#"RetailCosts", 30M),
};
string sqlCommand = String.Format(#"Exec {0} #InvoicePeriod, #CustomerContractId, #TypeA, #TypeB, #VatValue, #PurchaseCosts, #RetailCosts", functionName);
dbContext.Database.ExecuteSqlCommand(sqlCommand, functionParameters);
dbContext.SaveChanges();
}
In my opinion it is best to put this in an extension method of the DbSet. Whenever the UsageCosts changes, the compiler can check for the names and the property types.
Is it possible to call a TVF in EF6 Code First?
I started a new project using EF6 Database first and EF was able to import a TVF into the model and call it just fine.
But updating the model became very time consuming and problematic with the large read-only db with no RI that I'm stuck dealing with.
So I tried to convert to EF6 code first using the Power Tools Reverse Engineering tool to generate a context and model classes.
Unfortunately the Reverse Engineering tool didn't import the TVFs.
Next I tried to copy the DBFunctions from my old Database First DbContext to the new Code First DbContext, but that gave me an error that my TVF:
"cannot be resolved into a valid type or function".
Is it possible to create a code first Fluent mapping for TVFs?
If not, is there a work-around?
I guess I could use SPs instead of TVFs, but was hoping I could use mostly TVFs to deal with the problematic DB I'm stuck with.
Thanks for any work-around ideas
This is now possible. I created a custom model convention which allows using store functions in CodeFirst in EF6.1. The convention is available on NuGet http://www.nuget.org/packages/EntityFramework.CodeFirstStoreFunctions. Here is the link to the blogpost containing all the details: http://blog.3d-logic.com/2014/04/09/support-for-store-functions-tvfs-and-stored-procs-in-entity-framework-6-1/
[Tested]
using:
Install-Package EntityFramework.CodeFirstStoreFunctions
Declare a class for output result:
public class MyCustomObject
{
[Key]
public int Id { get; set; }
public int Rank { get; set; }
}
Create a method in your DbContext class
[DbFunction("MyContextType", "SearchSomething")]
public virtual IQueryable<MyCustomObject> SearchSomething(string keywords)
{
var keywordsParam = new ObjectParameter("keywords", typeof(string))
{
Value = keywords
};
return (this as IObjectContextAdapter).ObjectContext
.CreateQuery<MyCustomObject>(
"MyContextType.SearchSomething(#keywords)", keywordsParam);
}
Add
public DbSet<MyCustomObject> SearchResults { get; set; }
to your DbContext class
Add in the overriden OnModelCreating method:
modelBuilder.Conventions.Add(new FunctionsConvention<MyContextType>("dbo"));
And now you can call/join with
a table values function like this:
CREATE FUNCTION SearchSomething
(
#keywords nvarchar(4000)
)
RETURNS TABLE
AS
RETURN
(SELECT KEY_TBL.RANK AS Rank, Id
FROM MyTable
LEFT JOIN freetexttable(MyTable , ([MyColumn1],[MyColumn2]), #keywords) AS KEY_TBL
ON MyTable.Id = KEY_TBL.[KEY]
WHERE KEY_TBL.RANK > 0
)
GO
I was able to access TVF with the code below. This works in EF6. The model property names have to match the database column names.
List<MyModel> data =
db.Database.SqlQuery<MyModel>(
"select * from dbo.my_function(#p1, #p2, #p3)",
new SqlParameter("#p1", new System.DateTime(2015,1,1)),
new SqlParameter("#p2", new System.DateTime(2015, 8, 1)),
new SqlParameter("#p3", 12))
.ToList();
I actually started looking into it in EF6.1 and have something that is working on nightly builds. Check this and this out.
I have developed a library for this functionality. You can review my article on
UserTableFunctionCodeFirst.
You can use your function without writing SQL query.
Update
First of all you have to add reference to the above mentioned library and then you have to create parameter class for your function. This class can contain any number and type of parameter
public class TestFunctionParams
{
[CodeFunctionAttributes.FunctionOrder(1)]
[CodeFunctionAttributes.Name("id")]
[CodeFunctionAttributes.ParameterType(System.Data.SqlDbType.Int)]
public int Id { get; set; }
}
Now you have to add following property in your DbContext to call function and map to the property.
[CodeFunctionAttributes.Schema("dbo")] // This is optional as it is set as dbo as default if not provided.
[CodeFunctionAttributes.Name("ufn_MyFunction")] // Name of function in database.
[CodeFunctionAttributes.ReturnTypes(typeof(Customer))]
public TableValueFunction<TestFunctionParams> CustomerFunction { get; set; }
Then you can call your function as below.
using (var db = new DataContext())
{
var funcParams = new TestFunctionParams() { Id = 1 };
var entity = db.CustomerFunction.ExecuteFunction(funcParams).ToList<Customer>();
}
This will call your user defined function and map to the entity.
I'm working with asp.net mvc3.
I have a edmx that was created ADO.NET Entity Data Model. (Data-First)
TestDb.Designer.cs
namespace Test.Web.DataModel
{
public partial class TestDbContext : ObjectContext
{
public ObjectSet<T_Members> T_Members { ... }
public ObjectSet<T_Documents> T_Documents { ... }
....
}
}
T_Members, T_Documents <-- This property is a table of the database.
I want to get a list of this table.
How to get the list of table name from EDMX?
Answer Myself.
TestDbContext context = new TestDbContext();
var tableList = context.MetadataWorkspace.GetItems<EntityType>(System.Data.Metadata.Edm.DataSpace.CSpace);
foreach (var item in tableList)
{
item.Name;
}
To be help for people who have the same problem ...
I think your 'solution' only works when the table and the entity have the same name.
If you would rename your entity to Document (without the prefix) it would fail.
Quote from Microsoft employee:
No, unfortunately it is impossible using the Metadata APIs to get to
the tablename for a given entity. This is because the Mapping metadata
is not public, so there is no way to go from C-Space to S-Space using
the EF's APIs.
I'm a little confused as to the purpose of a data model in Entity Framework code-first. Because EF will auto-generate a database from scratch for you if it doesn't already exist using nothing more than the data model (including data annotations and Fluent API stuff in DbContext.OnModelCreating), I was assuming that the data model should fully describe your database's structure, and you wouldn't need to modify anything fundamental after that.
However, I came across this Codeplex issue in which one of the EF Triage Team members suggests that custom indexes be added in data migrations, but not as annotations to your data model fields, or Fluent API code.
But wouldn't that mean that anyone auto-generating the database from scratch would not get those custom indexes added to their DB? The assumption seems to be that once you start using data migrations, you're never going to create the database from scratch again. What if you're working in a team and a new team member comes along with a new SQL Server install? Are you expected to copy over a database from another team member? What if you want to start using a new DBMS, like Postgres? I thought one of the cool things about EF was that it was DBMS-independent, but if you're no longer able to create the database from scratch, you can no longer do things in a DBMS-independent way.
For the reasons I outlined above, wouldn't adding custom indexes in a data migration but not in the data model be a bad idea? For that matter, wouldn't adding any DB structure changes in a migration but not in the data model be a bad idea?
Are EF code-first models intended to fully describe a database's structure?
No, they don't fully describe the database structure or schema.Still there are methods to make the database fully described using EF. They are as below:
You can use the new CTP5’s ExecuteSqlCommand method on Database class which allows raw SQL commands to be executed against the database.
The best place to invoke SqlCommand method for this purpose is inside a Seed method that has been overridden in a custom Initializer class. For example:
protected override void Seed(EntityMappingContext context)
{
context.Database.ExecuteSqlCommand("CREATE INDEX IX_NAME ON ...");
}
You can even add Unique Constraints this way.
It is not a workaround, but will be enforced as the database will be generated.
OR
If you are badly in need of the attribute, then here it goes
[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
public class IndexAttribute : Attribute
{
public IndexAttribute(string name, bool unique = false)
{
this.Name = name;
this.IsUnique = unique;
}
public string Name { get; private set; }
public bool IsUnique { get; private set; }
}
After this , you will have an initializer which you will call in your OnModelCreating method as below:
public class IndexInitializer<T> : IDatabaseInitializer<T> where T : DbContext
{
private const string CreateIndexQueryTemplate = "CREATE {unique} INDEX {indexName} ON {tableName} ({columnName});";
public void InitializeDatabase(T context)
{
const BindingFlags PublicInstance = BindingFlags.Public | BindingFlags.Instance;
Dictionary<IndexAttribute, List<string>> indexes = new Dictionary<IndexAttribute, List<string>>();
string query = string.Empty;
foreach (var dataSetProperty in typeof(T).GetProperties(PublicInstance).Where(p => p.PropertyType.Name == typeof(DbSet<>).Name))
{
var entityType = dataSetProperty.PropertyType.GetGenericArguments().Single();
TableAttribute[] tableAttributes = (TableAttribute[])entityType.GetCustomAttributes(typeof(TableAttribute), false);
indexes.Clear();
string tableName = tableAttributes.Length != 0 ? tableAttributes[0].Name : dataSetProperty.Name;
foreach (PropertyInfo property in entityType.GetProperties(PublicInstance))
{
IndexAttribute[] indexAttributes = (IndexAttribute[])property.GetCustomAttributes(typeof(IndexAttribute), false);
NotMappedAttribute[] notMappedAttributes = (NotMappedAttribute[])property.GetCustomAttributes(typeof(NotMappedAttribute), false);
if (indexAttributes.Length > 0 && notMappedAttributes.Length == 0)
{
ColumnAttribute[] columnAttributes = (ColumnAttribute[])property.GetCustomAttributes(typeof(ColumnAttribute), false);
foreach (IndexAttribute indexAttribute in indexAttributes)
{
if (!indexes.ContainsKey(indexAttribute))
{
indexes.Add(indexAttribute, new List<string>());
}
if (property.PropertyType.IsValueType || property.PropertyType == typeof(string))
{
string columnName = columnAttributes.Length != 0 ? columnAttributes[0].Name : property.Name;
indexes[indexAttribute].Add(columnName);
}
else
{
indexes[indexAttribute].Add(property.PropertyType.Name + "_" + GetKeyName(property.PropertyType));
}
}
}
}
foreach (IndexAttribute indexAttribute in indexes.Keys)
{
query += CreateIndexQueryTemplate.Replace("{indexName}", indexAttribute.Name)
.Replace("{tableName}", tableName)
.Replace("{columnName}", string.Join(", ", indexes[indexAttribute].ToArray()))
.Replace("{unique}", indexAttribute.IsUnique ? "UNIQUE" : string.Empty);
}
}
if (context.Database.CreateIfNotExists())
{
context.Database.ExecuteSqlCommand(query);
}
}
private string GetKeyName(Type type)
{
PropertyInfo[] propertyInfos = type.GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public);
foreach (PropertyInfo propertyInfo in propertyInfos)
{
if (propertyInfo.GetCustomAttribute(typeof(KeyAttribute), true) != null)
return propertyInfo.Name;
}
throw new Exception("No property was found with the attribute Key");
}
}
Then overload OnModelCreating in your DbContext
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.SetInitializer(new IndexInitializer<MyContext>());
base.OnModelCreating(modelBuilder);
}
Apply the index attribute to your Entity type, with this solution you can have multiple fields in the same index just use the same name and unique.
OR
You can do the migrations later on.
Note:
I have taken a lot of this code from here.
The question seems to be if there is value in having migrations added mid-stream, or if those will cause problems for future database initializations on different machines.
The initial migration that is created also contains the entire data model as it exists, so by adding migrations (enable-migrations in the Package Manager Console) you are, in effect, creating the built-in mechanism for your database to be properly created down the road for other developers.
If you're doing this, I do recommend modifying the database initialization strategy to run all your existing migrations, lest EF should start up and get the next dev's database out of sync.
Something like this would work:
Database.SetInitializer(new MigrateDatabaseToLatestVersion<YourNamespace.YourDataContext, Migrations.Configuration>());
So, no, this won't inherently introduce problems for future work/developers. Remember that migrations are just turned into valid SQL that executes against the database...you can even use script mode to output the TSQL required to make the DB modifications based on anything in the migrations you have created.
Cheers.
Using EF 4.1 how could I add a default value to the underlying table? In this particular case how could I set a datetime column to the equivalent of getdate every time I insert a new record to the database, without having to set it in code.
Thanks in advance
The solution proposed by #elkdanger is way to go but just if you use code-first approach you don't have to create partial class - you can place initialization directly to your entity.
Don't use database approach! It will not work because it would demand marking property as database generated (to be correctly repopulated after insert). Once you mark property database generated you can never change its value in the application.
The last option is overriding SaveChanges in your derived DbContext and setting the property manually. Something like:
public override int SaveChanges()
{
var entities = ChangeTracker.Entries<YourEntityType>()
.Where(e => e.State == EntityState.Added)
.Select(e => e.Entity);
var currentDate = DateTime.Now;
foreach(var entity in entities)
{
entity.Date = currentDate;
}
return base.SaveChanges();
}
This approach can be better if there can be significant difference between creating an instance of the entity and saving the instanance.
You could create a partial class for your entity, and inside the constructor set the date column to DateTime.Now. This way, every time you create an instance of your class, that field will be set to the current date "automatically".
You could (and perhaps should) do it in the table itself using a trigger or a default value.
Entity Framework itself has not a mechanism for it. You have to do it manually in the db or the code.
You can also modify your T4 template (.tt file) to add a partial method that you call from within the generated constructor. Then, you can create your own partial class and implement the partial method and set your default value.
A snippet from the T4 template where the constructor is created, followed by the partial method. Note the last three lines:
public <#=code.Escape(entity)#>()
{
<#
foreach (var edmProperty in propertiesWithDefaultValues)
{
#>
this.<#=code.Escape(edmProperty)#> = =code.CreateLiteral(edmProperty.DefaultValue)#>;
<#
}
foreach (var navigationProperty in collectionNavigationProperties)
{
#>
this.<#=code.Escape(navigationProperty)#> = new HashSet<<#=code.Escape(navigationProperty.ToEndMember.GetEntityType())#>>();
<#
}
foreach (var complexProperty in complexProperties)
{
#>
this.<#=code.Escape(complexProperty)#> = new <#=code.Escape(complexProperty.TypeUsage)#>();
<#
}
#>
SetDefaultValues();
}
partial void SetDefaultValues();
That will result in a generated entity having something like:
public Foo()
{
// Properties set based on defaults in edmx
SetDefaultValues();
}
partial void SetDefaultValues();
Then, in your partial class, you can simply add something like:
partial void SetDefaultValues()
{
this.SomeDate = DateTime.Today;
}
Use [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
from System.ComponentModel.DataAnnotations.Schema;
if you have the default values configured on the database.