Tracking property names of revisions - c#

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

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.

EF6: Multiple added entities may have the same primary key

I've inherited a database and I need to insert data using EF6. I get the error:
DbUpdateException: Unable to determine the principal end of the 'POSModel.FK_KitMemberTaxRaw_KitMemberSaleReturnRaw_KitMemberSaleReturnRowId' relationship. Multiple added entities may have the same primary key.
I deserialize XML to the POCO objects using DataContractSerializer.
I'm using the object references from the xml document's structure to define the relationships. The POCO objects are generated using a t4 script provided from the NuGet package (which does not work with either deserializer well at all!)
I've decorated KitMemberTaxRaw like so:
[ForeignKey("KitMemberSaleReturnRaw")]
public virtual KitMemberSaleReturnRaw KitMemberSaleReturnRaw { get; set; }
[ForeignKey("KitMemberKitMemberSaleReturnRaw")]
public virtual KitMemberKitMemberSaleReturnRaw KitMemberKitMemberSaleReturnRaw { get; set; }
The KitMemberTaxRaw table may be joined to either table KitMemberKitMemberSaleReturnRaw or KitMemberSaleReturnRaw (but not both).
How does EF determine 'the principal end of the relationship'?
The issue turned out to be EF6 was not able to automatically understand a table with links to a parent and an optional grand parent. The navigation properties generated by the Microsoft provided template were correct but insufficient.
To cure the issue I manually created temporary primary keys for the relationship it did not understand.
Note: The DataContractSerializer class I used created the POCO objects creates an array for the instantiated navigation properties. I had to change the template to generate IList<> properties instead of ICollection<>. At run time there were errors because the array could not be dynamically resized.

Saving OData entities from api with id to database in entity framework

I have multiple projects that return the same OData entities through a API endpoint. Now i want to call all of the projects and store them in my calling projects database with entity framework.
To add them to the db the ID gets overwritten but i want to save the id that the entity has in the projects database as well. so i can still access them if need be and to check if the data isn't already in my database. Because of this i need to add another MainProjectID and projectID column to the entity.
I tried making a new class that has a reference to the entity i want to save but this used new id's for the entities. I also tried inheriting the class but this gave me key conflict issues, and generics don't work either in entity framework(i'm not saying they should). So i'm kinda at a loss right now.
I basically want to save the id as a non-key. Is there any way i can do this without writing entirely new classes and parsing them manually ?
Any help would be greatly appreciated.
We have multiple alternatives here:
In a distributed system, best way to cope with these kinds of ID clashes is to make IDs globally unique. If you can modify how IDs are generated, that would be my choice to go. You can use a UUID (or Microsoft implementation GUID) that will produce a universal unique identifier. Or if that seems like an overkill you can devise a simple mechanism that combines ID with projectID. However you should ensure that the method you will use will not produce any collisions (no two different id-projectId pair will map to same value).
This will ensure that same entity is used throughout your application and no overlaps occur if you try to put records from different sources into the same table. You only need to implement a mechanism to record which ID originated from which source. You can use a reference entity at aggregator for this purpose. You also need to disable auto increment nature of the ID column so that your global unique values are used in table.
You can use different entities for producing and aggregating applications. I don't know your application, but that seems like an OK approach to me since the aggregating application has a different idea about the entity. The aggregating application cares for which application produced the entity, that might make putting the source application identifier into the entry justifiable. Your entities will only differ in that and when you receive the OData object from API you'll need copy all other properties and put project identifier yourself.
You can use the previous solution, but you can use a derived class in order to not to repeat your object properties. This is a better design alternative. However with this method you'll have some problems with the primary key (as you've stated you had). Consider this example
public class Base {
public int ID { get; set; }
[Required]
[StringLength(50)]
[Display(Name = "Name")]
public string Name { get; set; }
}
public class Derived : Base {
[Key]
public int projectId {get; set; }
}
If you don't put [Key] to Derived then you'll have only ID as primary key. When you put [Key] to Derived then you'll have only projectId as primary key. You need to define a composite key and you can do this by removing the [Key] annotation from projectId and using the onModelCreating override of DbContext
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Derived>().HasKey(a => new { a.ID, a.projectId })
.Property(c => c.ID).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None);
modelBuilder.Entity<Derived>().Property(c => c.projectId).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None);
}
You can mix these alternatives. You can drop the primary key on ID field, and then you can insert a new Entity that will model 1-M relationship between ID's and project ID's.

