Access SQL Server 2008 Change Tracking Info via Entity Framework - sql

Based on this link it looks like I can get date inserted / date modified information "for free" (without the need for triggers etc.) using Sql Server 2008 by simply enabling Change Tracking. I am using Entity Framework to access the data. My question is, how do I access date modified / date inserted information for the database records via Entity Framework and LINQ?
I'm using VS2008 so I don't have the new EF v4 stuff yet (but if it can be done in the new EF please let me know).
Thanks.

Aw, nevermind. I found it plenty easy to just create the Inserted and LastModified columns in each table myself, set a default value for each, and build an update trigger for LastModified. That seems to be what people do, and the Change Tracking looks like it's mainly set up for sync'ing. Right?

OK, this works beautifully (also see this article)...
public partial class MyEntities
{
partial void OnContextCreated()
{
this.SavingChanges += new System.EventHandler(CustomSavingChangesLogic);
}
/// <summary>
/// Apply timestamps
/// </summary>
public void CustomSavingChangesLogic(object sender, System.EventArgs e)
{
var changedEntities = ((ObjectContext)sender).ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified);
foreach (var stateEntryEntity in changedEntities)
{
if(!stateEntryEntity.IsRelationship) {
var entity = stateEntryEntity.Entity;
var lastModifiedPropInfo = entity.GetType().GetProperty("LastModified");
if (lastModifiedPropInfo != null)
lastModifiedPropInfo.SetValue(entity, DateTime.UtcNow, null);
if (stateEntryEntity.State == EntityState.Added)
{
var createdPropInfo = entity.GetType().GetProperty("Created");
if (createdPropInfo != null)
createdPropInfo.SetValue(entity, DateTime.UtcNow, null);
}
}
}
}
}

Related

edit only changed or mentionned values with entity framework core

I need to update only mentioned fields in the put request body , the current issue is that all the values that are not mentioned in the entity to update are set to null
below is my currrent update implementation in the generic repository.
public virtual void Update(T entity)
{
Context.Attach(entity);
Context.Entry(entity).State = EntityState.Modified;
}
You need two different steps. First you have to perform a patch operation. Description here
public IActionResult PatchEntity(int id, [FromBody] JsonPatchDocument<Entity> patchdoc)
{
var entity = dbContext.Entities.Find(e=>e.Id == id);
patchdoc.ApplyTo(entity);
dbContext.Update(entity);
return Ok(entity);
}
Here is a method to perform partial update on DB (take a look at this question too):
public virtual void Update(params object[] keys, T entity)
{
var current = Context.Entities.Find(keys);
Context.Entry(entity).CurrentValues.SetValues(entity);
Context.SaveChanges();
}
If you donĀ“t need to partially update the database record you are fine with:
public virtual void Update(T entity)
{
Context.Update(entity); // entity is attached by default after select of entity
Context.SaveChanges();
}
What you could do is to get the entity before updating it :
Get your entity from your Context
Update the fields of your entity with the data from your model. You can use tools like Automapper to achieve this goal in a clean way.
Then call your Update method on the entity
Another way would be to check the state of each field such as in this answer.
EDIT Update point 2.
Hope it helps.
finally figured it out without even changing the repository
i just added a config within the automapper config file to ignore any null value
CreateMap<TeamDto, Team>().ForAllMembers(opts => opts.Condition((src, dest, srcMember) => srcMember != null));

Sitecore Glass mapper GetItem<TypeName>(guid) always return null

I saw a related question:
Sitecore Glass Mapper always null
But unfortunately it does not give a solution for my case.
Here goes a code snippet:
var db = Factory.GetDatabase("master");
var context = new SitecoreContext();
// the ID of Needed item
var g = new Guid("{F21C04FE-8826-41AB-9F3C-F7BDF5B35C76}");
// just to test if it's possible to fetch item using db.GetItem
var i = db.GetItem(new ID(g), Language.Current, Sitecore.Data.Version.Latest);
// Grab item
var t = context.GetItem<Article>(g);
In the code above:
i is not null
t is null
Article is the simple class like:
[SitecoreType(TemplateId = "{4C4EC1DA-EB77-4001-A7F9-E4C2F61A9BE9}")]
public class Article
{
[SitecoreField(FieldName = "Title")]
public string Title { get; set; }
}
There are only one language installed in Sitecore - en, it has been specified in the web.config in the items as well.
Also I have added GlassMapperSc.Start(); to Application_Start in the Global.asax.cs and added my assembly to the list of included assemblies via var attributes = new AttributeConfigurationLoader(new[] { "Assembly.Name" }); and I succeeded to find my class in the SitecoreContext mappings.
It does not looks like a language issue, as stated in the link provided in the very beginning. And I'm struggling with it already for a pretty long time, but no luck...
Thank You!
I just noticed that you are using master db for the Sitecore DB and SitecoreContext for Glass.
The SitecoreContext class will use the database that is defined by the Sitecore.Context.Database property at runtime. This probably means that it is using the web database.
Can you check that you have published the item to the web database or instead using:
var context = new SitecoreService("master");

Entity Framework 6 - Save Changes

