How to mock an object, declared / initialized inside a method under test? - c#

Consider this programs
public class ItemManager
{
private ItemFetcher itemFetcher;
public ItemManager(ItemFetcher _itemFetcher)
{
itemFetcher = _itemFetcher;
}
public List<Item> GetItemsFomTable()
{
List<Item> itemsList = new List<Item>();
Item item;
DataTable resultDataTable = itemFetcher.GetItemsFromDB();
foreach (DataRow row in resultDataTable.Rows)
{
item = new Item();
// set item's name property
// set item's price property
itemsList.Add(item);
}
return itemsList;
}
}
public class Item
{
string name;
public string Name
{
get { return name; }
set { name = value; }
}
double price;
public double Price
{
get { return price; }
set { price = value; }
}
}
public class ItemFetcher
{
public virtual DataTable GetItemsFromDB()
{
// DoSomething and fetch records in DataTable and return
return new DataTable();
}
}
I want to test itemFetcher.GetItemsFromDB method is called once in the method "GetItemsFomTable()" of the class ItemManager. Here is the Test
[TestFixture]
public class ItemManagerTester
{
[SetUp]
public void Init()
{
}
[Test]
public void TestForGetItemsFomTable()
{
var mockItemFetcher = new Mock<ItemFetcher>();
var itemManager = new ItemManager(mockItemFetcher.Object);
mockItemFetcher.Setup(x => x.GetItemsFromDB());
itemManager.GetItemsFomTable();
mockItemFetcher.VerifyAll();
}
}
As you may see there is List initialised inside the method under test
List<Item> itemsList = new List<Item>();
I get this Exception raised:
TestCase 'MockingSample.ItemManagerTester.TestForGetItemsFomTable'
failed: System.NullReferenceException : Object reference not set to an instance of an object.
ItemManager.cs(26,0): at MockingSample.ItemManager.GetItemsFomTable()
ItemManager.cs(77,0): at MockingSample.ItemManagerTester.TestForGetItemsFomTable()
What should I do with the List ? How and where can I mock this if needed ?

Since you haven't specified a return value, the mock item fetcher returns null, so the attempt to access resultDataTable.Rows throws.
To fix the error, tell Moq what you want it to return when you configure the expectation:
mockItemFetcher.Setup(x => x.GetItemsFromDB()).Returns(new DataTable());

Related

Is there any solution to handle dataType for TestCaseSource ? [Nunit Framework]

Based on https://gigi.nullneuron.net/gigilabs/data-driven-tests-with-nunit/ website. I have try to create a simple testcase which prepare for read data in the future. But I have no idea how to handle Argument and use it properly
I have try to set as a object, but i think this might not be a correct solution
[TestCaseSource("GetDataString")]
public void TestMethod2(object configs)
{
}
Here is source code
namespace SAP
{
[TestFixture]
public class Scenario1
{
// This one Give System.ArgumentException
[TestCaseSource("GetDataString")]
public void TestMethod(List<Config> configs)
{
Console.WriteLine("Config " + configs);
}
// This one can handle an Exception
[TestCaseSource("GetDataString")]
public void TestMethod2(object configs)
{
}
public static List<Config> GetDataString()
{
var datas = new List<Config>();
datas.Add(new Config("Nick", "Coldson"));
return datas;
}
}
public class Config
{
public string NickName { get; set; }
public string Name { get; set; }
public Config(string nickname, string name)
{
NickName = nickname;
Name = name;
}
}
}
Here is error msg
System.ArgumentException : Object of type 'SAP.Config' cannot be
converted to type 'System.Collections.Generic.List`1[SAP.Config]'.
The testcasesource has slightly different definition pattern. Assuming you use nunit 3 it should be:
[TestCaseSource(typeof(MyTestData), nameof(GetDataString))]
public void TestMethod2(List<Config> configs)
{
...
}
public class MyTestData
{
public static IEnumerable GetDataString()
{
var datas = new List<Config>();
datas.Add(new Config("Nick", "Coldson"));
return new TestCaseData(datas);
}
}
For more info, check the documentation:
https://github.com/nunit/docs/wiki/TestCaseData
Your GetDataString returns a List<Config>.
Meaning, your test method with a [TestCaseSource("GetDataString")] will be executed as many times as many items the list has and your method must match the item type.
//// This one throws System.ArgumentException
//[TestCaseSource("GetDataString")]
//public void TestMethod(List<Config> configs)
//{
// Console.WriteLine("Config " + configs);
//}
// This one is ok
[TestCaseSource("GetDataString")]
public void TestMethod(Config config)
{
Console.WriteLine(config);
}
If you need to get List<Config> instances in your test, then your source must return some collection containing list items.