default vs custom using entity framework

I've got a table of default templates. It's global to all users. If a user has no custom template, I want to pull the default. If a user decides to customize the template it should be saved in a customtemplates table - as opposed to the globaltempaltes table.
the custom table has all the globaltemplates fields plus a userid and an id relating to which global it is replacing.
To flesh this out a bit more, lets say there are 3 templates, and a user wants to customize template 2 only. I would normaly pull the whole globaltemplates table and whatever relates to the user in the customtemplates table. Then, in the class property I'd do something in the get like this:
MyTemplateA
get { return customtemplates.A ?? globaltemplates.A; }
Can I do this using straight ef4/linq without poco?
Would a partial class with some additional properties like the get above work?
Since i'm always editing only the customtemplates table (add/edit/delete) it doesn't matter which version of the template I pull. I guess it could get hairy figuring out if it's an insert or an update.
In my opinion it will not work as you expect because EF closely relates entity to table. You cannot have single entity mapped to two tables except very special situations like splitting or inheritance.
So if you have Template entity it can be mapped only to single table but you have two. What you can do is to use TPC inheritance where Template will be a base entity mapped to GlobalTemplates table and UserTemplate will be derived entity mapped to UserTemplates table. TPC (table per concrete type) is type of inheritance where table for derived entity contains all columns from table for parent entity.
But inheritance still has a few problems for your scenario:
Template is editable - if you want to have it read only you must correctly handle it in your application logic. Any changes to attached Template instance will be saved when you call SaveChanges on the context.
When you load Template you cannot directly convert it to UserTemplate to make it user specific. You must create new instance of UserTemplate and copy properties from Template to the newly created instance.

how does your custom class relate to the database

Okay, so i've studied c# and asp.net long enough and would like to know how all these custom classes i created relate to the database. for example.
i have a class call Employee
public class Employee
{
public int ID { get; set; }
public string Name { get; set; }
public string EmailAddress { get; set; }
}
and i have a database with the following 4 fields:
ID
Name
EmailAddress
PhoneNumber
it seems like the custom class is my database. and in asp.net i can simple run the LINQ to SQL command on my database and get the whole schema of my class without typing out a custom class with getter and setter.
so let's just say that now i am running a query to retrieve a list of employees. I would like to know how does my application map to my Employee class to my database?
by itself, it doesn't. But add any ORM or similar, and you start to get closer. for example, LINQ-to-SQL (which I mention because it is easy to get working with Visual Studio), you typically get (given to you by the tooling) a custom "data context" class, which you use as:
using(var ctx = new MyDatabase()) {
foreach(var emp in ctx.Employees) {
....
}
}
This is generating TSQL and mapping the data to objects automatically. By default the tooling creates a separate Employee class, but you can tweak this via partial classes. This also supports inserts, data changes and deletion.
There are also tools that allow re-use of your existing domain objects; either approach can be successful - each has advantages and disadvantages.
If you only want to read data, then it is even easier; a micro-ORM such as dapper-dot-net allows you to use our type with TSQL that you write, with it handling the tedious materialisation code.
Your question is a little vague, imo. But what you are referring to is the Model of the MVC (Model-View-Controller) architecture.
What the Model , your Employee Class, manages data of the application. So it can not only get and set (save / update) your data, but it can also be used to notify of a data change. (Usually to the view).
You mentioned you where using SQL, so more then likely you could create and save an entire employee record by sending an Associative Array of the table data to save it to the database. Your setting for the Class would handle the unique SQL syntax to INSERT the data. In larger MVC Frameworks. The Model of your application inherits several other classes to handle the proper saving to different types of backends other than MS SQL.
Models will also, normally, have functions to handle finding records and updating records. This is normally by specify a search field, and it returning the record, of which would include the ID and you would normally base this back into a save / update function to make changes to record. You could also tie into this level of the Model to create revision of the data you are saving
So how the model directly correlates to your SQL structure is dependent on how you right it. Or which Framework you decide to use. I believe a common one for asp.net is the Microsoft's ASP.Net MVC
Your class cannot be directly mapped to the database without ORM tool, The ORM tool will read your configuration and will map your class to DB row as per your mappings automatically. That means you don't need to read the row and set the class fields explicitly but you have to provide mapping files and have to go through the ORM framework to load the entities, and the framework will take care of the rest
You can check nHibernate and here is getting started on nHibernate.

Categories

Resources