db4o getting history of container - db4o

var config = Db4oEmbedded.NewConfiguration ();
using (var container = Db4oEmbedded.OpenFile (config, FILE))
{
var foo = new Foo ("Test");
container.Store (foo);
foo.Name = "NewName";
container.Store (foo);
}
Any way to resolve the history of container for foo in the format below?
Foo created with values "Test" Foo
Foo's property "Test" changed to "NewName"

You can do by implementing event-handlers. Basically you can register a event-handler for the creating and the updating event. Like this:
IEventRegistry events = EventRegistryFactory.ForObjectContainer(container);
events.Creating +=delegate(object sender, CancellableObjectEventArgs args)
{
Console.WriteLine("{0} created: Value {1}",args.Object.GetType(),args.Object);
};
For viewing value changes you maybe need to peek the old state in the event-handler. You can do this like this:
IEventRegistry events = EventRegistryFactory.ForObjectContainer(container);
events.Creating +=delegate(object sender, CancellableObjectEventArgs args)
{
IObjectContainer eventContainer = args.ObjectContainer();
object oldVersion = eventContainer.Ext().Peek(args.Object,1,false);
object currentVersion = args.Object;
// Do comparison and print stuff
};
Of course the comparison and printing is the work you have to do. There's nothing built in for that.

Related

To remove an item in List

I am trying to remove an item in my gcNumber but it is not getting removed. Below is my code. i know i am doing some silly thing but don't know where :(
var gcn=gcNumber.ToList();
foreach (var selectGc in gcn)
{
var countToRemove = _uow.GetRepository<TripSheetGcDetail>().GetAll().Where(x => x.FkGcId == selectGc.Id && x.IsActive == true).Select(y => y);
if(countToRemove .Count()>0)
{
gcNumber.ToList().Remove(selectGc);
}
else
{
}
}
return gcNumber.ToList();
if my CountToRemove.count()>0 then i want to remove that item in my gcNumber list. Kindly help where i am doing wrong.
gcNumber.ToList().Remove(selectGc);
This line creates a new list, which you remove stuff from, then since you don't do anything else (like assign the list any where) throws the modified list away.
I'm guessing what you want to do instead is something like:
gcn.Remove(selectGc);
You also want to modify your foreach so that it isn't looping over something that is getting modified.
foreach(var selectGg in gcNumber)
This will loop over the original thing, allowing you to modify the copy.
And you'll probably want to return gcn, as your return statement just creates another new list based on gcNumber which hasn't been modified.
In summary: .ToList() creates a new list every time, so modifying that list won't do anything to the original.
instead of gcNumber.ToList().Remove(selectGc); just do gcNumber.Remove(selectGc);
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public class MyClass
{
public string Name {get; set;}
}
public static void Main()
{
var list = new List<MyClass>();
list.Add(new MyClass() {Name = "aaa"});
list.Add(new MyClass() {Name = "bbb"});
list.Add(new MyClass() {Name = "ccc"});
list.Add(new MyClass() {Name = "ddd"});
var indexesToRemove = new List<int>();
foreach(var item in list)
{
if(item.Name == "aaa")
indexesToRemove.Add(list.IndexOf(item));
}
foreach (var num in indexesToRemove)
list.RemoveAt(num);
Console.WriteLine(list.First().Name);
}
}
Maby it is not the best way to remove items from list but it's clear. You can't remove items form list which is enumerating.

Listing WorkItem State Reasons programmatically

