Creating history table using Entity Framework 4.1 - asp.net-mvc

I am working on asp.net MVC 3 application and I am using codeFirst approach. I am trying to create history table or user table, Where I want to keep track of what columns were modified by user. How can I do this using EF Code First.
Do I need to do it after DataContext.savechanges ?
Please suggest.
Thanks.

The DbContext has a method called Entry<T>:
var entity = context.Items.Find(id);
entity.Name = "foobar";
var entry = context.Entry<Item>(entity);
entry will be of type DbEntityEntry<T> and has the properties OriginalValues and CurrentValues.
You could probably write something that will generically inspect these properties to see what has changed and then automatically insert a new record into your history table.
Either that, or use database triggers.

I'm not sure if this is really the "appropiate" way to do it, but this is how its usually done in sql:
Create an extra property version of type int or something.
Because you probably do not want to loop every time, add another property IsLatestVersion of type bool
When an entity is saved, check if the entity already exists. If so, set the entity on IsLatestVersion = false.
Increment the version, and save the changes as new entity.

Sounds to me like you want an a filter that inherits from ActionFilterAttribute. In my case, this is the simplest example that I have. This is my model, notice that the attributes dictate the mapping to the database.
[Table("UserProfile")]
public class UserProfile
{
[Key, DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
}
In my case, it was as simple as the following, although it was not historical:
public sealed class UsersContext : DbContext
{
public UsersContext() : base("DefaultConnection")
{
}
public DbSet<UserProfile> UserProfiles { get; set; }
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
LazyInitializer.EnsureInitialized(ref _initializer, ref isInitialized, ref initializerLock);
}
public void CheckDatabase()
{
Database.SetInitializer<YourDBContextType>(null);
using (var context = new YourDBContextType())
{
if (!context.Database.Exists())
{
((IObjectContextAdapter)context).ObjectContext.CreateDatabase();
}
}
// Uses your connection string to build the following table.
WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true);
}
The end result is not only EF being code first, but also allows for your models for your views to use primitives derived from your complex entities. Now, if you have another, lets say historical, DBContext then I would recommend modifying either the text transformation file or creating a base class for your entities. Both ways allow for an easy generation of code that could insert into your table, then follow up with that entity, clone it into a historical model and save. All that being said, I am a fan of database first approaches with concentration on constraints, triggers, etc. instead of a framework.

Related

Relationships with EntityFramework Code First using DataAnnotations

I have three tables that have the following relationships between them:
Account {
public int Id;
}
Job {
public int Id;
public int AccountId;
}
Practice {
public int Id;
public int AccountId;
public string Name;
}
I would like to be able to access a Practice object through a Job object like so:
Job.Account.Practice.Name
The database structure is pre-existing, so changing it is not an option. I would like to get EF to provide access to this relationship, and I'm sure it can. I did get it to work by putting an ICollection navigation property to Practice on the Account class and annotating an Account navigation property on the Practice class with ForeignKey, but there has to be a way to do it without using a collection.
So, the question is, what data annotation attributes should I attempt to use to get the desired result?
Description
You need no DataAnnotation Attribute to do that. In Codefirst you can do the following. The Entity Framework will generate the table you described for you.
Sample
Account {
public int Id;
}
Job {
public int Id;
public virtual Account Account;
}
Practice {
public int Id;
public virtual Account Account;
public string Name;
}
If you want also a ìnt column (AccountId) in your Job / Practice Entity you can do this using the ModelBuilder. The Entity Framwork creates only one foreign key column, like you want.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Job>.HasRequired(x => x.Account).WithMany().HasForeignKey(x => x.Accountid);
//
}
More Information
ScottGu - Using EF “Code First” with an Existing Database
Update
You can use Entity Framework Power Tools CTP1 to generate the
Models from your existing Database.

EF 4.1 Codefirst: Instantiate complex navigation properties

imagine having a simple POCO for EF 4.1 Codefirst:
public class Product
{
// Native properties
[Key]
public Guid ID { get; set; }
// Navigation properties
public virtual Category Category { get; set; }
public virtual ICollection<Customer> Customers { get; set; }
public Product()
{
this.ID = Guid.NewGuid();
// Do I have to instantiate navigation properties in the constructor or not???
this.Category = new Category();
this.Customers = new List<Customer>();
}
}
What I couldn't figure out so far is if I should instantiate complex navigation properties in the POCO's constructor or not?
Seems like all my current code is working if I don't instantiate, but I'm concerned that my code might cause problems in the future.
Are there any rules, best practices or any side effects?
Thanks for your ideas and tips!
You don't need to instantiate Category. Category is single entity which either exists or not - Product is not responsible for its creation. You can need to instantiate Customers to empty list.
The reason why it works now is because your context will wrap entities with dynamic proxy which will handle instantiation of you Customers collection. Because of that other code can access the collection without receiving NullReferenceException. This can change if you create instance of Product in your code without using EF. In such case there will be no dynamic proxy and your collection will be null = you will have to instantiate it yourselves.

