EF CodeFirst mapping enum with custom value mapping - c#

I need to create an application that is compatible with various legacy database systems.
So, the database exists, but I still want to use code first to be independent from whatever database is used as datastore. For each deployment, I intend to create a "mapping" library containing the correct FluentAPI mappings of the entities to the database.
I'm using EF6.
I don't want code first to alter anything in the database structure automagically, so I use
Database.SetInitializer<mycontext>(null);
Now I'm stuck on following issue:
my code defines an enum Gender, which is used as a property in the Person entity
public enum Gender
{
M = 1,
F = 2
}
However, in one of the legacy databases, the values are the other way around. The table "Gender" exists, and the lookup data in that table is ID 1 = female, ID 2 = male.
The Person table has a "FK Gender ID" column.
How would I configure through Fluent API the mapping of the Gender property of my Person entity, to the Person table in the legacy database table.
modelbuilder.Entity<Person>()
.Property(c => c.Gender)
.HasColumnName("FK Gender ID") //--> and how to "inverse" these values here ?
Is this possible with Fluent API, and if not, is there a workaround ?
Thanks.

I don't think that something you are trying to do is possible. For simplicity you should consider changing your code to match what you have in your database. If you cannot do that here is what you can do. Define an enum type (something like GenderDb or something). Ideally no one should even see this enum. Then create private properties of GenderDb type and map them to database columns (I believe in EF you can map columns to private properties). Again the properties are private so that no one can see them. Then add public properties of the Gender type on the entities that have the private GenderDb properties. The public properties should be configured as not mapped/ignored. Now implement the setter and the getter of the public properties so that it converts the value accordingly (i.e. setter converts the Gender enum to GenderDb and sets the private property, getter reads the private property and converts GenderDb to Gender).
(Yes you could get away with just one enum type if you like to receive phone calls at 2 am)

Related

How to get all previous data by using select method in EF Core