We have a customised TFS workflow, I want to be able to access the Reasons I can close a Bug (change the state from Active to Closed) from TFS so that we don't have to update our code every time we want to tweak our process.
This is what I have so far:
WorkItemType wiType = this.GetWorkItemStore().Projects[this.ProjectName].WorkItemTypes["Bug"];
var reason = wiType.FieldDefinitions["Reason"];
var state = wiType.FieldDefinitions["State"];
var filterList = new FieldFilterList();
FieldFilter filter = new FieldFilter(wiType.FieldDefinitions[CoreField.State], "Active");
filterList.Add(filter);
var allowedReasons = reason.FilteredAllowedValues(filterList);
However I'm not getting any results. I'd like to get a list of all the reasons why I can close a bug (Not Reproduceable, Fixed etc)
There isn't any easy way to get the transition via API directly as I know since the API read the allowed values from database directly.
The alternative way would be export the workitemtype definition via WorkItemType.Export() method and then get the information from it. Vaccano's answer in this question provided the entire code sample you can use.
Edited to give an example of how I solved this using the above recommendation:
public static List<Transition> GetTransistions(this WorkItemType workItemType)
{
List<Transition> currentTransistions;
// See if this WorkItemType has already had it's transistions figured out.
_allTransistions.TryGetValue(workItemType, out currentTransistions);
if (currentTransistions != null)
return currentTransistions;
// Get this worktype type as xml
XmlDocument workItemTypeXml = workItemType.Export(false);
// Create a dictionary to allow us to look up the "to" state using a "from" state.
var newTransistions = new List<Transition>();
// get the transistions node.
XmlNodeList transitionsList = workItemTypeXml.GetElementsByTagName("TRANSITIONS");
// As there is only one transistions item we can just get the first
XmlNode transitions = transitionsList[0];
// Iterate all the transitions
foreach (XmlNode transition in transitions)
{
XmlElement defaultReasonNode = transition["REASONS"]["DEFAULTREASON"];
var defaultReason = defaultReasonNode.Attributes["value"].Value;
var otherReasons = new List<string>();
XmlNodeList otherReasonsNodes = transition["REASONS"].SelectNodes("REASON");
foreach (XmlNode reasonNode in otherReasonsNodes)
{
var reason = reasonNode.Attributes["value"].Value;
otherReasons.Add(reason);
}
// save off the transistion
newTransistions.Add(new Transition
{
From = transition.Attributes["from"].Value,
To = transition.Attributes["to"].Value,
DefaultReason = defaultReason,
OtherReasons = otherReasons
});
}
// Save off this transition so we don't do it again if it is needed.
_allTransistions.Add(workItemType, newTransistions);
return newTransistions;
}

Serilog printing contextual property

I am using Serilog and want to print some information message that contains contextual property.
For example:
var logger = Log.ForContext("FirstName", "Yosi");
logger.Information("Hello {FirstName}")
and this works as expected, but when I add parameter to the log message it self, I get unexpected result.
var logger = Log.ForContext("FirstName", "Yosi");
logger.Information("Hello {FirstName} {LastName}", "Attias")
I expect the result to be:
Hello Yosi Attias
but I get:
Hello Attias {LastName}
Is there a fix for that? am I missing something? or this is a bug?
Message templates are only evaluated against the parameters that are passed in to the logging method. Log statements are intended to work consistently regardless of any other configuration/context in the program.
If you want to add additional properties to the logging output, add them to the outputTemplate argument of whatever sink you are configuring:
Log.Logger = new LoggerConfiguration()
.WriteTo.ColoredConsole(outputTemplate:
"{Timestamp:HH:mm} [{Level}] ({LastName}) {Message}{NewLine}{Exception}")
.CreateLogger();
use refection to add properties and their values automatically to log them all in Serilog:
public static Task GenericLog(MyClassLog myclassLog, string message=null)
{
if (string.IsNullOrEmpty(message)) message = nameof(myclassLog);
var properties = typeof(MyClassLog).GetProperties();
var propertyEnrichers = new PropertyEnricher[properties.Length];
for (int i = 0; i < properties.Length; i++)
propertyEnrichers[i] =
new PropertyEnricher(
properties[i].Name,
properties[i].GetValue(myclassLog)
);
var withProps = Log.ForContext(propertyEnrichers);
withProps.Debug(message);
return Task.CompletedTask;
}

Generic procedure execution method for DAL incl. parameters

