I am following this tutorial
https://www.codeproject.com/Articles/990492/RESTful-Day-sharp-Enterprise-Level-Application?msg=5635950#xx5635950xx
He explains simple thing very well but as a beginner difficult for me to understand the implementation of the advanced function of Generic Repository
public virtual IEnumerable<TEntity> GetMany(Func<TEntity, bool>
where)
{
return DbSet.Where(where).ToList();
}
/// <summary>
/// generic method to get many record on the basis of a condition but
query able.
/// </summary>
/// <param name="where"></param>
/// <returns></returns>
public virtual IQueryable<TEntity> GetManyQueryable(Func<TEntity,
bool> where)
{
return DbSet.Where(where).AsQueryable();
}
/// <summary>
/// generic get method , fetches data for the entities on the basis of
condition.
/// </summary>
/// <param name="where"></param>
/// <returns></returns>
public TEntity Get(Func<TEntity, Boolean> where)
{
return DbSet.Where(where).FirstOrDefault<TEntity>();
}
I implement like this in my Service file
public GEN_TransactionTypeSetup GetTransactionIdByTableName(string
tableName)
{
IEnumerable<GEN_TransactionTypeSetup> list =
_unitOfWorks.TransactionType_Repository.GetMany(p =>
p.Master_TableName = tableName);
return list.ToList();
}
and i get the following error
Cannot implicitly convert type 'string' to 'bool'
Cannot convert lambda expression to delegate type
'System.Func' because some
of the return types in the block are not implicitly convertible to
the delegate return type
Cannot implicitly convert type
'System.Collections.Generic.List'
to 'DataModels.GEN_TransactionTypeSetup'
You'll be able to use the predicate in function of the TEntity type in your implementation.
Let's say your TEntity is Person (GenericRepository<Person>) and your Person has a property Age, which is an int.
You'll then be able to say:
IEnumerable<Person> result = repository.GetMany(p => p.Age > 18);
As you see, this (generic) method returns an IEnumerable<TEntity>, meaning you can apply (and chain) other Linq methods to your result.
Also note that the ToList() in
return DbSet.Where(where).ToList();
is superfluous and, in large resultsets, might create significant overhead because the method returns an IEnumerable<TEntity> and so already does a Linq .Where().
Related
I'm filling the itemssource of a WPF combobox in code-behind with a datatable containing the columns "Listkey" and "Listvalue" like that:
SetElementProperty(element, "ItemsSource", (new ListtablesRead()).ReadListtable(changeTextProperties.SelectedListTable).DefaultView);
SetElementProperty(element, "DisplayMemberPath", "Listvalue");
SetElementProperty(element, "SelectedValuePath", "Listkey");
SetElementProperty is a method which checks by reflection, if the frameworkelement (in that case the combobox) has the given property and sets it.
Then i want to serialize the control with XmlWriter.
So i wrote a converter class for the type DataRowView:
using System;
using System.ComponentModel;
using System.Data;
using System.Windows;
using System.Windows.Markup;
namespace WPFDesignerConverterLibrary
{
public class DataRowViewConverter : ExpressionConverter
{
/// <summary>
/// Finds out if the converter can convert an expression-object to the given destinationtype.
/// </summary>
/// <param name="context">An ITypeDescriptorContext-interface which provides a context for formatting.</param>
/// <param name="destinationType">A type-class which represents the target-type of the conversion.</param>
/// <returns>Returns an object of type bool. True = the destinationtype can be converted.</returns>
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(MarkupExtension))
return true;
return false;
}
/// <summary>
/// Converts the expression to the given destinationtype.
/// </summary>
/// <param name="context">An ITypeDescriptorContext-interface which provides a context for formatting.</param>
/// <param name="culture">The System.Globalization.CultureInfo which is actually used as culture.</param>
/// <param name="value">The object to convert.</param>
/// <param name="destinationType">A type-class which represents the target-type of the conversion.</param>
/// <returns></returns>
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value,
Type destinationType)
{
if (destinationType == typeof(MarkupExtension))
{
DataRowView datarowview = value as DataRowView;
if (datarowview == null)
throw new Exception();
return datarowview.Row;
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
}
This converter works and produces the following lines in the serialized XML:
<sd:DataRow RowError="">
<sd:DataRow.ItemArray>
<x:Array Type="s:Object" xml:space="preserve"><s:String>01</s:String><s:String>Ersttest </s:String></x:Array>
</sd:DataRow.ItemArray>
</sd:DataRow>
<sd:DataRow RowError="">
<sd:DataRow.ItemArray>
<x:Array Type="s:Object" xml:space="preserve"><s:String>02</s:String><s:String>Wiederholungstest </s:String></x:Array>
</sd:DataRow.ItemArray>
</sd:DataRow>
<sd:DataRow RowError="">
<sd:DataRow.ItemArray>
<x:Array Type="s:Object" xml:space="preserve"><s:String>03</s:String><s:String>Konstanzprüfung </s:String></x:Array>
</sd:DataRow.ItemArray>
</sd:DataRow>
But when i try to reload the serialized XML i get an error message, which says, that no standard constructor for the type DataRow was found.
What's going wrong?
And further: To set the itemssource of the combobox with a datatable's defaultview is the simplest way to do this, but can it be that i have to go another way?
InnerException:
=-2146233069 HResult
Message=For the type "System. Data. DataRow" no standard constructor was found. The type can be provided with the argument or the FactoryMethod directive.
Source=System. Xaml
StackTrace:
at system. Xaml. Pattern. XamlTypeInvoker. DefaultCtorXamlActivator. EnsureConstructorDelegate (XamlTypeInvoker type)
at system. Xaml. Pattern. XamlTypeInvoker. CreateInstance (Object [] of argument)
at MS.Internal. Xaml. Run time. ClrObjectRuntime. CreateInstanceWithCtor (XamlType xamlType, Object [] args)
at MS.Internal. Xaml. Run time. ClrObjectRuntime. CreateInstance (XamlType xamlType, Object [] args)
Meanwhile i have found another way.
I'm using a dummy-class called KeyAndValue:
/// <summary>
/// Dummy class with key and value properties for construction of comboboxitems.
/// </summary>
public class KeyAndValue
{
/// <summary>
/// Gets or sets the key.
/// </summary>
public string Key { get; set; }
/// <summary>
/// Gets or sets the value.
/// </summary>
public string Value { get; set; }
}
That class helps to fill the items-collection of the combobox:
foreach (System.Data.DataRow row in table.Rows)
{
// Add new pairs with Listkey and Listvalue as content to the Items-collection.
customComboBox.Items.Add(new KeyAndValue() { Key = row["Listkey"].ToString(), Value = row["Listvalue"].ToString() });
}
That's a little bit like using KeyValuePair with one diffenrence: A generic list like KeyValuePair cannot be serialized.
But my dummy-class does it.
What you need is to have a class with parameterless contrsuctor aka Default Constructor that will allow Serialization.
Step 1.
Create a class to hold your database results.
You can do this through manual code or you could use DataSet.
Step 2.
After you created the class or used the dataset serialize it.
FileStream fs = new FileStream(savePath + "\\" + a.Location + ".bin", FileMode.Create);
BinaryFormatter serializer = new BinaryFormatter();
try
{
serializer.Serialize(fs, a);
}
catch (Exception e)
{
//handle your fail;
}
finally
{
fs.Close();
}
or deserialize it:
List<Model.YOURCLASS> _l = new List<Model.YOURCLASS>();
string[] files = Directory.GetFiles(pathToSearch);
FileStream fs;
BinaryFormatter deserializer = new BinaryFormatter();
foreach (var a in files)
{
if(Path.GetExtension(a) == ".bin")
{
fs = new FileStream(a, FileMode.Open);
_l.Add((Model.YOURCLASS)deserializer.Deserialize(fs));
}
}
return _l;
The type that your routine returns doesn't have to be a DataTable your ListView will automatically use any collection you pass it, even an array[]
I'm attempting to reuse some code found here. After adding a new using the c# (shown below) seems to be fine.
When I go to publish the database, however, the code generator does not seem to recognize the IEnumerable type and ends up producing an error. The actual error (Incorrect Syntax near AS) from the generated code below is obvious, so my question is how can I get SSDT to generate the correct code and avoid the cause of the error? (I assume that I can add the CLR manually, however, I would prefer to do everything from SSDT)
Currently Generates:
CREATE FUNCTION [dbo].[RegExMatches] (#sourceString [nvarchar](4000), #pattern [nvarchar](4000))
RETURNS /* Error: Unsupported type. */
AS EXTERNAL NAME [CampaignStrategyStaging].[SQLRegEx].[RegExMatches];
Should Generate something like:
CREATE FUNCTION [dbo].[RegExMatches] (#sourceString [nvarchar](4000), #pattern [nvarchar](4000))
RETURNS TABLE (
[rowId] int, --RowId each row as it`s ID
[matchId] int, --ID of particular match (starts from 1)
[groupId] int, --ID of particular group in RegEx match (GroupID = 0) represents a complete match
[value] nvarchar(4000) --value of the group
) WITH EXECUTE AS CALLER
AS EXTERNAL NAME [CampaignStrategyStaging].[SQLRegEx].[RegExMatches];
The C# for the CLR:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Text.RegularExpressions;
using System.Collections;
public class SQLRegEx
{
private class RegExRow
{
/// <summary>
/// Private class for passing matches of the RegExMatches to the FillRow method
/// </summary>
/// <param name=”rowId”>ID of the Row</param>
/// <param name=”matchId”>ID of the Match</param>
/// <param name=”groupID”>ID of the Group within the Match</param>
/// <param name=”value”>Value of the particular group</param>
public RegExRow(int rowId, int matchId, int groupID, string value)
{
RowId = rowId;
MatchId = matchId;
GroupID = groupID;
Value = value;
}
public int RowId;
public int MatchId;
public int GroupID;
public string Value;
}
/// <summary>
/// Applies Regular Expression on the Source string and returns value of particular group from withing a specified match
/// </summary>
/// <param name=”sourceString”>Source string on which the regular expression should be applied</param>
/// <param name=”pattern”>Regular Expression pattern</param>
/// <param name=”matchId”>ID of the Match to be returned 1 inex-based</param>
/// <param name=”groupId”>ID of the group from within a match to return. GroupID 0 returns complete match</param>
/// <returns>Value of the Group from within a Match</returns>
[SqlFunction(IsDeterministic=true)]
public static SqlChars RegExMatch(string sourceString, string pattern, int matchId, int groupId)
{
Match m = null;
Regex r = new Regex(pattern, RegexOptions.Compiled);
if (matchId == 1)
{
m = r.Match(sourceString);
}
else if (matchId > 1)
{
MatchCollection mc = r.Matches(sourceString);
if (mc!=null && mc.Count > matchId-1)
{
m = mc[matchId-1];
}
else
{
m= null;
}
///m = mc != null && mc.Count > matchId – 1 ? mc[matchId - 1] : null;
}
return m != null && m.Groups.Count > groupId ? new SqlChars(m.Groups[groupId].Value) : SqlChars.Null;
}
/// <summary>
/// Applies Regular Expression o the Source strings and return all matches and groups
/// </summary>
/// <param name=”sourceString”>Source string on which the regular expression should be applied</param>
/// <param name=”pattern”>Regular Expression pattern</param>
/// <returns>Returns list of RegExRows representing the group value</returns>
[SqlFunction(FillRowMethodName = "FillRegExRow")]
public static IEnumerable RegExMatches(string sourceString, string pattern)
{
Regex r = new Regex(pattern, RegexOptions.Compiled);
int rowId = 0;
int matchId = 0;
foreach (Match m in r.Matches(sourceString))
{
matchId++;
for (int i = 0; i < m.Groups.Count; i++)
{
yield return new RegExRow(++rowId, matchId, i, m.Groups[i].Value);
}
}
}
/// <summary>
/// FillRow method to populate the output table
/// </summary>
/// <param name=”obj”>RegExRow passed as object</param>
/// <param name=”rowId”>ID or the returned row</param>
/// <param name=”matchId”>ID of returned Match</param>
/// <param name=”groupID”>ID of group in the Match</param>
/// <param name=”value”>Value of the Group</param>
public static void FillRegExRow(Object obj, out int rowId, out int matchId, out int groupID, out SqlChars value)
{
RegExRow r = (RegExRow)obj;
rowId = r.RowId;
matchId = r.MatchId;
groupID = r.GroupID;
value = new SqlChars(r.Value);
}
}
After some help from a co-worker I discovered that two changes were needed in the CLR:
the SQLFunction[] declaration needed to include a TableDefinition
argument as shown in the example on here. (code is shown below)
[SqlFunction(FillRowMethodName = "FillRegExRow",
TableDefinition = "[rowId] int,[matchId] int,[groupId] int, [value] nvarchar(4000)")]
public static IEnumerable RegExMatches(string sourceString, string pattern)
int data types in RegExRow.RegExRow were changed to SqlInt32. (This might not have been necessary to resolve the issue in my original question).
So the overall code changed to:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Text.RegularExpressions;
using System.Collections;
public class SQLRegEx
{
private class RegExRow
{
/// <summary>
/// Private class for passing matches of the RegExMatches to the FillRow method
/// </summary>
/// <param name=”rowId”>ID of the Row</param>
/// <param name=”matchId”>ID of the Match</param>
/// <param name=”groupID”>ID of the Group within the Match</param>
/// <param name=”value”>Value of the particular group</param>
public RegExRow(SqlInt32 rowId, SqlInt32 matchId, SqlInt32 groupID, string value)
{
RowId = rowId;
MatchId = matchId;
GroupID = groupID;
Value = value;
}
public SqlInt32 RowId;
public SqlInt32 MatchId;
public SqlInt32 GroupID;
public string Value;
}
/// <summary>
/// Applies Regular Expression on the Source string and returns value of particular group from withing a specified match
/// </summary>
/// <param name=”sourceString”>Source string on which the regular expression should be applied</param>
/// <param name=”pattern”>Regular Expression pattern</param>
/// <param name=”matchId”>ID of the Match to be returned 1 inex-based</param>
/// <param name=”groupId”>ID of the group from within a match to return. GroupID 0 returns complete match</param>
/// <returns>Value of the Group from within a Match</returns>
[SqlFunction(IsDeterministic=true)]
public static SqlChars RegExMatch(string sourceString, string pattern, int matchId, int groupId)
{
Match m = null;
Regex r = new Regex(pattern, RegexOptions.Compiled);
if (matchId == 1)
{
m = r.Match(sourceString);
}
else if (matchId > 1)
{
MatchCollection mc = r.Matches(sourceString);
if (mc!=null && mc.Count > matchId-1)
{
m = mc[matchId-1];
}
else
{
m= null;
}
///m = mc != null && mc.Count > matchId – 1 ? mc[matchId - 1] : null;
}
return m != null && m.Groups.Count > groupId ? new SqlChars(m.Groups[groupId].Value) : SqlChars.Null;
}
/// <summary>
/// Applies Regular Expression o the Source strings and return all matches and groups
/// </summary>
/// <param name=”sourceString”>Source string on which the regular expression should be applied</param>
/// <param name=”pattern”>Regular Expression pattern</param>
/// <returns>Returns list of RegExRows representing the group value</returns>
///
[SqlFunction(FillRowMethodName = "FillRegExRow",
TableDefinition = "rowId int,[matchId] int,[groupId] int, [value] nvarchar(4000)")]
public static IEnumerable RegExMatches(string sourceString, string pattern)
{
Regex r = new Regex(pattern, RegexOptions.Compiled);
int rowId = 0;
int matchId = 0;
foreach (Match m in r.Matches(sourceString))
{
matchId++;
for (int i = 0; i < m.Groups.Count; i++)
{
++rowId;
yield return new RegExRow(rowId, matchId, i, m.Groups[i].Value);
}
}
}
/// <summary>
/// FillRow method to populate the output table
/// </summary>
/// <param name=”obj”>RegExRow passed as object</param>
/// <param name=”rowId”>ID or the returned row</param>
/// <param name=”matchId”>ID of returned Match</param>
/// <param name=”groupID”>ID of group in the Match</param>
/// <param name=”value”>Value of the Group</param>
public static void FillRegExRow(Object obj, out SqlInt32 rowId, out SqlInt32 matchId, out SqlInt32 groupID, out SqlChars value)
{
RegExRow r = (RegExRow)obj;
rowId = r.RowId;
matchId = r.MatchId;
groupID = r.GroupID;
value = new SqlChars(r.Value);
}
}
Using VB.net & WPF
I've converted code available at Overlaying Controls in WPF with Adorners from C# to VB.Net
Original C# Code
/// <summary>
/// Overlays a control with the specified content
/// </summary>
/// <typeparam name="TOverlay">The type of content to create the overlay from</typeparam>
public class OverlayAdorner<TOverlay> : Adorner, IDisposable where TOverlay : UIElement, new()
{
private UIElement _adorningElement; private AdornerLayer _layer; /// <summary> /// Overlay the specified element /// </summary> /// <param name="elementToAdorn">The element to overlay</param> /// <returns></returns> public static IDisposable Overlay(UIElement elementToAdorn) { return Overlay(elementToAdorn, new TOverlay()); }
/// <summary>
/// Overlays the element with the specified instance of TOverlay
/// </summary>
/// <param name="elementToAdorn">Element to overlay</param>
/// <param name="adorningElement">The content of the overlay</param>
/// <returns></returns>
public static IDisposable Overlay(UIElement elementToAdorn, TOverlay adorningElement)
{
var adorner = new OverlayAdorner<TOverlay>(elementToAdorn, adorningElement);
adorner._layer = AdornerLayer.GetAdornerLayer(elementToAdorn);
adorner._layer.Add(adorner);
return adorner as IDisposable;
}
private OverlayAdorner(UIElement elementToAdorn, UIElement adorningElement)
: base(elementToAdorn)
{
this._adorningElement = adorningElement;
if (adorningElement != null)
{
AddVisualChild(adorningElement);
}
Focusable = true;
}
protected override int VisualChildrenCount
{
get { return _adorningElement == null ? 0 : 1; }
}
protected override Size ArrangeOverride(Size finalSize)
{
if (_adorningElement != null)
{
Point adorningPoint = new Point(0, 0);
_adorningElement.Arrange(new Rect(adorningPoint, this.AdornedElement.DesiredSize));
}
return finalSize;
}
protected override Visual GetVisualChild(int index)
{
if (index == 0 && _adorningElement != null)
{
return _adorningElement;
}
return base.GetVisualChild(index);
}
public void Dispose()
{
_layer.Remove(this);
}
}
VB.Net Code (Converted by Me)
Public Class OverlayAdorner(Of TOverlay As {UIElement, New})
Inherits Adorner
Implements IDisposable
Private _adorningElement As UIElement
Private _layer As AdornerLayer
Public Shared Function Overlay(elementToAdorn As UIElement, adorningElement As TOverlay) As IDisposable
Dim adorner = New OverlayAdorner(Of TOverlay)(elementToAdorn, adorningElement)
adorner._layer = AdornerLayer.GetAdornerLayer(elementToAdorn)
adorner._layer.Add(adorner)
Return TryCast(adorner, IDisposable)
End Function
Private Sub New(elementToAdorn As UIElement, adorningElement As UIElement)
MyBase.New(elementToAdorn)
Me._adorningElement = adorningElement
If adorningElement IsNot Nothing Then
AddVisualChild(adorningElement)
End If
Focusable = True
End Sub
Protected Overrides ReadOnly Property VisualChildrenCount() As Integer
Get
Return If(_adorningElement Is Nothing, 0, 1)
End Get
End Property
Protected Overrides Function ArrangeOverride(finalSize As Size) As Size
If _adorningElement IsNot Nothing Then
Dim adorningPoint As New Point(0, 0)
_adorningElement.Arrange(New Rect(adorningPoint, Me.AdornedElement.DesiredSize))
End If
Return finalSize
End Function
Protected Overrides Function GetVisualChild(index As Integer) As Visual
If index = 0 AndAlso _adorningElement IsNot Nothing Then
Return _adorningElement
End If
Return MyBase.GetVisualChild(index)
End Function
Public Sub Dispose() Implements IDisposable.Dispose
_layer.Remove(Me)
End Sub
End Class
Now I've created MainWindow & an User Control UserControl1 in my test project and trying code
Using OverlayAdorner(Of UserControl1).Overlay(G1)
'Err in First Line Itself
End Using
Error Argument not specified for parameter 'adorningElement' of 'Public Shared Function Overlay(elementToAdorn As System.Windows.UIElement, adorningElement As TOverlay) As System.IDisposable'.
What is Wrong Here
Below code block given on blog post you are referring to has incorrect usage shown.
using (OverlayAdorner<ProgressMessage>.Overlay(LayoutRoot))
{
// Do some stuff here while adorner is overlaid
}
Should be (in C#):
using (OverlayAdorner<ProgressMessage>.Overlay(LayoutRoot, this)) // Here I assume `this` is somehow `UserControl`'s object
{
// Do some stuff here while adorner is overlaid
}
In VB.NET
Using OverlayAdorner(Of UserControl).Overlay(G1, UserControl1)
' Do some stuff here while adorner is overlaid
End Using
Also note that: OverlayAdorner(Of UserControl1) should be OverlayAdorner(Of UserControl) because T here should be Type's name not name of object instance.
I hope that helps.
Some preaching
From this question of yours I get hint that you need to work yourself on language more. Give more time to VB.NET or C#.NET and use intellisense in Visual Studio to get yourself familiar with the class library and parameter types to be passed into methods you use. This will help you working in new class libraries which have some to no documentation.
I use this shortcut more when working in new class libaries. Type ctrl+k then ctrl+i. Generally said as ctrl+k,i
Update
Based on recent comment, Check the actual usage below:
XAML snippet:
<Window x:Class="WpfTestApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid x:Name="G1">
<UserControl x:Name="ucMyControl"></UserControl>
</Grid>
</Window>
Code behind snippet:
Using OverlayAdorner(Of UserControl).Overlay(G1, ucMyControl)
' Do some stuff here while adorner is overlaid
End Using
Here's the scenario: C# middle tier webservice has the data sent from SQL Server in a System.Data.DataTable object. There are dozens of columns in the DataTable's columns collection. The browser client only needs to see about six of them. The stored proc on the server is general purpose, not designed for this specific task. Assume we have to use it, rather than writing a new SP.
Is there a simple way, perhaps using generics, to extract the desired subset of columns from that DataTable into a List or a Collection, and then convert the resulting list or collection to JSON using JSON.NET?
Is it possible to create a skeletal C# class definition foo with the relevant field names (and datatypes) and then match on those field names "automagically" and thereby generate a List<foo> from the DataTable's rows collection?
Ideally, during the JSON conversion, any SQL Server datetime values (e.g. 2014-06-24T18:45:00) would be converted to a value that would make it easy to instantiate a javascript Date in the client without having to do string manipulation of the date representation.
Full working Console App code pasted below. But the 2 main methods you need are as follows.
For this code to work, you will have to do the following in your Project.
Add the JSON.Net Nuget Package to the Project.
Add a reference to System.Web.Extensions (if you get a compile error in the line where System.Web.Script.Serialization.JavaScriptSerializer is being referenced in the GetJson method.
/// <summary>
/// Returns Json representation of Generic class with only matching properties from the DataTable (passed as parameter)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="dt"></param>
/// <returns></returns>
public static string GetJsonFromDataTable<T>(DataTable dt) where T : new()
{
string json = GetJson(dt);
return JsonConvert.SerializeObject(JsonConvert.DeserializeObject<List<T>>(json));
}
/// <summary>
/// Returns a JSON string for entire DataTable (passed as parameter)
/// </summary>
/// <param name="dt"></param>
/// <returns></returns>
public static string GetJson(DataTable dt)
{
System.Web.Script.Serialization.JavaScriptSerializer serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
List<Dictionary<string, object>> rows = (from DataRow dr in dt.Rows select dt.Columns.Cast<DataColumn>().ToDictionary(col => col.ColumnName.Trim(), col => dr[col])).ToList();
return serializer.Serialize(rows);
}
Fully working Console App code.
Create a new console App, and replace everything in the Program.cs with this code. Also add JSON.Net to the Console App Project and add the references to System.Web.Extensions.
namespace DataTable2Json
{
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
public class Patient
{
public string FullName { get; set; }
public string PatientID { get; set; }
public int NumberOfIllnesses { get; set; }
public DateTime DateAdmitted { get; set; }
}
public class PatientDrug
{
public string Patient { get; set; }
public string Drug { get; set; }
}
internal class Program
{
private static void Main(string[] args)
{
DataTable patientDrugDataTable = GetPatientDrugTable();
DataTable patientDataTable = GetPatientTable();
string patientDrugJson = GetJsonFromDataTable<PatientDrug>(patientDrugDataTable);
Console.WriteLine("Json for PatientDrug:\n{0}",patientDrugJson);
string patientJson = GetJsonFromDataTable<Patient>(patientDataTable);
Console.WriteLine("\nJson for Patient:\n{0}", patientJson);
Console.WriteLine("\n\nPress a key to Exit...");
Console.ReadKey();
}
private static DataTable GetPatientDrugTable()
{
//
// Here we create a DataTable with four columns.
//
DataTable table = new DataTable();
table.Columns.Add("Dosage", typeof(int));
table.Columns.Add("Drug", typeof(string));
table.Columns.Add("Patient", typeof(string));
table.Columns.Add("Date", typeof(DateTime));
//
// Here we add five DataRows.
//
table.Rows.Add(25, "Indocin", "David", DateTime.Now);
table.Rows.Add(50, "Enebrel", "Sam", DateTime.Now);
table.Rows.Add(10, "Hydralazine", "Christoff", DateTime.Now);
table.Rows.Add(21, "Combivent", "Janet", DateTime.Now);
table.Rows.Add(100, "Dilantin", "Melanie", DateTime.Now);
return table;
}
private static DataTable GetPatientTable()
{
//
// Here we create a DataTable with four columns.
//
DataTable table = new DataTable();
table.Columns.Add("NumberOfIllnesses", typeof(int));
table.Columns.Add("PatientID", typeof(string));
table.Columns.Add("FullName", typeof(string));
table.Columns.Add("DateAdmitted", typeof(DateTime));
table.Columns.Add("StreetAddress1", typeof(string));
table.Columns.Add("City", typeof(string));
table.Columns.Add("State", typeof(string));
//
// Here we add five DataRows.
//
table.Rows.Add(2, "PAT-00001", "David", DateTime.Now, "1 Mill Ln", "Schenectady", "NY");
table.Rows.Add(1, "PAT-00002", "Sam", DateTime.Now, "1915 Boylston Steet", "Boston", "MA");
table.Rows.Add(3, "PAT-00003", "Christoff", DateTime.Now, "15 Polk Steet", "San Francisco", "CA");
table.Rows.Add(4, "PAT-00004", "Janet", DateTime.Now, "10 Waverly St", "Los Angeles", "CA");
table.Rows.Add(5, "PAT-00005", "Melanie", DateTime.Now, "50 Kapaa St", "Kailua", "HI");
return table;
}
/// <summary>
/// Returns Json representation of Generic class with only matching properties from the DataTable (passed as parameter)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="dt"></param>
/// <returns></returns>
public static string GetJsonFromDataTable<T>(DataTable dt) where T : new()
{
string json = GetJson(dt);
return JsonConvert.SerializeObject(JsonConvert.DeserializeObject<List<T>>(json));
}
/// <summary>
/// Returns a JSON string for entire DataTable (passed as parameter)
/// </summary>
/// <param name="dt"></param>
/// <returns></returns>
public static string GetJson(DataTable dt)
{
System.Web.Script.Serialization.JavaScriptSerializer serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
List<Dictionary<string, object>> rows = (from DataRow dr in dt.Rows select dt.Columns.Cast<DataColumn>().ToDictionary(col => col.ColumnName.Trim(), col => dr[col])).ToList();
return serializer.Serialize(rows);
}
}
}
Explanation of Code:
Notice I have 2 classes, Patient and PatientDrug.
I wrote helper methods to return data tables for both classes, that have additional columns.
Then the following 2 lines get the JSON for the class representation for Patient and PatientDrug respectively, while ignoring the additional data columns in DataTable that don't match names.
string patientDrugJson = GetJsonFromDataTable<PatientDrug>(patientDrugDataTable);
string patientJson = GetJsonFromDataTable<Patient>(patientDataTable);
Output in Console Windows (the json strings)
I am binding a WPF ComboBox to a nullable property of type MyEnum? (where MyEnum is an enumerated type)
I am programmatically populating the ComboBox items like this:
// The enum type being bound to
enum MyEnum { Yes, No }
// Helper class for representing combobox listitems
// (a combination of display string and value)
class ComboItem {
public string Display {get;set}
public MyEnum? Value {get;set}
}
private void LoadComboBoxItems()
{
// Make a list of items to load into the combo
var items = new List<ComboItem> {
new ComboItem {Value = null, Display = "Maybe"},
new ComboItem {Value = MyEnum.Yes, Display = "Yes"},
new ComboItem {Value = MyEnum.No, Display = "No"},};
// Bind the combo's items to this list.
theCombo.ItemsSource = items;
theCombo.DisplayMemberPath = "Display";
theCombo.SelectedValuePath = "Value";
}
Also in the code-behind, I am setting the DataContext to an instance of a class with a property called TheNullableProperty (for this example anyway) of type MyEnum?.
The binding of theCombo's SelectedValue is done in my XAML file.
<ComboBox
Name="theCombo"
SelectedValue="{Binding Path=TheNullableProperty,
UpdateSourceTrigger=PropertyChanged}"/>
Problem:
When the value of the bound property is initially non-null, the combo box displays the value properly.
But when the value of the bound property is initially null, the combo box is blank.
It looks like every aspect of data binding is working apart from the representation of the null value when the combobox is first shown.
For example: you can select Maybe from the dropdown, and the bound property is correctly set to null. It's just that initial loading that's failing. Maybe I need to just manually set the SelectedValue initially...
What I Ended Up Doing
Add a hidden textblock databound to the underlying nullable enum value via a Converter that converts from the nullable enum to a string (enum.ToString, or "null").
Load up the combo box with 'ComboItems' each having a string Label (displayed in the combo) and a string Value equal to the enum values as strings (and "null" for the null value).
Data-bind the combo box to the textblock.
/// <summary>
/// Convert from EnumeratedType? to string (null->"null", enum values->ToString)
/// </summary>
public class EnumConverter<T> : IValueConverter where T:struct
{
public static string To(T? c)
{
if (c == null)
return "null";
return c.ToString();
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return To((T?)value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var s = (string) value;
if (s == "null")
return null;
return (T?)Enum.Parse(typeof(T), s);
}
}
public class MyEnumConverter : EnumConverter<MyEnum>
{
}
public class ComboItem
{
public string Value { get; set; }
public string Label { get; set; }
public ComboItem(MyEnum? e, string label)
{
Value = MyEnumConverter.To(e);
Label = label;
}
}
static IEnumerable<ComboItem> GetItems()
{
yield return new ComboItem(null, "maybe");
yield return new ComboItem(MyEnum.Yes, "yup");
yield return new ComboItem(MyEnum.No, "nope");
}
private void SetupComboBox()
{
thecombo.ItemsSource = GetItems().ToList();
}
You cannot bind to a null value by design in WPF (at least 3.5 SP1). This means that at the moment Source gets a Null as a value your Binding will be automatically broken and won't work even if you specify a valid value for the Source. You need to provide some "heart beating" mechanism for your binding via Value Converter. So that Converter transforms null value to some "Undefined" (non-null) for the Targets and than is able to convert your "Undefined" back to null...
There seem to be many issues with having null be a valid value for a ComboBox or ListBox, as similar questions have been asked here, here and here.
There hasn't been a superb, ogre-slaying answer to any of those.
My own suggestion is to add Maybe as a member of MyEnum. I know it's just an example but if everything with a MyEnum has it as nullable, then maybe MyEnum should have an Undefined member (or something similar), this is pretty common with enums anyway.
I found this solution online, which I think is pretty deck. I don't fully understand the implementation -- it's some pretty serious .NET kung fu -- but it's great from my perspective as a user. Give it a try:
http://philondotnet.wordpress.com/2009/09/18/how-to-select-null-none-in-a-combobox-listbox-listview/
DependencyProperty.UnsetValue Field
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace MyConverters
{
[ValueConversion(typeof(DateTime), typeof(String))]
public class StringToIntConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
int number;
return int.TryParse(value.ToString(), out number) ? number : DependencyProperty.UnsetValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value.ToString();
}
}
}
Here's a veryy simple solution to this problem:
Instead of having an item with a value of null in your ItemsSource, use DbNull.Value as item or as the item's value property.
I described this approach in detail here: https://stackoverflow.com/a/44170898/6713814