Entity framework table names mapping at runtime - entity-framework

I need to import a huge amount of data without db work interruption. So there are two tables: Data and DataTemp(they are identical). At first data is uploaded to the temp table and then tables are swapped by backing up Data table and renaming DataTemp to Data(this example is simplified - there much more than two tables). Entity Framework is used in this project.
So the question is: is it possible to use entity framework to use DataTemp without duplicating the Data table in the schema? Is there a way to edit final sql before executing it to temporary replace table names? OnModelCreating doesn't fit because it's called once but I need to use both tables at different times.
Thanks!

public class Datum
{
public int Id { get; set; }
}
public class DataContext : DbContext
{
public DbSet<Datum> Data { get; set; }
}
public class DataTempContext : DataContext
{
protected override void OnModelCreating(DbModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Datum>().ToTable("DataTemp");
}
}
EDIT
This is working for me.
Use of old:
using (var context = new DataContext())
{
// illustrate original table name, Data
Console.WriteLine(context.Data.ToString());
Console.WriteLine();
// Add some real data, for LinqPad
context.Data.Add(new Datum());
context.SaveChanges();
}
SQL output by WriteLine:
SELECT
[Extent1].[Id] AS [Id],
FROM [Data] AS [Extent1]
Use of new:
using (var context1 = new DataContext())
using (var context2 = new DataTempContext())
{
// copy old table to new
foreach (var d in context1.Data)
context2.Data.Add(d);
context2.SaveChanges();
}
using (var context = new DataTempContext())
{
// illustrate different table name, DataTemp
Console.WriteLine(context.Data.ToString());
Console.ReadKey();
}
SQL output by WriteLine:
SELECT
[Extent1].[Id] AS [Id],
FROM [DataTemp] AS [Extent1]

Related

How can update a column by itself on db Level with Entity Framework and Unit of Work?

I'm using Entity Framework and Unit of Work.
I have a decimal column OrderBalance in the Person table and I have an Order table. I want to update orderbalance column by itself at the db level to support concurrent order creations.
I want to insert an order and update OrderBalance column with atomocity (all or nothing).
public override void Create(Order order)
{
_orderReposiory.Add(order);
var person = _personRepository.GetById(order.PersonId);
person.OrderBalance += order.Amount*order.Price;
_personRepository.Edit(person);
_unitOfWork.Commit();
}
As you can see, '+=' process is on object level. How can I do this on db level without breaking atomicity?
I'm using ExeceutSqlCommand with transactionscope and it's work.
public class PersonRepository : GenericRepository<Person>, IPersonRepository
{
public void UpdateOrderBalance(decimal amount,long personId)
{
Entities.Database.ExecuteSqlCommand("Update Person set OrderBalance=OrderBalance+#p0 where id=#p1", amount,personId);
}
}
I have changed my Create Method to this
public override void Create(Order order)
{
using (var scope = new System.Transactions.TransactionScope())
{
_orderReposiory.Add(order);
AddOrderBalancePerson(order);
_unitOfWork.Commit();
scope.Complete();
}
}
private void AddOrderBalancePerson(Order order)
{
_personRepository.UpdateOrderBalance(order.Amount*order.Price, order.PersonId);
}
Entities in PersonRepository and UnitofWork are using same Dbcontext
You need to use the same DbContext instance in both repositories. If you do then you can wrap any number of inserts/updates in a transaction which will give you all or nothing. EF statements will automatically enlist in any transaction pending.
using (var tran = dbContext.Database.BeginTransaction())
{
// your updates here
}

Using Custom and EF POCO Entities Together

In one of my project EF 5.0 generate POCO entities for me under abcModel.tt, This is fine as I can use these in my project but if I apply validation in it then it get lost when I update .edmx from DB. so as a solution I also build all the entities by hand as well
Student.cs Generated by EF POCO
MyStudent.cs Generate by Me
One another reason to build MyStudent.cs is that DB column names are not well
written for example
Generate by EF
Student.cs
{
public int sid; // not good name due to table column name
public string sfname; // not good name due to table column name
public string slname; // not good name due to table column name
}
Build by me
MyStudent.cs
{
public int StudentId;
public string FirstName;
public string LastName;
}
So I like to know is my approach to have dual entities is ok?
Note:
I cannot change table column names because db is too big & already build by dba.
Only solution/suggestion with Data First approach is require.
Thanks
Sure, you can do this. I recommend creating a Data Transfer library and mapping your objects (like with AutoMapper). Then when you need to retrieve or save data to/from the database, call your DT and return a business object (your other class).
Something like this:
public class StudentDT
{
public StudentBO GetStudent(int id)
{
using (var db = new dbContext())
{
var studentDB = db.Students.First(s => s.sid == id);
StudentBO sbo = Mapper.Map<StudentBO>(studentDB);
return sbo;
}
}
public void SaveStudent(StudentBO sbo)
{
using (var db = new dbContext())
{
var sdb = Mapper.Map<Student>(sbo);
db.Students.Add(sdb);
db.SaveChanges();
}
}
}