I am trying to create a "generic" method in a data access layer that executes a passed stored procedure in Sql Server and also takes a list / array / collection of SqlParameters, to make the usage of a stored procedure call within other parts of the code easier (without requirement to care for connection, command objects etc).
The goal is sth. like this:
int iAffectedRows = dal.RunProcedure("dbo.mySP", parameters);
The parameters should of course be defined previously but without the types. I want them to be created using the AddwithValue() method of SqlParameterCollection class.
It looks like it's impossible because the SqlParameterCollection class can't be instanciated. Look at this discussion.
Anyone knows how to create this?
It's not a good idea to send in a DbParameterCollection (SqlParameterCollection), since it's tightly coupled (which you have discovered) with the ADO.NET infrastructure that you're trying to abstract away. It's better to map your own parameter representation to the collection inside your method.
You can solve it using something like this:
public class DataAccess
{
private ConnectionStringSettings _settings;
public DataAccess(ConnectionStringSettings settings)
{
_settings = settings;
}
public int RunProcedure(string name, dynamic parameters)
{
using (var conn = CreateConnection())
using (var command = CreateCommand(conn, name, parameters))
{
return command.ExecuteNonQuery();
}
}
private DbConnection CreateConnection()
{
var factory = DbProviderFactories.GetFactory(_settings.ProviderName);
var connection = factory.CreateConnection();
connection.ConnectionString = _settings.ConnectionString;
connection.Open();
return connection;
}
private DbCommand CreateCommand(DbConnection conn, string commandText,
object parameters)
{
var cmd = conn.CreateCommand();
cmd.CommandText = commandText;
cmd.CommandType = CommandType.StoredProcedure;
foreach(PropertyInfo parameter in parameters.GetType().GetProperties())
{
var commandParameter = cmd.CreateParameter();
commandParameter.ParameterName = "#" + parameter.Name;
commandParameter.Value = parameter.GetValue(parameters);
cmd.Parameters.Add(commandParameter);
}
return cmd;
}
}
Callable with a syntax like this:
dal.RunProcedure("dbo.mySP", new {
Parameter1 = value1,
Parameter2 = value2
});
You can greatly simplify the code if you only want to support SqlClient.
But instead of rolling this on your own, use a ready made stable library, such as Dapper.
I ended up with the following solution:
SqlParameter[] parameters = {
new SqlParameter("#p1", SqlDbType.Int) { Value = 999},
new SqlParameter("#p2", SqlDbType.Char, 30, "source") { Value = "test"}
};
da.RunProcedure("[dbo].[SP1]", parameters, out rowsAffected);
The RunProcedure accepts IDataParameter[] parameters and forwards this to an command builder method that adds each single of them into the SqlParameters Property of my SqlCommand object:
private static SqlCommand BuildQueryCommand(string storedProcName, IDataParameter[] parameters)
{
SqlCommand command = new SqlCommand( storedProcName, GetDBConnection() );
command.CommandType = CommandType.StoredProcedure;
if (parameters != null)
{
foreach (SqlParameter parameter in parameters)
{
command.Parameters.Add( parameter );
}
}
return command;
}
This works fine and this way I can add each Param with 1 single line of code (that was my destination #1) incl. all Properties of SqlParameter available (use SqlDBType if required, this is up to the user).

Merge an Object that wen outside the datacontext

I have the following question:
It is easy to insert an oBject in database with a form.
Just create an object
link it to the fields in your from.
Post back to controller,
create a new datacontext and do datacontext.InsertOnSubmit(object)
.
public static void AddPage(string lang, Page page)
{
using (var db = new CardReaderDataContext())
{
page.Lang = lang;
page.URL = UrlHelper.CreateValidSeoUrl(page.Name, "-");
db.Pages.InsertOnSubmit(page);
db.SubmitChanges();
}
}
But if you want to update an object, it is a tedious job.
You do the same flow,
you get the object,
link it to your form,
post it, but THEN !!!
because it went outside your datacontext, you have to reload the object from the datacontext,
transfer all the variables and save it,
this is a little complex explained so I give an example:
To update an object that you modified in a form:
public static void Update(Page page)
{
using (var db = new CardReaderDataContext())
{
var _page = db.Pages.Where(p => p.Guid == page.Guid).Single();
_page.ModificationDate = DateTime.Now;
_page.Title = page.Title;
_page.Description = page.Description;
_page.Content = page.Content;
_page.Keywords = page.Keywords;
_page.Name = page.Name;
_page.WTLang = page.WTLang;
_page.WTSKU = page.WTSKU;
_page.WTTi = page.WTTi;
_page.WTUri = page.WTUri;
_page.URL = UrlHelper.CreateValidSeoUrl(page.Name, "-");
// _page.Order = GetMaxOrderByMenuGuid(page.MenuGuid);
db.SubmitChanges();
}
}
I don't know if it is clear, if it isn't comment me, I will edit
I think you're looking for DataContext.Attach, but you can only use that with linqtosql objects that have been serialised/deserialised.
Have a read of the answer to this question -
http://social.msdn.microsoft.com/forums/en-US/linqprojectgeneral/thread/384a1c03-3acf-43ef-9a25-b84f93025e63/
"It's also not a good idea to even
attempt to fetch the old version. By
doing that you are in effect turning
off optimistic concurrency, so unless
you intended that this is a bad
approach. What you need to do is
round trip both the original state and
the current state of the object."

Resources