Wpf Observable collection and DataGrid not updating changes

I have an observable collection in the view model that implements Bindable Base as follows Please have a look at the MoveUp and MoveDown methods where they are bound to two buttons in the view. When ever up button is pressed I want the selected row in the datagrid to move one step up in the based on the sequence column in the database and for down one step down.. Both the methods works PERFECTLY. Problem is the changes get shown in the datagrid only when the entire view is refreshed. My requirement is when the button is clicked I want the view to be automatically refreshed. I apologize for such long code. Please Help!!!!. I have some cs code as well for the both up and down buttons specified below the viewmodel. Only pointers in the code that needs to be emphasized is the ObservableCollection JobEntities, MoveUp and MoveDown commands.
ViewModel.cs:
public class JobConfigurationViewModel : BindableBase
{
public JobConfigurationLogic JobConfigurationLogic =
new JobConfigurationLogic(new JobConfigurationResultsRepository());
public SrcDestConfigurationLogic SrcDestConfigurationLogic =
new SrcDestConfigurationLogic(new SrcDestCofigurationRepository());
private string _enterprise;
public string Enterprise
{
get { return _enterprise; }
set { SetProperty(ref _enterprise, value); }
}
private int currentJobID;
private int currentSequence;
private int previousJobID;
private int previousSequence;
private string _site;
public string Site
{
get { return _site; }
set { SetProperty(ref _site, value); }
}
private int _siteID;
public int SiteID
{
get { return _siteID; }
set { SetProperty(ref _siteID, value); }
}
private ObservableCollection<JobConfigurationResults> _jobEntities;
public ObservableCollection<JobConfigurationResults> JobEntities
{
get { return _jobEntities; }
set
{
SetProperty(ref _jobEntities, value);
this.OnPropertyChanged("JobEntities");
}
}
//Source System List for Job
private List<SourceSiteSystem> _lstJobSrcSystems;
public List<SourceSiteSystem> LstJobSrcSystems
{
get { return _lstJobSrcSystems; }
set
{
//Using bindable base setproperty method instead of older inotify prop changed method
SetProperty(ref _lstJobSrcSystems, value);
}
}
//Deestination System List for Job
private List<DestinationSiteSystem> _lstJobDestSystems;
public List<DestinationSiteSystem> LstJobDestSystems
{
get { return _lstJobDestSystems; }
set
{
//Using bindable base setproperty method instead of older inotify prop changed method
SetProperty(ref _lstJobDestSystems, value);
}
}
//the Selected Source Site system ID
private int _selectedSrcSiteSystemId = 0;
public int SelectedSrcSiteSystemId
{
get { return _selectedSrcSiteSystemId; }
set
{
//Using bindable base setproperty method instead of older inotify prop changed method
SetProperty(ref _selectedSrcSiteSystemId, value);
}
}
//the Selected Source Site system from the dropdown
private SourceSiteSystem _selectedSrcSiteSystem;
public SourceSiteSystem SelectedSrcSiteSystem
{
get { return _selectedSrcSiteSystem; }
set
{
//Using bindable base setproperty method instead of older inotify prop changed method
if (value != null)
{
SetProperty(ref _selectedSrcSiteSystem, value);
SelectedSrcSiteSystemId = SelectedSrcSiteSystem.SiteSystemId;
}
}
}
//the Selected Destination Site system ID
private int _selectedDestSiteSystemId = 0;
public int SelectedDestSiteSystemId
{
get { return _selectedDestSiteSystemId; }
set
{
//Using bindable base setproperty method instead of older inotify prop changed method
SetProperty(ref _selectedDestSiteSystemId, value);
}
}
//the Selected Destination Site system from the dropdown
private DestinationSiteSystem _selectedDestSiteSystem;
public DestinationSiteSystem SelectedDestSiteSystem
{
get { return _selectedDestSiteSystem; }
set
{
//Using bindable base setproperty method instead of older inotify prop changed method
if (value != null)
{
SetProperty(ref _selectedDestSiteSystem, value);
SelectedDestSiteSystemId = SelectedDestSiteSystem.SiteSystemId;
}
}
}
private JobConfigurationResults _jeJobConfigurationResults;
public JobConfigurationResults JEJobConfigurationResults
{
get { return _jeJobConfigurationResults; }
set { _jeJobConfigurationResults = value; }
}
private List<JobTaskConfiguration> _taskSelectionList = new List<JobTaskConfiguration>();
private CancellationTokenSource _source;
private RelayCommand<object> _commandSaveInstance;
private RelayCommand<object> _hyperlinkInstance;
private RelayCommand<object> _commandRunJob;
private RelayCommand<object> _upCommand;
private RelayCommand<object> _downCommand;
private IEventAggregator _aggregator;
/// <summary>
/// This is a Subscriber to the Event published by EnterpriseViewModel
/// </summary>
/// <param name="agg"></param>
public JobConfigurationViewModel(IEventAggregator agg)
{
_aggregator = agg;
PubSubEvent<Message> evt = _aggregator.GetEvent<PubSubEvent<Message>>();
evt.Subscribe(message => Enterprise = message.Enterprise.ToString(), ThreadOption.BackgroundThread);
evt.Subscribe(message => Site = message.Site.ToString(), ThreadOption.BackgroundThread);
evt.Subscribe(message => SiteID = message.SiteID, ThreadOption.BackgroundThread);
//evt.Unsubscribe();
StartPopulate();
}
private async void StartPopulate()
{
await TaskPopulate();
}
//This is to ensure that the publisher has published the data that is needed for display in this workspace
private bool TaskProc()
{
Thread.Sleep(500);
PubSubEvent<Message> evt = _aggregator.GetEvent<PubSubEvent<Message>>();
evt.Subscribe(message => Enterprise = message.Enterprise.ToString(), ThreadOption.BackgroundThread);
evt.Subscribe(message => Site = message.Site.ToString(), ThreadOption.BackgroundThread);
evt.Subscribe(message => SiteID = message.SiteID, ThreadOption.BackgroundThread);
return DoPopulate();
}
private Task<bool> TaskPopulate()
{
_source = new CancellationTokenSource();
return Task.Factory.StartNew<bool>(TaskProc, _source.Token);
}
/// <summary>
/// This method handles the populating of the Source and Destination Dropdowns and the Job entity and Task Datagrid
/// This is mainly driven by the Site selected in the previous workspace
/// </summary>
/// <returns></returns>
private bool DoPopulate()
{
PopulateSourceDestinations(this.SiteID);
return true;
}
/// <summary>
/// this method displays all entities and tasks for the site.
/// This is done async so that the Publisher thread is not held up
/// </summary>
public void GetJobConfigurationResults()
{
if (SelectedSrcSiteSystem == null)
{
SelectedSrcSiteSystem = LstJobSrcSystems[0];
}
if (SelectedDestSiteSystem == null)
{
SelectedDestSiteSystem = LstJobDestSystems[0];
}
SelectedSrcSiteSystemId = SelectedSrcSiteSystem.SiteSystemId;
SelectedDestSiteSystemId = SelectedDestSiteSystem.SiteSystemId;
var jobConfigurationResults = new JobConfigurationResults
{
SourceId = SelectedSrcSiteSystemId,
DestinationId = SelectedDestSiteSystemId
};
JobEntities = new ObservableCollection<JobConfigurationResults>();
JobEntities = JobConfigurationLogic.GetResults(jobConfigurationResults.SourceId,
jobConfigurationResults.DestinationId);
_taskSelectionList = new List<JobTaskConfiguration>(JobEntities.Count * 3);
}
/// <summary>
/// //Adding a method to pupulate the Source and Destination dropdown lists.
/// This is done async so that the Publisher thread is not held up
/// </summary>
///
///
public async void PopulateSourceDestinations(int siteId)
{
this.LstJobSrcSystems = SrcDestConfigurationLogic.LoadSourceSiteSystems(siteId);
this.LstJobDestSystems = SrcDestConfigurationLogic.LoadDestinationSystems(siteId);
GetJobConfigurationResults();
}
public ICommand HyperlinkCommand
{
get
{
if (_hyperlinkInstance == null)
_hyperlinkInstance = new RelayCommand<object>(openDialog);
return _hyperlinkInstance;
}
}
private void openDialog(object obj)
{
JobConfigurationResults results = obj as JobConfigurationResults;
JEJobConfigurationResults = JobEntities.SingleOrDefault(x => x.JobEntityId == results.JobEntityId);
}
public ICommand CommandSave
{
get
{
if (_commandSaveInstance == null)
_commandSaveInstance = new RelayCommand<object>(saveJobConfigurationChanges);
return _commandSaveInstance;
}
}
public ICommand CommandRunJob
{
get { return _commandRunJob ?? (_commandRunJob = new RelayCommand<object>(RunJob)); }
}
/// <summary>
/// this saves all the changes in the selection made by the user
/// </summary>
/// <param name="ob"></param>
public void saveJobConfigurationChanges(object ob)
{
foreach (var job in JobEntities)
{
int jobEntityId = job.JobEntityId;
foreach (var task in job.TaskDetails)
{
int id = task.JobTask_ID;
bool isSelected = task.IsSelected;
_taskSelectionList.Add(task);
}
}
JobConfigurationLogic.UpdateTaskSelection(_taskSelectionList);
}
public ICommand UpCommand
{
get
{
if (_upCommand == null)
_upCommand = new RelayCommand<object>(MoveUp);
return _upCommand;
}
}
private void MoveUp(object obj)
{
if (obj != null)
{
JobConfigurationResults results = obj as JobConfigurationResults;
currentJobID = results.JobEntityId;
currentSequence = results.SequenceOrder - 1;
try
{
JobConfigurationResults res = _jobEntities.SingleOrDefault(n => n.SequenceOrder == currentSequence);
previousJobID = res.JobEntityId;
previousSequence = res.SequenceOrder + 1;
// JobConfigurationLogic.UpdateSequence(currentJobID, previousSequence, previousJobID, currentSequence);
JobConfigurationLogic.UpdateSequence(currentSequence, currentJobID, previousSequence, previousJobID);
OnPropertyChanged("JobEntities");
}
catch (NullReferenceException)
{
MessageBox.Show("Can't move the top record");
}
}
else
{
MessageBox.Show("Please Select a row that you want to sort");
}
}
public ICommand DownCommand
{
get
{
if (_downCommand == null)
_downCommand = new RelayCommand<object>(MoveDown);
return _downCommand;
}
}
private void MoveDown(object obj)
{
if (obj != null)
{
JobConfigurationResults results = obj as JobConfigurationResults;
currentJobID = results.JobEntityId;
currentSequence = results.SequenceOrder + 1;
try
{
JobConfigurationResults res = _jobEntities.SingleOrDefault(a => a.SequenceOrder == currentSequence);
previousJobID = res.JobEntityId;
previousSequence = res.SequenceOrder - 1;
JobConfigurationLogic.UpdateSequence(currentSequence, currentJobID, previousSequence, previousJobID);
OnPropertyChanged("JobEntities");
}
catch (NullReferenceException)
{
MessageBox.Show("You have reached the end");
}
}
else
{
MessageBox.Show("Please Select a row that you want to sort");
}
}
/// <summary>
/// Execute an etl job using the current job id
/// </summary>
private void RunJob(object obj)
{
JobEngine jobEngine = new JobEngine();
var jobId = JobEntities[0].JobId;
jobEngine.ProcessJob(jobId);
}
}
CS CODE:
private void btnup_Click(object sender, RoutedEventArgs e)
{
dgEntities.Items.Refresh();
//dgEntities.GetBindingExpression(DataGrid.ItemsSourceProperty).UpdateTarget();
}
private void btndown_Click(object sender, RoutedEventArgs e)
{
dgEntities.GetBindingExpression(DataGrid.ItemsSourceProperty).UpdateTarget();
}
An ObservableCollection will notify on change. There's no reason to do it manually, so you can remove all of the OnPropertyChanged("JobEntities");. This will get you to a cleaner solution.
MSDN
WPF provides the ObservableCollection class, which is a built-in
implementation of a data collection that implements the
INotifyCollectionChanged interface.
The next part is that an ObservableCollection will only notify on changes to the collection itself (add/remove). Any modifications to an element within the list will not have have the notify message sent. To do this, the simplest method is to implement the INotifyPropertyChanged to the elements used in the Observable Collection
I'm using PRISM 5 in the example, so it should be pretty equal to what you're doing. There's a couple of major design changes to you're code. First, I'm using a straight property for my Observable Collection. We know the framework will handle any add/remove operations to this collection. Then to notify when I change a property within the entity in an observable collection, I've used a notify property within the TestEntity class itself.
public class MainWindowViewModel : BindableBase
{
//Notice no OnPropertyChange, just a property
public ObservableCollection<TestEntity> TestEntities { get; set; }
public MainWindowViewModel()
: base()
{
this.TestEntities = new ObservableCollection<TestEntity> {
new TestEntity { Name = "Test", Count=0},
new TestEntity { Name = "Test1", Count=1},
new TestEntity { Name = "Test2", Count=2},
new TestEntity { Name = "Test3", Count=3}
};
this.UpCommand = new DelegateCommand(this.MoveUp);
}
public ICommand UpCommand { get; private set; }
private void MoveUp()
{
//Here is a dummy edit to show the modification of a element within the observable collection
var i = this.TestEntities.FirstOrDefault();
i.Count = 5;
}
}
Here's my entity, notice the BindableBase and the fact I notify on change. This allows the DataGrid or whatever you're using to be notified that the property changed.
public class TestEntity : BindableBase {
private String _name;
public String Name
{
get { return _name; }
set { SetProperty(ref _name, value); }
}
//Notice I've implemented the OnPropertyNotify (Prism uses SetProperty, but it's the same thing)
private Int32 _count;
public Int32 Count
{
get { return _count; }
set { SetProperty(ref _count, value); }
}
}
Now really all the TestEntity needs to have implemented the INotifyPropertyChanged for this to work, but I'm using the PRISM BindableBase as an example.
EDIT
I found a similar question on SO. I think yours is slightly different, but they overlap on the concepts. It may help to look over it.
Observable Collection Notify when property changed in MVVM
EDIT
If the datagrid is sorted the previous method will not update the grid. To handle this you need to refresh the grid's view, but are unable to directly access it using MVVM. So to handle this you'll want to use a CollectionViewSource.
public class MainWindowViewModel : BindableBase
{
//This will bind to the DataGrid instead of the TestEntities
public CollectionViewSource ViewSource { get; set; }
//Notice no OnPropertyChange, just a property
public ObservableCollection<TestEntity> TestEntities { get; set; }
public MainWindowViewModel()
: base()
{
this.TestEntities = new ObservableCollection<TestEntity> {
new TestEntity { Name = "Test", Count=0},
new TestEntity { Name = "Test1", Count=1},
new TestEntity { Name = "Test2", Count=2},
new TestEntity { Name = "Test3", Count=3}
};
this.UpCommand = new DelegateCommand(this.MoveUp);
//Initialize the view source and set the source to your observable collection
this.ViewSource = new CollectionViewSource();
ViewSource.Source = this.TestEntities;
}
public ICommand UpCommand { get; private set; }
private void MoveUp()
{
//Here is a dummy edit to show the modification of a element within the observable collection
var i = this.TestEntities.FirstOrDefault();
i.Count = 5;
//Now anytime you want the datagrid to refresh you can call this.
ViewSource.View.Refresh();
}
}
The TestEntity class does not change, but here's the class again:
public class TestEntity : BindableBase
{
private String _name;
public String Name
{
get { return _name; }
set { SetProperty(ref _name, value); }
}
//Notice I've implemented the OnPropertyNotify (Prism uses SetProperty, but it's the same thing)
private Int32 _count;
public Int32 Count
{
get { return _count; }
set { SetProperty(ref _count, value); }
}
}
For clarification, here's my XAML showing the binding to the new CollectionViewSource.
<DataGrid Grid.Row="1" ItemsSource="{Binding ViewSource.View}"></DataGrid>
For further reading you can refer to the MSDN article on this.
Here's another relevant question/answer - Re-sort WPF DataGrid after bounded Data has changed

