Per this Using System.Text.Json to Serialize an IConfiguration back to Json it seems that the limited type information that you can put into JSON is discarded.
You seem to be under the impression that IConfiguration objects are storing ints, bools, etc. (for example) corresponding to the JSON Element type. This is incorrect. All data within an IConfiguration is stored in stringified form. The base Configuration Provider classes all expect an IDictionary<string, string> filled with data. Even the JSON Configuration Providers perform an explicit ToString on the values.
I noticed this when I addressed the problem posed in that question with this extension method.
using System.Collections.Generic;
using System.Dynamic;
using Microsoft.Extensions.Configuration;
public static class ExtendConfig
{
public static dynamic AsDynamic(this IConfigurationRoot cr)
{
var result = new ExpandoObject();
var resultAsDict = result as IDictionary<string, object>;
foreach (var item in cr.AsEnumerable())
{
resultAsDict.Add(item.Key, item.Value);
}
return result;
}
}
This method reconstructs the graph but everything is now a string.
I could write my own parser and apply it to the original JSON string but that's a bit dire. Is there any way I can get this metadata so I can improve the fidelity of merged configs? I'm passing it through for consumption by JS which does notice the difference.
Merging is why I'm using the config extensions builder.
As IConfiguration doesn't provide the information about types, but System.Text.Json used by JsonConfigurationProvider does, the working solution (or a workaround) would be using the System.Text.Json deserializer directly to read the configuration file and match the types to the configuration keys.
But we have some minor issues to solve first. Like - where is the configuration file? We don't want to duplicate that information in code, we have to extract it from IConfiguration instance.
Then - match the concrete existing configuration key to the JSON document tree node. That will require either DFS or BFS tree traversal algorithm. I'll go for DFS (Depth First Search). In a nutshell - if you have expandable nodes, you put them in the stack in reverse order. Then you have a while loop that takes a node from the stack, if it has children, you put them on the same stack, if not - you just yield the node. As simple as that, and BFS is pretty similar, but nevermind.
There's one thing more: Newtonsoft.Json - a popular Nuget package that was at a time used even by Microsoft. That JSON serializer is a little slower than System.Text.Json, but it's more advanced, allowing the user to build a JSON document tree node by node.
Having this powerful tool makes creating a writeable JSON IConfiguration relatively easy, especially using some helpers like mine below.
Look at the SaveChanges() method. It walks through the IConfiguration nodes, matches appropriate JObject nodes by their path and copies the changes from IConfiguration instance to the JObject instance. Then you can just write the JSON file.
There's an ugly hack used to get the file. I get the private field containing the IConfigurationRoot instance, but that could be skipped if you already have the configuration root. Having the root you can get JsonConfigurationProvider from it, then it's just Source.Path property.
So here's the code. It's a part of the Woof.Toolkit and Woof.Config Nuget package, that provides writeable JSON configurations, some helper methods to them, and also Azure Key Vault client that uses a JSON configuration, with some helper methods to encrypt and decrypt sensitive data with keys stored on AKV.
This is the first release of ConfigurationExtensions class, so it might be sub-optimal in performance terms, but it works and illustrates how you can match the IConfiguration instance nodes with JObject nodes to get the types of the configuration properties.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Json;
using Newtonsoft.Json.Linq;
namespace Woof.Config;
/// <summary>
/// Extensions for <see cref="IConfiguration"/> making JSON type <see cref="IConfiguration"/> writeable.
/// </summary>
public static class ConfigurationExtensions {
/// <summary>
/// Gets the configuration root element.
/// </summary>
/// <param name="configuration">Any <see cref="IConfiguration"/> part.</param>
/// <returns>Root element.</returns>
public static IConfigurationRoot? GetRoot(this IConfiguration configuration) {
if (configuration is IConfigurationRoot root) return root;
var rootField = configuration.GetType().GetField("_root", BindingFlags.Instance | BindingFlags.NonPublic);
return rootField?.GetValue(configuration) as IConfigurationRoot;
}
/// <summary>
/// Gets the first <see cref="JsonConfigurationProvider"/> if exists, null otherwise.
/// </summary>
/// <param name="root">Configuration root element.</param>
/// <returns><see cref="JsonConfigurationProvider"/> or null.</returns>
public static JsonConfigurationProvider? GetJsonConfigurationProvider(this IConfigurationRoot root)
=> root.Providers.OfType<JsonConfigurationProvider>().FirstOrDefault();
/// <summary>
/// Gets the first <see cref="JsonConfigurationProvider"/> if exists, null otherwise.
/// </summary>
/// <param name="config">Any <see cref="IConfiguration"/> part.</param>
/// <returns><see cref="JsonConfigurationProvider"/> or null.</returns>
public static JsonConfigurationProvider? GetJsonConfigurationProvider(this IConfiguration config)
=> config.GetRoot()?.GetJsonConfigurationProvider();
/// <summary>
/// Saves changes made to <see cref="IConfiguration"/> to the JSON file if exists.
/// </summary>
/// <param name="config">Any <see cref="IConfiguration"/> part.</param>
/// <exception cref="InvalidOperationException">Configuration does not have <see cref="JsonConfigurationProvider"/>.</exception>
public static void SaveChanges(this IConfiguration config) {
var provider = config.GetJsonConfigurationProvider();
if (provider is null) throw new InvalidOperationException("Can't get JsonConfigurationProvider");
var sourceJson = File.ReadAllText(provider.Source.Path);
var target = JObject.Parse(sourceJson);
var stack = new Stack<IConfigurationSection>();
foreach (IConfigurationSection section in config.GetChildren().Reverse()) stack.Push(section);
while (stack.TryPop(out var node)) {
var children = node.GetChildren();
if (children.Any()) foreach (var child in children.Reverse()) stack.Push(child);
else {
var jPath = GetJPath(node.Path);
var element = target.SelectToken(jPath);
var valueString =
element!.Type == JTokenType.Null
? "null" :
element!.Type == JTokenType.String ? $"\"{node.Value}\"" : node.Value;
element!.Replace(JToken.Parse(valueString));
}
}
File.WriteAllText(provider.Source.Path, target.ToString());
}
/// <summary>
/// Sets <paramref name="configuration"/>'s <paramref name="key"/> with specified <paramref name="value"/>.
/// </summary>
/// <param name="configuration">The configuration.</param>
/// <param name="key">The key of the configuration section.</param>
/// <param name="value">Value to set.</param>
/// <exception cref="InvalidOperationException">Not supported type as value.</exception>
public static void SetValue(this IConfiguration configuration, string key, object? value) {
var c = CultureInfo.InvariantCulture;
var valueString = value switch {
null => null,
string v => v,
Uri v => v.ToString(),
byte[] v => Convert.ToBase64String(v),
bool v => v.ToString(c),
int v => v.ToString(c),
decimal v => v.ToString(c),
double v => v.ToString(c),
uint v => v.ToString(c),
long v => v.ToString(c),
ulong v => v.ToString(c),
short v => v.ToString(c),
ushort v => v.ToString(c),
byte v => v.ToString(c),
sbyte v => v.ToString(c),
float v => v.ToString(c),
_ => throw new InvalidOperationException($"Cannot set value of type {value.GetType()}")
};
configuration[key] = valueString;
}
/// <summary>
/// Gets the path for JObject.SelectToken method.
/// </summary>
/// <param name="path"><see cref="IConfiguration"/> path.</param>
/// <returns><see cref="JObject"/> path.</returns>
private static string GetJPath(string path) => RxIConfigurationIndex.Replace(path, "[$1]").Replace(':', '.');
/// <summary>
/// Matches the <see cref="IConfiguration"/> indices.
/// </summary>
private static readonly Regex RxIConfigurationIndex = new(#":(\d+)", RegexOptions.Compiled);
}
Why JObject? Can JSON file represent only an object? No - it can represent any value, including null. But the JSON configuration MUST be an object. That's why I use JObject as my secondary configuration root.
Related
I have a this model:
public partial class UserPosition
{
...
public DbGeometry Position { get; set; }
...
}
I create this object and save it to the MySql DB (db is my DBContext):
var pos = new UserPosition
{
...
Position = GeographyHelper.CreatePoint(request.Latitude, request.Longitude),
...
};
db.UserPositions.Add(pos);
db.SaveChanges();
GeographyHelper.CreatePoint:
/// <summary>
/// Creates a DbGeometry point from latitude and longitude values
/// </summary>
/// <param name="latitude"></param>
/// <param name="longitude"></param>
/// <returns></returns>
public static DbGeometry CreatePoint(double latitude, double longitude)
{
var text = string.Format(CultureInfo.InvariantCulture.NumberFormat, "POINT({0} {1})", longitude, latitude);
// 4326 is most common coordinate system used by GPS/Maps
return DbGeometry.PointFromText(text, 4326);
}
The value is perfectly saved to to DB:
POINT (19.038934 47.531)
But when I read the value from the DB, the Position is null, other fields are OK:
var rec = db.UserPositions.FirstOrDefault()
I get the right record because the ID matches.
Why is the Position null?
Visual Studio 2015, .NET 4.5.2, EntityFramework 6.2.0, MySql.Data.Entity.EF6 6.9.12, MySql database version: 5.6.15.
It was globalization problem: decimal point was saved to the DB but couldn't parse the same geometry data when read because decimal comma was needed (for Double.TryParse()) because of the globalization. The fast solution was to set the globalization to en-US in web.config but I accept if you know better solution :)
<configuration>
<system.web>
<globalization uiCulture="en-US" culture="en-US"/>
...
I am making an app of drinks for windows store.
According to requirement user can select drink as favorite.
So his favorite drinks should be shown in favorite page.
So how can I add these drinks to favorite page on button click as shown in image 1
Is it possible without using database..?
Any share of idea would be appreciated.
I am using xml file to save data on button click
I have managed to get the data from xml file in a grid on my favourite page
but it is statically done by me as I had wrote xml file by myself.
I want it to be wrote like that:
<drink>
<drinkImage>ck.png</drinkImage>
<drinkTitle>COKE</drinkTitle>
<drinkDescription>(1793-1844)</drinkDescription>
</drink>
my current file is this:
<?xml version="1.0" encoding="utf-8" ?>
<drinks>
<drink>
<drinkImage>pepsi.png</drinkImage>
<drinkTitle>PEPSI</drinkTitle>
<drinkDescription>(1793-1844)</drinkDescription>
</drink>
**<here I Want above xml on add to my favourite button click>**
</drinks>
The solution you're looking for really depends on what it is that you're wanting to get out of the adding to favourites page.
If you just want to add it to the favourites page for the duration of the app, have a ViewModel which contains the collection of favourites that you can access from any page by storing it in an IOC container (possibly using MVVMLight).
If you're wanting to then save it, you can write the favourites out to a JSON file which you can store in the local storage for the application. You'll also want to load it back into your app next time it loads.
You can do your JSON save logic as below
/// <summary>
/// Save an object of a given type as JSON to a file in the storage folder with the specified name.
/// </summary>
/// <typeparam name="T">The type of object</typeparam>
/// <param name="folder">Folder to store the file in</param>
/// <param name="data">The object to save to the file</param>
/// <param name="encoding">The encoding to save as</param>
/// <param name="fileName">The name given to the saved file</param>
/// <returns>Returns the created file.</returns>
public async Task<StorageFile> SaveAsJsonToStorageFolder<T>(StorageFolder folder, T data, Encoding encoding, string fileName)
{
if (folder == null)
throw new ArgumentNullException("folder");
if (data == null)
throw new ArgumentNullException("data");
if (fileName == null)
throw new ArgumentNullException("fileName");
string json = JsonConvert.SerializeObject(data, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All });
byte[] bytes = encoding.GetBytes(json);
return await this.SaveBytesToStorageFolder(folder, bytes, fileName);
}
/// <summary>
/// Saves a byte array to a file in the storage folder with the specified name.
/// </summary>
/// <param name="folder">Folder to store the file in</param>
/// <param name="bytes">Bytes to save to file</param>
/// <param name="fileName">Name to assign to the file</param>
/// <returns>Returns the created file.</returns>
public async Task<StorageFile> SaveBytesToStorageFolder(StorageFolder folder, byte[] bytes, string fileName)
{
if (folder == null)
throw new ArgumentNullException("folder");
if (bytes == null)
throw new ArgumentNullException("bytes");
if (string.IsNullOrWhiteSpace(fileName))
throw new ArgumentNullException("fileName");
StorageFile file = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
await FileIO.WriteBytesAsync(file, bytes);
return file;
}
Having a horrible time reading a custom configuration section from web.config:
I am using Configuration Section Designer (http://csd.codeplex.com/).
UPDATE:
here is the error I am getting:
System.Configuration.ConfigurationErrorsException: An error occurred creating the configuration section handler for SendToTestConfig/sendToTestIndexConfig: No parameterless constructor defined for this object. (C:\TFS\Mainline\Business.Utility.SendToTest\Business.Utility.SendToTest\web.config line 20) ---> System.MissingMethodException: No parameterless constructor defined for this object.
at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandle& ctor, Boolean& bNeedSecurityCheck)
at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean fillCache)
at System.RuntimeType.CreateInstanceImpl(Boolean publicOnly, Boolean skipVisibilityChecks, Boolean fillCache)
Here is my auto-generated configuration section:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.225
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Business.Utility.SendToTest.Configuration
{
/// <summary>
/// The SendToTestIndexConfig Configuration Section.
/// </summary>
public partial class SendToTestIndexConfig : global::System.Configuration.ConfigurationSection
{
#region Singleton Instance
/// <summary>
/// The XML name of the SendToTestIndexConfig Configuration Section.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("ConfigurationSectionDesigner.CsdFileGenerator", "2.0.0.0")]
internal const string SendToTestIndexConfigSectionName = "sendToTestIndexConfig";
/// <summary>
/// Gets the SendToTestIndexConfig instance.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("ConfigurationSectionDesigner.CsdFileGenerator", "2.0.0.0")]
public static global::Business.Utility.SendToTest.Configuration.SendToTestIndexConfig Instance
{
get
{
return ((global::Business.Utility.SendToTest.Configuration.SendToTestIndexConfig)(global::System.Configuration.ConfigurationManager.GetSection(global::Business.Utility.SendToTest.Configuration.SendToTestIndexConfig.SendToTestIndexConfigSectionName)));
}
}
#endregion
#region Xmlns Property
/// <summary>
/// The XML name of the <see cref="Xmlns"/> property.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("ConfigurationSectionDesigner.CsdFileGenerator", "2.0.0.0")]
internal const string XmlnsPropertyName = "xmlns";
/// <summary>
/// Gets the XML namespace of this Configuration Section.
/// </summary>
/// <remarks>
/// This property makes sure that if the configuration file contains the XML namespace,
/// the parser doesn't throw an exception because it encounters the unknown "xmlns" attribute.
/// </remarks>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("ConfigurationSectionDesigner.CsdFileGenerator", "2.0.0.0")]
[global::System.Configuration.ConfigurationPropertyAttribute(global::Business.Utility.SendToTest.Configuration.SendToTestIndexConfig.XmlnsPropertyName, IsRequired=false, IsKey=false, IsDefaultCollection=false)]
public string Xmlns
{
get
{
return ((string)(base[global::Business.Utility.SendToTest.Configuration.SendToTestIndexConfig.XmlnsPropertyName]));
}
}
#endregion
#region IsReadOnly override
/// <summary>
/// Gets a value indicating whether the element is read-only.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("ConfigurationSectionDesigner.CsdFileGenerator", "2.0.0.0")]
public override bool IsReadOnly()
{
return false;
}
#endregion
#region appGroups Property
/// <summary>
/// The XML name of the <see cref="appGroups"/> property.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("ConfigurationSectionDesigner.CsdFileGenerator", "2.0.0.0")]
internal const string appGroupsPropertyName = "appGroups";
/// <summary>
/// Gets or sets the appGroups.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("ConfigurationSectionDesigner.CsdFileGenerator", "2.0.0.0")]
[global::System.ComponentModel.DescriptionAttribute("The appGroups.")]
[global::System.Configuration.ConfigurationPropertyAttribute(global::Business.Utility.SendToTest.Configuration.SendToTestIndexConfig.appGroupsPropertyName, IsRequired=true, IsKey=false, IsDefaultCollection=false)]
public global::Business.Utility.SendToTest.Configuration.AppGroupSettingsCollection appGroups
{
get
{
return ((global::Business.Utility.SendToTest.Configuration.AppGroupSettingsCollection)(base[global::Business.Utility.SendToTest.Configuration.SendToTestIndexConfig.appGroupsPropertyName]));
}
set
{
base[global::Business.Utility.SendToTest.Configuration.SendToTestIndexConfig.appGroupsPropertyName] = value;
}
}
#endregion
}
}
namespace Business.Utility.SendToTest.Configuration
{
...
And here is my config file:
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="SendToTestConfig" type="System.Configuration.ConfigurationSectionGroup, System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<section name="sendToTestIndexConfig" type="Business.Utility.SendToTest.Configuration.SendToTestIndexConfig, Business.Utility.SendToTest.Configuration"/>
</sectionGroup>
etc.
</configSections>
<SendToTestConfig xmlns="Business.Utility.SendToTest.Configuration">
<sendToTestIndexConfig>
<appGroups>
<!-- TODO Change indexFileNamePrefix to desired value-->
<appGroupSettings name="ItemBased" indexFileNamePrefix="S" indexFolderLocation="C:\TFS\Mainline\Business.Utility.SendToTest\Business.Utility.SendToTest" imageFolderLocation="C:\TFS\Mainline\Business.Utility.SendToTest\Business.Utility.SendToTest">
<topicEntries>
<topicEntry topicIDSuffix="DATE" addDuplicateVSAMEntry="true">
<itemSubFields>
<subField index="0" typeName="Date"/>
</itemSubFields>
</topicEntry>
<topicEntry topicIDSuffix="Item" addDuplicateVSAMEntry="true">
<itemSubFields>
<subField index="0" typeName="ItemNumber"/>
</itemSubFields>
</topicEntry>
<topicEntry topicIDSuffix="DESCR">
<itemSubFields>
<subField index="0" typeName="LongDescription"/>
</itemSubFields>
</topicEntry>
</topicEntries>
</appGroupSettings>
</appGroups>
</sendToTestIndexConfig>
</SendToTestConfig>
...
</configuration>
I have tried all of the following, but I keep getting null for the first few config (which I think makes sense), and getting the abovementioned exception for the rest.
//These return null:
SendToTestIndexConfig config = SendToTestIndexConfig.Instance;
//SendToTestIndexConfig config = (SendToTestIndexConfig) ConfigurationManager.GetSection("sendToTestIndexConfig");
//SendToTestIndexConfig configb = (SendToTestIndexConfig)WebConfigurationManager.GetSection("sendToTestIndexConfig");
//SendToTestIndexConfig configc = (SendToTestIndexConfig)WebConfigurationManager.OpenWebConfiguration(null).GetSection("sendToTestIndexConfig");
//SendToTestIndexConfig configd = (SendToTestIndexConfig)WebConfigurationManager.GetWebApplicationSection("sendToTestIndexConfig");
//SendToTestIndexConfig configf = (SendToTestIndexConfig)WebConfigurationManager.GetSection("sendToTestIndexConfig");
////These throw a "parameterless constructor error" on object "SendToTestConfig/sendToTestIndexConfig"
//SendToTestIndexConfig configg = (SendToTestIndexConfig)WebConfigurationManager.GetSection("SendToTestConfig/sendToTestIndexConfig");
//SendToTestIndexConfig configh = (SendToTestIndexConfig)WebConfigurationManager.OpenWebConfiguration(null).GetSection("SendToTestConfig/sendToTestIndexConfig");
//SendToTestIndexConfig configi = (SendToTestIndexConfig)WebConfigurationManager.GetWebApplicationSection("SendToTestConfig/sendToTestIndexConfig");
//SendToTestIndexConfig configj = (SendToTestIndexConfig)WebConfigurationManager.GetSection("SendToTestConfig/sendToTestIndexConfig");
I'm guessing it has something to do with my naming. ConfigurationManager.AppSettings works fine, so I know I have the right web.config.
Actually, the solution to the problem is succinctly stated in an example for the GenericEnumConverter on MSDN. I did the same thing you probably did and explicitly set the [TypeConverter(typeof(GenericEnumConverter))] attrbibute on one of my configuration section properties and got the same error. According to the documentation for GenericEnumConverter linked to above, you do not need to set this attribute in order to use the GenericEnumConverter type converter--it is called implicitly by the framework. Remove that attribute from your configuration property specifications, and this error should disappear and everything should just work.
Here is an example of a configuration section property that uses an Enum:
public enum UsernameFormat
{
DownLevelDomainName,
UsernameOnly,
UserPrincipalName
}
public class WindowsADElement : ConfigurationElement
{
// This property will implicitly use the GenericEnumConverter type converter.
[ConfigurationProperty("usernameFormat", IsRequired=true, DefaultValue=UsernameFormat.UserPrincipalName)]
public UsernameFormat UsernameFormat
{
get { return (UsernameFormat)this["usernameFormat"]; }
set { this["usernameFormat"] = value; }
}
Then, to use it in code:
MyConfigurationSection config = ConfigurationManager.GetSection("myConfigurationSection") as MyConfigurationSection;
UsernameFormat format = config.UsernameLookup.WindowsAD.UsernameFormat;
Hope that helps.
I am not sure if this is the best solution, but I was able to get around the problem. The error was because I was using the GenericEnumTypeConverter class to convert the config strings to the AppGroup and SubFieldTypes enums. I created my own custom TypeConverters, and it solved the problem. Apparently, GenericEnumTypeConverter has no parameterless constructors, requiring the enum type for the constructor. Would love to know if there is a way to use GenericEnumTypeConverter, but this worked for me.
This clued me in on the answer: Configuration Error With Custom Behaviour
I have implemented Generic repository which only depends on IUnitOfWork (in Infrastructure.Repositories Library) and also haven't used any references to Entity Framework 4.1 dll. Entity Framework's DbContext wrapped with EFUnitOfWork which is in different class library called Infrastructure.EntityFramework. However, I came across some difficulty with Linq to Entity query that may force me to include direct dependancy with repository and EF 4.1 library.
In one of my class repository I need to use following query with a join. How can I overcome DBContext usage in my repository ?
var result = from cc in ProjectXEFDbContext.CurrentContext().PurchaseOrderLineItemCollection
join bb in GetQuery() on cc.PurchaseOrderId equals bb.Id
where bb.Id == purchaseOrder.Id && cc.Total > 50
select cc;
I made my repositories to expose IQuerables over DbContext. DbContext wrap with UnitOfWork as follows
My Repository base goes like this
/// <summary>
/// Gets the query.
/// </summary>
/// <returns></returns>
public IQueryable<TEntity> GetQuery()
{
return this.UnitOfWork.GetQuery<TEntity>();
}
/// <summary>
/// Loads the type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public IQueryable<T> LoadType<T>() where T : class
{
return this.UnitOfWork.GetQuery<T>();
}
my unit of work implementation goes here
/// <summary>
/// Gets the query.
/// </summary>
/// <typeparam name="TEntity">The type of the entity.</typeparam>
/// <returns></returns>
public IQueryable<TEntity> GetQuery<TEntity>() where TEntity:class
{
return this.DbContext.Set<TEntity>();
}
Changes to my query as follows
var result = from cc in GetQuery()
join bb in LoadType<PurchaseOrderLineItem>() on cc.Id equals bb.PurchaseOrderId
where cc.Id == purchaseOrder.Id && bb.Total > 50
select bb;
I have found an extension method that handles sorting and paging for LINQ. While this works well, I am trying to see if there are some other ways I can use this.
At present, the code for the extension method is as follows:
public static IQueryable<T> Page<T, TResult>(
this IQueryable<T> obj,
int page,
int pageSize,
System.Linq.Expressions.Expression<Func<T, TResult>> keySelector,
bool asc,
out int rowsCount)
{
rowsCount = obj.Count();
int innerRows = (page - 1) * pageSize;
if (asc)
return obj.OrderBy(keySelector).Skip(innerRows).Take(pageSize).AsQueryable();
else
return obj.OrderByDescending(keySelector).Skip(innerRows).Take(pageSize).AsQueryable();
}
The method takes in an expression, which is based off the type.
In my Dealer class, I have a method GetDealers, which essentially calls this,
i.e.
db.User.Page(1, 2, p => p.User.UserProperty.Name, true, out rowCount)
From the presentation side of things though, I do not know or can access the expression as above, e.g.
ListView1.DataSource = users.GetDealers("SortColumn", pageNo, pageSize, out rowCount, bool asc);
ListView1.DataBind();
The only way is to have a switch statement in my GetDealers method that would then convert to the expression. Is there a way to bypass this, or is this method OK?
I'm not exactly sure what you're asking, but I believe it's something that I have looked into myself. If you would like to know how to dynamically sort your results based on a string, rather than a proper LINQ expression, then you're in luck.
Scott Guthrie published a great article on that very topic. It references a Microsoft file which extends any IQueryable object to support dynamic sorting.
C# Dynamic Query Library (included in the \LinqSamples\DynamicQuery directory). Just add the page to your App_Code folder and include "Using System.Linq.Dynamic" in your project and you will be able to use the following syntax:
myUsers = myUsers.OrderBy("LastName");
I hope this helps!
If you are looking for extension method to work on all types
public static class SortingAndPagingHelper
{
/// <summary>
/// Returns the list of items of type on which method called
/// </summary>
/// <typeparam name="TSource">This helper can be invoked on IEnumerable type.</typeparam>
/// <param name="source">instance on which this helper is invoked.</param>
/// <param name="sortingModal">Page no</param>
/// <returns>List of items after query being executed on</returns>
public static IEnumerable<TSource> SortingAndPaging<TSource>(this IEnumerable<TSource> source, SortingAndPagingInfo sortingModal)
{
// Gets the coloumn name that sorting to be done o`enter code here`n
PropertyInfo propertyInfo = source.GetType().GetGenericArguments()[0].GetProperty(sortingModal.SortColumnName);
// sorts by ascending if sort criteria is Ascending otherwise sorts descending
return sortingModal.SortOrder == "Ascending" ? source.OrderByDescending(x => propertyInfo.GetValue(x, null)).Skip(sortingModal.PageSelected * sortingModal.PageSize).Take(sortingModal.PageSize)
: source.OrderBy(x => propertyInfo.GetValue(x, null)).Skip(sortingModal.PageSelected * sortingModal.PageSize).Take(sortingModal.PageSize);
}
}
DbContext dbContext = new DbContext();
dbContext.rainingSessions.Where(x => x.RegistrationDeadline > DateTime.Now) .SortingAndPaging(sortAndPagingInfo).ToList()