Why does Entity Framework 5 query different tables when executing a .ToList() versus a .Count() on the same entity?

I am using Entity Framework to map two tables together using Entity Splitting as outlined here and here.
I have found that if I execute a .ToList() on an IQueryable<SplitEntity> then the results are from an Inner Join. However, If I take that same IQueryable and execute a .Count() it will return the number of records returned by a Full Join.
Here is a unit test that fails:
[TestMethod]
public void GetCustomerListTest()
{
// arrange
List<Customer> results;
int count;
// act
using (var context = new DataContext())
{
results = context.Customers.ToList();
count = context.Customers.Count();
}
// assert
Assert.IsNotNull(results); // succeeds
Assert.IsTrue(results.Count > 0); // succeeds. Has correct records from inner join
Assert.AreEqual(count, results.Count); // This line fails. Has incorrect count from full join.
}
This strikes me as very bad. How can I get the .Count() method to return the results from an Inner Join like the .ToList()?
Update - SQL
I was wrong about the full vs inner joins.
The .ToList() results in:
SELECT
[Extent1].[CustomerNumber] AS [CustomerNumber],
-- ...etc...
[Extent2].[CustomerName] AS [CustomerName],
-- ... etc...
FROM [dbo].[CustomerTable1] AS [Extent1]
INNER JOIN [dbo].[CustomerTable2] AS [Extent2] ON [Extent1].[CustomerNumber] = [Extent2].[CustomerNumber]
The .Count() results in:
SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
COUNT(1) AS [A1]
FROM [dbo].[customerTable2] AS [Extent1]
) AS [GroupBy1]
Update - DataContext and entity code
The DataContext:
public class DataContext : DbContext
{
public DataContext() { Database.SetInitializer<DataContext>(null); }
public DbSet<Customer> Customers { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new CustomerMapping());
}
}
}
The Customer Mapping (FluentAPI):
public class CustomerMapping : EntityTypeConfiguration<Customer>
{
public CustomerMapping()
{
this.Map( m => {
m.Properties( x => new { x.CustomerNumber, /*...etc...*/});
m.ToTable("CustomerTable1");
})
.Map( m => {
m.Properties( x => new { x.CustomerName, /*...etc...*/});
m.ToTable("CustomerTable2");
});
}
}
The Customer entity:
public class Customer
{
[Key]
public string CustomerNumber { get; set; }
public string CustomerName { get; set; }
}
If the database and all records in CustomerTable1 and CustomerTable2 have been created by Entity Framework and SaveChanges calls in your application code this difference must not happen and you can go straight ahead and report this as a bug.
If you are mapping to an existing database or if other applications write records into the tables and you actually expect that not every record in CustomerTable1 has a corresponding record in CustomerTable2 and vice versa then Entity Splitting is the wrong mapping of your database schema.
Apparently the difference means that you can have Customers with a CustomerNumber (etc.), but without a CustomerName (etc.) - or the other way around. The better way to model this would be a one-to-one relationship where one side is required and the other side is optional. You will need an additional entity and a navigation property for this, for example like so:
[Table("CustomerTable1")]
public class Customer
{
[Key]
public string CustomerNumber { get; set; }
// + other properties belonging to CustomerTable1
public AdditionalCustomerData AdditionalCustomerData { get; set; }
}
[Table("CustomerTable2")]
public class AdditionalCustomerData
{
[Key]
public string CustomerNumber { get; set; }
public string CustomerName { get; set; }
// + other properties belonging to CustomerTable2
}
With this Fluent API mapping:
public class CustomerMapping : EntityTypeConfiguration<Customer>
{
public CustomerMapping()
{
this.HasOptional(c => c.AdditionalCustomerData)
.WithRequired()
.WillCascadeOnDelete(true);
}
}
I am querying a local table and I get the same count for both. I believe there is a problem with your context and that's why your results are inconsistent.
screenshot of essentially the same code just querying a simple dataset.
UPDATE:
I don't know why the SQL that is generated is different. You would think that they would be the same except for simply executing Count(*) instead of returning all the rows. That is obviously why you are getting a different counts. I just can't say why the SQL is different.
Maybe Jon Skeet or other genius will see this and answer! :)

EF Code First Fluent API - lowercase first letters of all columns