DotLiquid - access an collection element via a string indexer?

Is it possible to access a collection item by a string reference instead of an index offset for DotLiquid?
public class MyItem
{
public string Name;
public object Value;
public MyItem(string Name, object Value)
{
this.Name = Name;
this.Value = Value;
}
}
public class MyCollection : List<MyItem>
{
public MyCollection()
{
this.Add(new MyItem("Rows", 10));
this.Add(new MyItem("Cols", 20));
}
public MyItem this[string name]
{
get
{
return this.Find(m => m.Name == name);
}
}
}
So in normal c# if I create an instance of the MyCollection class I can access the elements like this
MyCollection col =new MyCollection();
col[1] or col["Rows"]
Can I access via the name element col["Rows"] in a DotLiquid template? If so how do I implement this?
Yes, it is possible. First, define a Drop class like this:
public class MyCollectionDrop : Drop
{
private readonly MyCollection _items;
public MyCollectionDrop(MyCollection items)
{
_items = items;
}
public override object BeforeMethod(string method)
{
return _items[method];
}
}
Then, in the code that renders your template, add an instance of it to the context:
template.Render(Hash.FromAnonymousObject(new { my_items = new MyCollectionDrop(myCollection) }));
Finally, access it like this in your template:
{{ my_items.rows.name }}
"rows" will be passed as-is to MyCollectionDrop.BeforeMethod as the method argument.
Note that you'll also need to make MyItem inherit from Drop, to be able to access its properties. Or write a MyItemDrop class like this:
public class MyItemDrop : Drop
{
private readonly MyItem _item;
public MyItemDrop(MyItem item)
{
_item = item;
}
public string Name
{
get { return _item.Name; }
}
public string Value
{
get { return _item.Value; }
}
}
and then change MyCollectionDrop.BeforeMethod to this:
public override object BeforeMethod(string method)
{
return new MyItemDrop(_items[method]);
}