I know that my question is a bit confused, But let me explain in detail.
Suppose that I have person class like this.
public class Person {
public int Id {get; set;}
public string Name {get; set;}
}
and I want create a new entity, but these two classes are similarly so I would like to just inherit and add some new properties
public class Employee : Person {
public string Position {get; set;}
}
everything works fine, but I have a problem when I want to select the data from person table and add it to Employee class like this
employee = _context.Person.Select(
a => new Employee {
Name = a.Name,
Position = "Programmer"
}).ToList();
So as you can see here, I want to add the position property, but also want the previous data from person table. The problem is, I have to type the previous data from person table manually. If the person table has a lot of properties I need to type all of that to get all data. Is there anyway to get previous data without typing all of them. So in javascript it have something like
new State = {
...State,
Position : "employee"
}
Is it possible to do something like this in c#?
Having employee as an entity, you can use
var employees = _context.Employee.Include(e=>e.Person).ToList();
then you'll do it like this employees[0].Person.Name and so on.
If I understand you, you essentially want to "upgrade" an existing Person entity to an Employee entity. Unfortunately, this is not as simple or straight-forward as you would like. EF Core models inheritance via a single table with a discriminator column. That discriminator column informs what class type should actually be instantiated, when the entity is pulled from the database. Since it was saved as a Person, it will have "Person" as the value there, and EF Core will instantiate a Person instance when you retrieve it from the database.
You can then potentially downcast it to Employee, but EF Core isn't going to know what to do with this. Technically, the downcast instance will be untracked. If you attempt to track it, you'll get an error on saving as EF Core will attempt to add a new Employee with the same PK as an existing Person. Boom.
Your best bet is to map over the data from the Person instance to a new Employee instance, including all associated relationships. Then, create the Employee, causing the relationships to be moved at the database-level, before finally removing the old Person.
This will of course result in a new PK being assigned (which is why it's important to migrated the related entities), and that could potentially be problematic if you've got URLs referencing that PK or if you're simply dealing with an auto-increment PK. You'll end up leaving gaps in the keys, and could potentially even run out of keys, if you do this enough.
The only other potential alternative is to change the value of the discriminator column. This would have to be done via straight SQL, as the discriminator column is a shadow property not exposed by EF Core (i.e. there's no way to modify it via C# code). However, if you literally change the value to something like "Employee", then when you fetch it, EF will create an Employee instance, just will all the Employee-specific properties null or default. However, at that point, you can make the necessary updates and save it back.

Tracking property names of revisions

I am currently attempting to implement a revision history screen in an MVC app. I need to be able to retrieve the names of fields which have changed in each revision using Envers. So I am following directions here: http://envers.bitbucket.org/#envers-tracking-modified-entities-revchanges
I am using the second option since we have a custom revision entity. It looks like this:
[RevisionEntity(typeof(MyRevisionListener))]
public class RevisionEntity : DefaultTrackingModifiedEntitiesRevisionEntity
{
public virtual Person User { get; set; }
}
As you can see I am inheriting from DefaultTrackingModifiedEntitiesRevisionEntity in order to make sure the class has the property to hold the modified entities' names.
Per the documentation this should create a table called RevChanges in which this information is stored with reference to the revisions table:
Envers provides a simple mechanism that creates REVCHANGES table which
stores entity names of modified persistent objects. Single record
encapsulates the revision identifier (foreign key to REVINFO table)
and a string value.
I am never seeing this table created. I tried creating such a table myself along with a related class and wiring up the mappings, but I don't see how Envers would know to put the data into that table without me configuring it somehow. I just get an exception saying that the object is different from the target type when the get method is called on the new type.
How can I get this to work?
If you use a custom revision entity, you need to map this just like you do with normal entites.
http://envers.bitbucket.org/#revisionlog

How to find the mapping tables in from the EF generated Classes

I have a mvc 4 application which is entity framework database first based. Therefore lots of the classes are generated based on database tables via EF.
Currently, I created a method to retrive data from a table and display them in a dropdownlist, which works fine. However, I am thinking to modify that method to a generic type method, and passing the class type as the method parameter, and I would like c# code to find the mapping table in the database and retrive the data.
Following is my modified method
Thanks for your helps!
public static SelectList FromDbTableToSelectList<T>() where T: class
{
var db = new TableEntities();
//find the mapping database table
Dictionary<Guid, string> dic = db.MappingTable.ToDictionary(v => v.tableColumn1, v => v.tableColumn2);
return new SelectList(dic, "Key", "Value");
}
I don't understand your what your code does. However, there is a trick to find mapping table name (Entity Set Name in EF glossary) of an entity.
Suppose that you have a table in your db named Products. If you create your model with the Pluralize and singularize option checked, EF will create an entity class named Product.
Then, whenever you perform a query, EF naming convention thinks that your table name is dbo.Products. This is a general rule - in fact a naming convention.
From the other hand, if you uncheck the Pluralize and Singularize option, EF creates an entity class named Products for you and in this case, the class an the table have same names.
So, according to the selection of P&S option, you can infer table names from entity names and you don't need any code for it...

Generics and database - a design issue

The situation is that I have a table that models an entity. This entity has a number of properties (each identified by a column in the table). The thing is that in the future I'd need to add new properties or remove some properties. The problem is how to model both the database and the corresponding code (using C#) so that when such an occasion appears it would be very easy to just "have" a new property.
In the beginning there was only one property so I had one column. I defined the corresponding property in the class, with the appropriate type and name, then created stored procedures to read it and update it. Then came the second property, quickly copy-pasted, changed name and type and a bit of SQL and there it was. Obviously this is not a suitable model going forward. By this time some of you might suggest an ORM (EF or another) because this will generate the SQL and code automatically but for now this is not an option for me.
I thought of having only one procedure for reading one property (by property name) and another one to update it (by name and value) then some general procedures for reading a bunch or all properties for an entity in the same statement. This may sound easy in C# if you consider using generics but the database doesn't know generics so it's not possible to have a strong typed solution.
I would like to have a solution that's "as strongly-typed as possible" so I don't need to do a lot of casting and parsing. I would define the available properties in code so you don't go guessing what you have available and use magic strings and the like. Then the process of adding a new property in the system would only mean adding a new column to the table and adding a new property "definition" in code (e.g. in an enum).
It sounds like you want to do this:
MyObj x = new MyObj();
x.SomeProperty = 10;
You have a table created for that, but you dont want to keep altering that table when you add
x.AnotherProperty = "Some String";
You need to normalize the table data like so:
-> BaseTable
RecordId, Col1, Col2, Col3
-> BaseTableProperty
PropertyId, Name
-> BaseTableValue
ValueId, RecordId, PropertyId, Value
Your class would look like so:
public class MyObj
{
public int Id { get; set; }
public int SomeProperty { get; set; }
public string AnotherProperty { get; set; }
}
When you create your object from your DL, you enumerate the record set. You then write code once that inspect the property as the same name as your configuration (BaseTableProperty.Name == MyObj.<PropertyName> - and then attempt the type cast to that type as you enumerate the record set.
Then, you simply add another property to your object, another record to the database in BaseTableProperty, and then you can store values for that guy in BaseTableValue.
Example:
RecordId
========
1
PropertyId Name
========== ====
1 SomeProperty
ValueId RecordId PropertyId Value
======= ======== ========== =====
1 1 1 100
You have two result sets, one for basic data, and one joined from the Property and Value tables. As you enumerate each record, you see a Name of SomeProperty - does typeof(MyObj).GetProperty("SomeProperty") exist? Yes? What it it's data type? int? Ok, then try to convert "100" to int by setting the property:
propertyInfo.SetValue(myNewObjInstance, Convert.ChangeType(dbValue, propertyInfo.PropertyType), null);
For each property.
Even if you said you cannot use them, that is what most ORM do. Depending on which one you use (or even create if it's a learning experience), they will greatly vary in complexity and performance. If you prefer a light weight ORM, check Dapper.Net. It makes use of generics as well, so you can check the code, see how it works, and create your own solution if needed.

Accessing a Common Code Value Table in Generated EF Models

I have a database that uses a custom model for recording lookup valus (i.e. states, types, etc.). So across all of the database tables, there are various columns called something like state_cdv_id which would store an integer and reference the code_value table to get that state's value (i.e. "CA", "AK", etc.).
I would like to map my EF model so that I can access the code values for all of these fields, and I don't want to have to do it manually in partial classes for EVERY entity... that's a lot of repetition. So I want to be able to access my code values like: MyPerson.State and get back the string "CA" for example.
Here's what a single getter would be that I would have to repeat many times if I were to do it manually:
public string State
{
get
{
MyEntityContext c = new MyEntityContext();
return c.CodeValues.Single(cv => cv.Id == RecordStatusCdvId).Value;
}
}
I don't know what the best approach would be: change the T4 templates, add property attributes to certain fields and then programmatically add a getting to those, or something else.
Any help?
If there is a 1:1 relationship between the entity and the code_value table the entity should already have a State property, which by default which will be null by default, you could then fill it in by using an Include on your DB queries:
var foo = context.MyEntities.Include( x => x.State);
Your sample code is terribly wrong because it makes your entity dependent on the context (moreover you don't dispose it). Whole POCO approach just to avoid this (POCO T4 generator and DbContext T4 generator).
If you have relation to lookup table in your database EF will crate for you navigation property. If you don't have such relation in the database and you are using EDMX file you can still create such relation in your model and you will again get navigation property to lookup table. Once you have navigation property you can simply do:
string code = myEntity.State.Code;
But the navigation property must be loaded either by eager loading (as #BrokenGlass described) or by lazy loading.
If you don't like the idea of navigation property and you still want State property to show just code of the state you must understand what does it mean: If you map the entity that way it will be read-only because EF will not be able to transform compound entity back to real tables which must be updated. It is possible to map the entity the way you want but it is considered as advanced (and mostly not needed) scenario which works only if you have EDMX file (not with code first approach). The choices are:
Create database view and map the view to a new entity
Create DefiningQuery manually in EDMX (opened as XML) file and map it to a new entity (once you do that you cannot update your model from database or generate database from model any more)
Create QueryView manually in EDMX (opened as XML) file and map it to a new entity (this requires original entities to be already mapped)
You will have to do that for every table where you want such mapping. Anyway whole that complexity with manually changing EDMX is not needed because you can simply create custom classes like:
public class SomeViewModel // I suppose your main target is to have codes in presentation layer
{
public string SomeData { get; set; }
public string State { get; set; }
}
and use projection query
If you have navigation property:
var data = from x in context.SomeEntities
select new SomeViewModel
{
SomeData = x.SomeData,
State = x.State.Code
};
If you don't have navigation property
var data = from x in context.SomeEntities
join y in context.LookupValues on x.LookupId equals y.Id
select new SomeViewModel
{
SomeData = x.SomeData,
State = y.Code
};

Categories

Resources