I'm hoping to set up an EF Code First convention where all of the column names of the properties have a lowercase first letter.
However, I have other fluent API code that changes column names from the default. I can't seem to find a way to get access to the current column name of a property in order to lowercase the first letter. Starting with the PropertyInfo, as in modelBuilder.Properties() is not enough because the column name may have already been set to be different than the member name.
How do I generically tell EF Code First to lowercase the first letter of all column names?
OK, the DBA's are speaking. Let's bow our heads in reverence and see what we can do. I'm afraid that in EF 5 (and lower) there's not much you can do to make it easy. In EF 6 there is this feature of Custom Code First Conventions which actually make it a piece of cake. I just tried a small sample:
// Just some POCO
class Person
{
public int PersonId { get; set; }
public string PersonName { get; set; }
}
// A custom convention.
class FirstCharLowerCaseConvention : IStoreModelConvention<EdmProperty>
{
public void Apply(EdmProperty property, DbModel model)
{
property.Name = property.Name.Substring(0, 1).ToLower()
+ property.Name.Substring(1);
}
}
class MyContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>();
// Add the convention to the modelbuilder.
modelBuilder.Conventions.Add(new FirstCharLowerCaseConvention());
base.OnModelCreating(modelBuilder);
}
}
After running
using (var db = new MyContext())
{
db.Database.Create();
}
my database has a People table with personId and personName.
And some simple CRUD actions work flawlessly:
using (var db = new MyContext())
{
var p = new Person { PersonName = "Another Geek" };
db.Set<Person>().Add(p);
db.SaveChanges();
}
using (var db = new MyContext())
{
var x = db.Set<Person>().ToList();
}
So if the DBA's want their conventions, you can demand a new toy :)

EF 4.1, POCO: Is any way to get Table name in runtime to avoid hardcode?

I use POCO in Entity Framework. Is any direct or indirect way in the latest EF version to get Table name at the runtime to avoid hardcode values?
I need it inside my custom database initializer to run code like this:
context.Database.ExecuteSqlCommand(
string.Format("DBCC CHECKIDENT ({0}, RESEED, {1})", tableName, newSeed))
Thanks
I'm working from the assumption that your context looks something like mine, with each of the table names getting generated from the class names when you add a DbSet to your context. If that's the case, you can achieve your goal with reflection, though it's a little ugly:
public class MyContext : DbContext
{
public MyContext() : base("MyDatabase")
{
}
public DbSet<Video> Video { get; set; }
public DbSet<VideoRating> Rating { get; set; }
public DbSet<User> User { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
public class Initializer : IDatabaseInitializer<DashVaultContext>
{
public void InitializeDatabase(MyContext context)
{
if (!context.Database.Exists())
{
context.Database.Create();
PropertyInfo[] propertyInfos = typeof(MyContext).GetProperties(BindingFlags.DeclaredOnly |
BindingFlags.Public | BindingFlags.Instance);
var newSeed = 1000; // Or whatever is appropriate
foreach (PropertyInfo propertyInfo in propertyInfos)
{
var tableName = propertyInfo.PropertyType.GetGenericArguments()[0].Name;
context.Database.ExecuteSqlCommand(
string.Format("DBCC CHECKIDENT ({0}, RESEED, {1})", tableName, newSeed));
}
}
}
}
}
UPDATE: I removed the pluralization hack and just turned off pluralization in the generated table names (see the OnModelCreating override).
POCO means you can use "plain-old" CLR objects (POCO), such as existing domain objects, with your data model. These POCO data classes (also known as persistence-ignorant objects), which are mapped to entities that are defined in a data model and by definition it shouldn't be directly related to database implementation details. However, you can use constant class and Fluent mapping to facilitate your requirement in a better way
Your constant class implementation
public static class Constant
{
public const string CreditCustomer = "dbo.CreditCustomer";
}
Your mappings goes like this
builder.Entity<Customer>()
.HasKey(c => c.ID)
.MapSingleType(c => new {
cid = c.ID,
nme = c.Name
}
)
.ToTable(Constant.Table.CreditCustomer);
In your dbInitializer
context.Database.ExecuteSqlCommand(
string.Format("DBCC CHECKIDENT ({0}, RESEED, {1})", Constant.Table.CreditCustomer, newSeed))
Looking at how "active" this discussion is, it seems to me this functionality is just not provided in the current version of EF. I hope this features will be available in one of future version of EF.
Will this code be useful at all?
var query = from meta in context.MetadataWorkspace.GetItems(DataSpace.SSpace)
.Where(m => m.BuiltInTypeKind == BuiltInTypeKind.EntityType)
let properties = meta is EntityType ? (meta as EntityType).Properties : null
select new
{
TableName = (meta as EntityType).Name,
Fields = from p in properties
select new
{
FielName = p.Name,
DbType = p.TypeUsage.EdmType.Name
}
};