Can I access the discriminator value in TPH mapping with Entity Framework 4 CTP5

Using Entity Framework 4 CTP5 Code First and this example
Is it possible to access the discriminator value?
I would like to use it in a projection like
context.BillingDetails.Select(x => new { Number = x.Number, DiscrimitatorValue = /* how do I get the discriminator value? */ });
From this post I understand the discriminator cannot be mapped to a property but is there any other way of accessing it?
I may be late to the game on this one, but I just added a getter property to the base class that returned the name of the current type:
public string DiscriminatorValue {
get {
return this.GetType().Name;
}
}
Since by default EF is going to use this same value for the Discriminator field, they will match up.
In EF Core 2.1 (I haven't checked previous versions) it's enough to add Discriminator to the base abstract class as private set property. It will be mapped with adequate value.
public abstract class Entity
{
public int Id { get; set; }
public string Discriminator { get; private set; }
}
EF by itself will automatically insert appropriate discriminator value to the database and will automatically set it to an object on read.
After further information from Morteza Manavi in the comments of his post the simple answer is no
you should be aware that the discriminator column is used internally by Code First and you cannnot read/write its values from an inheritance mapping standpoint.
To access the discriminator I would have to execute a SqlQuery against the database or change my mapping strategy.
Reason aside, I recently ran into the same problem but believe this is still relevant for v4 of the EF Framework.
First, create a view which selects the discriminator value into two columns.
create view dbo.vw_BillingDetail
as
select BillingDetailId, DiscriminatorValue, DiscriminatorValue as DiscriminatorValue2 from dbo.BillingDetail
go
Secondly, map the view to your entity during context creation:
modelBuilder
.Entity<BillingDetail>()
.HasKey(n => n.BillingDetailId)
.Map(map =>
{
map.ToTable("vw_Person");
})
Thirdly, define your discriminator mapping for your derived class using one of the columns in your view:
.Map<MyDerivedBillingDetail>(map =>
{
map.Requires("DiscriminatorValue2").HasValue("YourValue");
})
Finally, define a getter and a private setter for the other discriminator column in your view with the DatabaseGenerated annotation set as Computed to prevent EF from updating/inserting for this field:
class BillingDetail
{
public BillingDetailId { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DiscriminatorValue { get; private set; }
}
You can change the private setter to be protected and set this value explicitly during the construction of your derived entities so that the discriminator has a value prior to being persisted:
class MyDerivedBillingDetail : BillingDetail
{
public MyDerivedBillingDetail()
{
this.DiscriminatorValue = "MyValue";
}
}
To expand on #Michael Black's answer for Entity Framework Core 2.1 (earlier? tested in 2.1.4)
You can use any property name, database field name and data type you want.
Create a property:
[Column("foo_type_id")]
class Foo {
public FooTypesEnum TypeId {get; set;}
}
Then in your context class with the fluent API via modelBuilder:
modelBuilder.Entity<Foo>(b => {
b.HasDiscriminator(foo => foo.TypeId)
.HasValue<SubFooA>(FooTypesEnum.SubFooA)
.HasValue<SubFooB>(FooTypesEnum.SubFooB);
});
This is really useful if you need to build composable queries that e.g., group on the discriminator, etc.
Why don't you use the following query instead?
var q = con.BillingDetails.OfType<BankAccount>().ToList();
You can add a property with the name you gave to the discriminator in EF Core. Example:
In DBContext:
...HasDiscriminator<string>("Type")..
In base class do:
public string Type { get; private set; }

ASP.NET MVC: Connection Controller with Model

I'm still learning, but with the stackoverflow commnuties help, I've been able to get closer and closer.
What I have right now is a View "Index.aspx":
System.Web.Mvc.ViewPage<Data.Models.GetDealsModel>
The Model:
public class GetDealsModel
{
// set up the model
public string DealId { get; set; }
public string StreetAddress { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
public string Logo { get; set; }
public string Website { get; set; }
public string TotalRows { get; set; }
}
And the controller:
public ActionResult Index()
{
LinqToDealsDataContext db = new LinqToDealsDataContext();
XElement xmlTree = XElement.Parse("<Request><ZipCode>92612</ZipCode></Request>");
var deals = db.spSearchDeals(xmlTree);
return View(deals);
}
And with this configuration I'm now getting this error:
The model item passed into the dictionary is of type 'System.Data.Linq.SqlClient.SqlProvider+SingleResult`1[Data.Models.spSearchDealsResult]', but this dictionary requires a model item of type 'Data.Models.GetDealsModel'.
I'm guessing that there's an issue connecting my Controller to my Model... I'm not sure why. PLEASE help me connect this final peice.
NOTE: I do understand that eventually I should separate my logic in the controller into a Repository Pattern, but for now, this will do.
You need to translate the data coming back from this call:
var deals = db.spSearchDeals(xmlTree);
into a GetDealsModel type. So something like:
GetDealsModel dealsModel = new GetDealsModel()
{
DealId = deals.DealId,
StreetAddress = deals.StreetAddress,
....
};
return View(dealsModel);
The reason being that your View is strongly typed to take a GetDealsModel, but your deals variable is not of that type and it gives you that exception when you pass it to the View.
You should create object of type GetDealsModel, but your DB Query returns object of type Data.Models.spSearchDealsResult. Try something like:
return new GetDealsModel
{
DealId = deals.Id,
// other fields here
}
Add to your learning curve list the following items:
Repository Pattern
Ask yourself the following question: Why do I need a service layer?
Read Steven Sanderson's book. It teaches you to think in MVC.
The above applies to your problems because your issues are clearly related to having code in your Controllers that should be in your Model (ie, data access code should be in a repository class). Ie, you are not thinking in MVC.
Your model should include the necessary repository classes, eg, DealRepository.
You need a Service class to map the objects your repository digs out of your database to your model class: that way conversion problems are encapsulated into the Service Layer code.
If you do this, you can then write in your controller:
public ActionResult Index()
{
return(DealService.GetByZipcode(92612));
}
Where DealService.GetByZipcode basically just maps DealRepository.GetByZipcode(92612) to your model class and returns the mapping result.
The DealRepository.GetByZipcode method would be roughly:
public static DealEntity GetByZipcode(string zip)
{
LinqToDealsDataContext db = new LinqToDealsDataContext();
XElement xmlTree = XElement.Parse("<Request><ZipCode>" + zip + "</ZipCode></Request>");
var deals = db.spSearchDeals(xmlTree);
return deals;
}
The DealEntity class is just whatever Linq gives you for your table.
The reason WHY for all this:
The reason for this structure is as follows:
a. All you data access code is in one place: DealRepository. You can test and debug that independently of everything else.
b. The mapping code is all in one place: DealService. You can test and debug that independently of everything else.
c. In other words, you need to properly separate your concerns.
The problem with your existing code is precisely that you have NOT separated concerns. Ie, you have taken a dash of MVC and put it in a food processor and ended up with mush full of problems that are way more difficult to deal with than they need be.
Your model is mixed into your controller, there is no repository, no service layer.
So hold your horses just a while and take the time to read Steve Sanderson's book.
I would also try modelling a simpler problem. That xml parsing makes my head hurt even on a good day.
NOTE:
You could seriously improve your naming conventions. LinqToDealsDataContext? You're kidding, right?

NHibernate - Sorting Entities based on Property/Column + how to manage?

I'm writting an ASP.NET MVC e-commerce app using NHibernate and I want the end-user to be able to control the ordering of Product Categories (not just have them appear alphebetically etc.).
Normally, I'd add an OrderIndex/Sort column (of type int) to the Category table, and property to the Category domain class. But the problem is in having to constantly manage this special OrderIndex/Sort column as Categories are sorted, added, and deleted. I'd rather hide it away and make it transparent so callers don't have to set the property directly.
Sure I could write my own code to manage all this, but wanted to know if NHibernate has anything built in that could help me, or if it could hook this property up automatically.
If not then I was thinking of creating an OrderedEntity base class (all domain objects derive from an Entity base), and create an IOrderedRepository base Repository as well. Something like this:
public class Entity
{
public virtual int Id { get; set; }
}
public class OrderedEntity : Entity
{
public virtual int OrderIndex { get; set; }
}
public class Category : OrderedEntity
{
}
public interface IRepository<T> where T : Entity
{
T FromId(int id);
void Save(T entity);
}
public interface IOrderedRepository<T> : IRepository<T> where T : OrderedEntity
{
void MoveUp(int places);
void MoveDown(int places);
}
Does this seem like a good approach? I don't want to reinvent an inferior wheel.
So far I know Hibernate has an annotation #OrderBy where you can specify the ordering when the collection is loaded. But Hibernate won't manage the position that for you when you add or remove element in the collection.
You can however easily do that yourself and provide methods addItem and removeItem on the parent entity, which will keep track of the position (or the methods MoveUp and MoveDown as you suggest).

Resources