Fluent API: Reference types take values from eachother

I'm currently writing a grid for MVC that is an HtmlHelper extension and I'm running into a strange problem with it.
First of all, this is the code I'm using the construct the grid:
RenderedOutput = HtmlHelper.GridFor(Model)
.WithColumns(column =>
{
column.Bind(x => x.Name)
.WithCss("inline");
column.Bind(x => x.Age)
.WithCss("inline fixed right");
})
.Render();
Now, the entire fluent API is built up by using interfaces, to make the copy-past work a bit smaller, I do not post the interfaces here but I'll post the implementation:
First of all, the HtmlHelper:
public static IGridBuilder<TEntity> GridFor<TModel, TEntity>(this HtmlHelper<TModel> htmlHelper,
IEnumerable<TEntity> dataSource)
{
return new GridBuilder<TEntity>(htmlHelper, dataSource);
}
So this return a GridBuilder, of which the implementation is here:
#region Constructors
public GridBuilder(HtmlHelper helper, IEnumerable<TModel> data)
{
HtmlHelper = helper;
DataSource = data;
ColumnBuilders = new List<IColumnBuilder<TModel>>();
}
#endregion
#region Properties
private string Id { get; set; }
#endregion
#region IGridBuilder Members
public IList<IColumnBuilder<TModel>> ColumnBuilders { get; protected set; }
public IEnumerable<TModel> DataSource { get; private set; }
public HtmlHelper HtmlHelper { get; private set; }
public IGridBuilder<TModel> WithId(string id)
{
Id = id;
return this;
}
public IGridBuilder<TModel> WithColumns(Action<IColumnBuilder<TModel>> bindAllColumns)
{
bindAllColumns(new ColumnBuilder<TModel>(this));
return this;
}
public HtmlString Render()
{
var outputBuilder = new StringBuilder();
var headerMember = new TagBuilder("div");
headerMember.MergeAttribute("class", "gridHolder v-scroll");
if (!string.IsNullOrEmpty(Id))
{
headerMember.GenerateId(Id);
}
outputBuilder.AppendLine(headerMember.ToString(TagRenderMode.StartTag));
// Process all the available columns.
var rowBuilder = new TagBuilder("div");
rowBuilder.MergeAttribute("class", "row");
outputBuilder.AppendLine(rowBuilder.ToString(TagRenderMode.StartTag));
foreach (var column in ColumnBuilders)
{
var columnBuilder = new TagBuilder("div");
outputBuilder.AppendLine(columnBuilder.ToString(TagRenderMode.StartTag));
outputBuilder.AppendLine(columnBuilder.ToString(TagRenderMode.EndTag));
}
outputBuilder.AppendLine(rowBuilder.ToString(TagRenderMode.EndTag));
outputBuilder.AppendLine(headerMember.ToString(TagRenderMode.EndTag));
return new HtmlString(outputBuilder.ToString());
}
#endregion
}
The GridBuilder takes an Action of IColumnBuilder to build the column, so here it is:
public class ColumnBuilder<TModel> : IColumnBuilder<TModel>
{
#region Constructors
public ColumnBuilder(IGridBuilder<TModel> gridBuilder)
{
GridBuilderReference = gridBuilder;
}
#endregion
#region IColumnBuilder Members
public IGridBuilder<TModel> GridBuilderReference { get; private set; }
public string CssClass { get; private set; }
public IColumnBuilder<TModel> Bind<TItem>(Expression<Func<TModel, TItem>> propertySelector)
{
// Reset the properties. This is needed because they are not cleared automatticly. It's not a new instance which is created.
CssClass = null;
GridBuilderReference.ColumnBuilders.Add(this);
return this;
}
public IColumnBuilder<TModel> WithCss(string className)
{
CssClass = className;
return this;
}
#endregion
}
First of all, it's my first fluent interface implementation so if it's not the good approach please point me in the right direction.
The situation:
When I'm constructing the grid, I pass in an action to bind the columns and those columns are fluent again as well, (I can add a class on a column for example). Now, to render those, I tought it was a good idea to hold a reference to all IColumnBuilder instances in my IGridBuilder, so that in my GridBuilder renders method I can do something like
// Rendering before the columns.
foreach (var c in myColumns)
{
c.Render();
}
// Rendering after the columns.
Therefore, I've created a list in the GridBuilder that will contain all the ColumnBuilders.
When I execute a WithColumns, my GridBuilder object (this) is passed, and then in ColumnBuilder, on each Bind() function I add the ColumnBuilder object to the passed reference.
But this has a strange behaviour (for example, the list containing the ColumnBuilders, does all match the properties of the last ColumnBuilder executed).
Method 1
public class ColumnBuilderFactory<TModel> : IColumnBuilderFactory<TModel>
{
#region Constructors
public ColumnBuilderFactory(IGridBuilder<TModel> gridBuilder)
{
gridBuilderReference = gridBuilder;
}
#endregion
#region IColumnBuilderFactory Members
private IGridBuilder<TModel> gridBuilderReference { get; private set; }
internal IList<IColumnBuilder<TModel>> Columns {get; private set; }
public IColumnBuilder<TModel> New()
{
var column = new ColumnBuilder(gridBuilderReference);
Columns.Add(column);
return column;
}
#endregion
}
and GridBuilder WithColumns becomes
public IGridBuilder<TModel> WithColumns(Action<IColumnBuilderFactory<TModel>> bindAllColumns)
{
var factory = new ColumnBuilderFactory<TModel>(this);
bindAllColumns(factory);
foreach(var column in factory)
{
this.ColumnBuilders.Add(column );
}
return this;
}
This has the usage
RenderedOutput = HtmlHelper.GridFor(Model)
.WithColumns(columnFactory =>
{
columnFactory.New().Bind(x => x.Name)
.WithCss("inline");
columnFactory.New().Bind(x => x.Age)
.WithCss("inline fixed right");
})
.Render();
Method 2
The alternative does much the same by cheating and ignoring this (using the FluentAPI to replace it), basically it's the same as your current usage but Bind becomes
public IColumnBuilder<TModel> Bind<TItem>(Expression<Func<TModel, TItem>> propertySelector)
{
var builder = new ColumnBuilder<TModel>(GridBuilderReference);
GridBuilderReference.ColumnBuilders.Add(builder);
return builder;
}
It relies on the GC tidying up the first(useless) instance
so.....
RenderedOutput = HtmlHelper.GridFor(Model)
.WithColumns(column =>
{
//column at this point isn't used, it's only there to avoid a NullReferenceException on the first call to Bind
column
.Bind(x => x.Name)
//Bind has returned a new ColumnBuilder to play with so the next call will be on the new instance
.WithCss("inline");
//Again this is just so we can make the next bind call
column
//Again bind replaces the previous instance with a new one so we won't overwrite Name with this call
.Bind(x => x.Age)
//This now sets the css on the new ColumnBuilder we just created for Age
.WithCss("inline fixed right");
})
.Render();
Example Console App showing Method 2
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
var master = FluentHelper.GridFor(new Model())
.WithColumns(column =>
{
column.Bind(x => x.Name)
.WithCSS("inline");
column.Bind(x => x.Age)
.WithCSS("inline fixed right");
});
foreach(var c in master.children)
{
Console.WriteLine(c.Binding.ToString());
Console.WriteLine(c.CSS);
}
Console.ReadKey(true);
}
}
class Model
{
public string Name { get; set; }
public string Age { get; set; }
}
class GridBuilder<T>
{
public GridBuilder<T> WithColumns(Action<ColumnBuilder<T>> bindAllColumns)
{
bindAllColumns(new ColumnBuilder<T>(this));
return this;
}
public List<ColumnBuilder<T>> children = new List<ColumnBuilder<T>>();
}
class ColumnBuilder<T>
{
private GridBuilder<T> grid;
public string Binding;
public string CSS;
public ColumnBuilder(GridBuilder<T> grid)
{
// TODO: Complete member initialization
this.grid = grid;
}
public void WithCSS(string css)
{
this.CSS = css;
}
public ColumnBuilder<T> Bind<TItem>(Expression<Func<T, TItem>> propertySelector)
{
var builder = new ColumnBuilder<T>(grid);
builder.Binding = (propertySelector.Body as MemberExpression).Member.Name;
grid.children.Add(builder);
return builder;
}
}
static class FluentHelper
{
internal static GridBuilder<T> GridFor<T>(T model)
{
return new GridBuilder<T>();
}
}
}