I am trying to perform an insert with EF 6.
I have verified I have a connection to the database, because I can do a read:
List<Driver> drivers = DataContext.Drivers.ToList();
With a sql profiler, I can see this do a select on the database, and it returns an item I manually inserted.
I am trying to perform an insert like this:
var driver = new Driver();
driver.DriverName = "Blah";
DataContext.Drivers.Add(driver);
DataContext.ChangeTracker.HasChanges(); //false
DataContext.SaveChanges();
except nothing is inserted, and the changetracker seems to show that it has not detected any changes. I also saw suggestions to use .Attach but this had the same results.
Any help on what I am doing wrong?
Cheers
(MyEntities.Context.cs)
public partial class MyEntities : DbContext
{
public MyEntities()
: base("name=MyEntities")
{
}
public partial class MyDataContext : MyEntities
{
public class SqlDataService : DataServiceBase<...Data.MyDataContext>
{
//where I am trying to do the insert with the code above
edit: No not using code first (not that i'm aware of!) not sure if the above code samples help, but show how I have set up my classes
It seems you have AutomaticTrackChanges turned off somewhere in your code.
Try adding this line before the addition:
var driver = new Driver();
driver.DriverName = "Blah";
//Turning Automatic changes tracking on:
DataContext.Configuration.AutoDetectChangesEnabled = true;
DataContext.Drivers.Add(driver);
DataContext.ChangeTracker.HasChanges(); //True
DataContext.SaveChanges();
Note: AutoDetectChangesEnabled is usually turned off for performance considerations

NHibernate database versioning: object level schema and data upgrades

I would like to approach database versioning and automated upgrades in NHibernate from a different direction than most of the strategies proposed out there.
As each object is defined by an XML mapping, I would like to take size and checksum for each mapping file/ configuration and store that in a document database (raven or something) along with a potential custom update script. If no script is found, use the NHibernate DDL generator to update the object schema. This way I can detect changes, and if I need to make DML changes in addition to DDL, or perform a carefully ordered transformation, I can theoretically do so in a controlled, testable manner. This should also maintain a certain level of persistence-layer agnosticism, although I'd imagine the scripts would still necessarily be database system-specific.
The trick would be, generating the "old" mapping files from the database and comparing them to the current mapping files. I don't know if this is possible. I also don't know if I'm missing anything else that would make this strategy prohibitively impractical.
My question, then: how practical is this strategy, and why?
what i did to solve just that problem
version the database in a table called SchemaVersion
query the table to see if schema is up to date (required version stored in DAL), if yes goto 6.
get updatescript with version == versionFromBb from resources/webservices/...
run the script which also alters the schemaversion to the new version
goto 2.
run app
to generate the scripts i have used 2 options
support one rdbms: run SchemaUpdate to export into file and add DML statements manually
support multiple rdbms: use Nhibernate class Table to generate at runtime ddl to add/alter/delete tables and code which uses a session DML
Update:
"what method did you use to store the current version"
small example
something like this
public static class Constants
{
public static readonly Version DatabaseSchemaVersion = new Version(1, 2, 3, 4);
}
public class DBMigration
{
private IDictionary<Version, Action> _updates = new Dictionary<Version, Action>();
private Configuration _config;
private Dialect _dialect;
private IList<Action<ISession>> _actions = new List<Action<ISession>>(16);
private string _defaultCatalog;
private string _defaultSchema;
private void CreateTable(string name, Action<Table> configuretable)
{
var table = new Table(name);
configuretable(table);
string createTable = table.SqlCreateString(_dialect, _config.BuildMapping(), _defaultCatalog, _defaultSchema);
_actions.Add(session => session.CreateSQLQuery(createTable).ExecuteUpdate());
}
private void UpdateVersionTo(Version version)
{
_actions.Add(session => { session.Get<SchemaVersion>(1).Value = version; session.Flush(); });
}
private void WithSession(Action<session> action)
{
_actions.Add(action);
}
public void Execute(Configuration config)
{
_actions.Clear();
_defaultCatalog = config.Properties[NH.Environment.DefaultCatalog];
_defaultSchema = config.Properties[NH.Environment.DefaultSchema];
_config = config;
_dialect = Dialect.GetDialect(config.Properties);
using (var sf = _config.BuildSessionFactory())
using (var session = sf.OpenSession())
using (var tx = session.BeginTransaction())
{
Version dbVersion = session.Get<SchemaVersion>(1).Value;
while (dbVersion < Constants.DatabaseSchemaVersion)
{
_actions.Clear();
_updates[dbVersion].Invoke(); // init migration, TODO: error handling
foreach (var action in _actions)
{
action.Invoke(session);
}
tx.Commit();
session.Clear();
dbVersion = session.Get<SchemaVersion>(1).Value;
}
}
}
public DBMigration()
{
_updates.Add(new Version(1, 0, 0, 0), UpdateFromVersion1);
_updates.Add(new Version(1, 0, 1, 0), UpdateFromVersion1);
...
}
private void UpdateFromVersion1()
{
AddTable("Users", table => table.AddColumn(...));
WithSession(session => session.CreateSqlQuery("INSERT INTO ..."));
UpdateVersionTo(new Version(1,0,1,0));
}
...
}

How to log subsonic3 sql

I'm starting to develop a new asp.net application based on subsonic3 (for queries) and log4net (for logs) and would like to know how to interface subsonic3 with log4net so that log4net logs the underlying sql used by subsonic.
This is what I have so far:
public static IEnumerable<arma_ocorrencium> ListArmasOcorrencia()
{
if (logger.IsInfoEnabled)
{
logger.Info("ListarArmasOcorrencia: start");
}
var db = new BdvdDB();
var select = from p in db.arma_ocorrencia
select p;
var results = select.ToList<arma_ocorrencium>(); //Execute the query here
if (logger.IsInfoEnabled)
{
// log sql here
}
if (logger.IsInfoEnabled)
{
logger.Info("ListarArmasOcorrencia: end");
}
return results;
}
You can use the Log property of the Provider class:
_db.Provider.Log = Console.Out;
will log your SQL statements to the console. If you want to use log4net or something similar you will have to write a small mediator class that implements TextWriter and redirects all received input to log4net.
You can get the generated sql like this:
string sql = select.GetQueryText();
Make sure you are using the version 3.0.0.4 or above.
Cheers