foreach dictionary to check derived class

I have a base class Rules.cs. There are 2 derived classes RowRules.cs and ColumnRules.cs. I have another class Test.cs. This class has a Dictionary <int, Rules> which keeps adding the values. When I loop through the dictionary I need to know if the value is a RowRule or a ColumnRule. To better understand I have the code below.
Rules.cs
class Rules
{
private int m_timepointId = 0;
private int m_studyId = 0;
public int TimepointId
{
get { return m_timepointId; }
set { m_timepointId = value;}
}
public int StudyId
{
get { return m_studyId; }
set {m_studyId = value; }
}
}
RowRules.cs
class RowRules : Rules
{
private int m_row;
public int Row
{
get { return m_row; }
set { m_row = value; }
}
}
ColumnRules.cs
class ColumnRules: Rules
{
private int m_column;
public int Column
{
get { return m_column; }
set { m_column = value; }
}
}
In the main class I have
private Dictionary<int, Rules> m_testDictionary = new Dictionary<int, Rules>();
ColumnRules columnrules = new ColumnRules();
RowRules rowRules = new RowRules();
rowRules.Row = 1;
rowRules.StudyId = 1;
m_testDictionary.Add(1, rowRules);
columnRules.Column = 2;
columnRules.TimepointId = 2;
m_testDictionary.Add(2, columnRules);
foreach(.... in m_testDictionary)
{
//Need code here.
//if(... == RowRules)
{
}
}
Now, I need to know what value will go in the foreach loop. Also, I need to know whether that particular dictionary row is a RowRule or a ColumnRule. Hope I am clear with the question. Any help will be really appreciated.
There are a bunch of answers that are telling you to test the type using "is". That's fine, but in my opinion if you're switching off the type of an object, you're probably doing something wrong.
Typically, derived classes are used when you need additional and varied functionality from a base class. Moreover, ad-hoc polymorphism via virtual and abstract methods means that you can let the run-time figure out the type, leading to significantly cleaner code.
For example, in your case, you might want to make Rules an abstract class, with an abstract ApplyRule() method. Then, each subclass can implement the method, with the full knowledge of what it means to be a rule of that type:
public class Rules
{
private int m_timepointId = 0;
private int m_studyId = 0;
public int TimepointId
{
get { return m_timepointId; }
set { m_timepointId = value;}
}
public int StudyId
{
get { return m_studyId; }
set {m_studyId = value; }
}
// New method
public abstract void ApplyRule();
}
class RowRules : Rules
{
private int m_row;
public int Row
{
get { return m_row; }
set { m_row = value; }
}
public override void ApplyRule() { // Row specific implementation }
}
class ColumnRules : Rules
{
private int m_column;
public int Column
{
get { return m_column; }
set { m_column = value; }
}
public override void ApplyRule() { // Column specific implementation }
}
Now, your loop is just:
foreach(var kvp in m_testDictionary)
{
kvp.Value.ApplyRule();
}
This should work:
foreach(KeyValuePair<int, Rules> pair in m_testDictionary)
{
if(pair.Value is RowRule)
{
// do row rule stuff
}
if(pair.Value is ColumnRule)
{
// do row column rule stuff
}
}
Here is more information on the is keyword.
Try the following
foreach(var rule in in m_testDictionary.Values)
{
var rowRules = rule as RowRules;
if (rowRules != null) {
// It's a RowRules
continue;
}
var columnRules = rule as ColumnRules;
if (columnRules != null) {
// It's a ColumnRules
continue;
}
}
You can try this:
foreach(var key in m_testDictionary.Keys)
{
var value = m_testDictionary[key];
if(value is RowRules)
{
//test your code.....
}
}
does that code work? You have added the same key twice I believe. This is the code you wanted I believe:
foreach(int key in m_testDictionary.Keys)
{
RowRules row = m_testDictionary[key] as RowRules;
if(row !=null)
{
//code here:)
}
}

Categories