XML Serialization - Is it possible to serialize a model in this way? - serialization

I have the following model:
public class ABaseObject
{
private Guid id = Guid.NewGuid();
public ABaseObject()
{
}
public Guid ID { get { return id; } }
}
public class ADerivedObject : ABaseObject
{
public ADerivedObject()
{
}
public string Name { get; set; }
}
public class AObjectCollection<T>
{
private List<T> items = new List<T>();
public AObjectCollection()
{
}
public IEnumerable<T> Items { get { return items; } }
public void Add(T item)
{
items.Add(item);
}
public void Save(string filePath)
{
FileStream writer = new FileStream(filePath, FileMode.Create);
DataContractSerializer s = new DataContractSerializer(typeof(T));
s.WriteObject(writer, this);
writer.Close();
}
public void Load(string filePath)
{
FileStream fs = new FileStream(filePath, FileMode.Open);
XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(fs, new XmlDictionaryReaderQuotas());
DataContractSerializer ser = new DataContractSerializer(typeof(T));
// Deserialize the data and read it from the instance.
T deserializedObj = (T)ser.ReadObject(reader, true);
reader.Close();
fs.Close();
}
}
So basically I want to be able to do the following:
var der = new ADerivedObject();
der.Name = "Test";
var col = new AObjectCollection<ADerivedObject>();
col.Add(der);
col.Save("C:\MyCollection.xml");
...
var col2 = new AObjectCollection<ADerivedObject>();
col2.Load("C:\MyCollection.xml");
When serialized it should look something like:
<Collection>
<Item>
<ID></ID>
<Name></Name>
</Item>
</Collection>
I have played around with DataContracts and XmlSerializer but I can't quite seem to find a way to do it.

This code:
public class ABaseObject
{
public ABaseObject() { }
public Guid ID { get; set;}
}
[XmlType("Item")]
public class ADerivedObject : ABaseObject
{
public ADerivedObject() {}
public string Name { get; set; }
}
[XmlType("Collection")]
public class AObjectCollection<T>
{
private System.Xml.Serialization.XmlSerializerNamespaces ns;
private System.Xml.Serialization.XmlSerializer s;
public AObjectCollection()
{
ns= new System.Xml.Serialization.XmlSerializerNamespaces();
ns.Add( "", "");
s= new System.Xml.Serialization.XmlSerializer(this.GetType());
Items = new List<T>();
}
public List<T> Items { get; set; }
public DateTime LastSaved { get;set; }
public void Add(T item)
{
Items.Add(item);
}
public void Save(string filePath)
{
LastSaved= System.DateTime.Now;
var xmlws = new System.Xml.XmlWriterSettings { OmitXmlDeclaration = true, Indent= true };
using ( var writer = System.Xml.XmlWriter.Create(filePath, xmlws))
{
s.Serialize(writer, this, ns);
}
}
public static AObjectCollection<T2> Load<T2>(string filepath)
{
AObjectCollection<T2> obj;
try
{
var s= new System.Xml.Serialization.XmlSerializer(typeof(AObjectCollection<T2>));
using(System.IO.StreamReader reader= System.IO.File.OpenText(filepath))
{
obj= (AObjectCollection<T2>) s.Deserialize(reader);
}
}
catch
{
obj= new AObjectCollection<T2>();
}
return obj;
}
}
produces this output:
<Collection>
<Items>
<Item>
<ID>00000000-0000-0000-0000-000000000000</ID>
<Name>Test</Name>
</Item>
</Items>
<LastSaved>2010-02-04T07:32:05.812-05:00</LastSaved>
</Collection>
There are ways to tweak the XML to remove the Collection/Items layer. Search SO for other Collection / Serialization questions.

Related

How to implement growing object with a design pattern?

I want to design a response and design it by responses in my asp.net core application. The simlpe response is like following.
public class Response {
public string Status { get; set; } => "Ok";
public BaseReport BaseReport { get;set;}
}
if user sends extra parameters to my service, I want to change my response content dynamically.
public class ReportsController : ControllerBase
{
[HttpGet]
public ActionResult<Response> GetReport(bool isEmployee, bool isFinanace, bool isInformatinTech)
{
// if all parameters fals, return base report.
var report = baseReposrService.Get();
var response = new Response() { BaseReport = report };
if(isEmployee)
{
var ereport = employeeService.Get();
var response = new Response() {
BaseReport = report,
EmployeeReport = ereport
};
}
if(isFinanace)
{
var freport = financeService.Get();
var response = new Response() {
BaseReport = report,
EmployeeReport = freport
};
}
...
...
}
}
the response object is growing by query parameters.
So, is implementing the decorator pattern for this problem right way? Is there any best practice for these type problems?
I tried as below:
public class Response
{
public Response()
{
BaseReport = new BaseReport();
}
public string Status { get; set; } ="Ok";
public BaseReport BaseReport { get; set; }
}
public class BaseReport
{
public string report { get; set; }
public string reportcontent { get; set; }
}
public interface IResponseFactory
{
IResponseFactory Add(string key);
Response Create(string key);
}
public class ResponseFactory1 : IResponseFactory
{
private readonly Dictionary<string, Response> _responsedic = new Dictionary<string, Response>();
public ResponseFactory1()
{
this.Add("Employee").Add("Finanace").Add("InformatinTech");
}
public IResponseFactory Add(string key)
{
var response = new Response();
response.BaseReport.report = key;
response.BaseReport.reportcontent = key + "content";
_responsedic.Add(key, response);
return this;
}
public Response Create(string responsename)
{
if (_responsedic.ContainsKey(responsename))
{
return _responsedic[responsename];
}
else
{
return new Response() { BaseReport = new BaseReport() { report = "basereport",reportcontent= "basereportcontent" } };
}
}
in startup class:
services.AddSingleton<IResponseFactory, ResponseFactory1>();
in controller:
[HttpGet]
public ActionResult<Response> GetReport(string responsetype)
{
var response = _responseFactory.Create(responsetype);
return response;
}

custom file validation for .net core 2.0

I am trying to make a custom file validation for my project which is based on .net core 2.
I want to validate file size and also file extension in order to prevent users from uploading large files and for example .png files.
I have searched a lot but I could not find anything that works.
Here is my file validation class :
public class FileTypeAttribute : ValidationAttribute, IClientModelValidator
{
private const int MaxSize = 1048576;
private const string _DefaultErrorMessage = "Only the following file types are allowed: {0}";
private IEnumerable<string> _ValidTypes { get; set; }
public string ValidTypes { get; set; }
public string ErrorMessageExtension { get; set; }
public string ErrorMessageSize { get; set; }
public FileTypeAttribute(string errorExtension, string errorSize, string vt)
{
ErrorMessageExtension = errorExtension;
ErrorMessageSize = errorSize;
_ValidTypes = vt.Split(',');
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
IFormFile file = value as IFormFile;
if (file != null)
{
if (!_ValidTypes.Any(e => file.FileName.EndsWith(e)))
{
return new ValidationResult(ErrorMessageExtension);
}
if (file.Length > MaxSize)
{
return new ValidationResult(ErrorMessageSize);
}
}
return ValidationResult.Success;
}
public void AddValidation(ClientModelValidationContext context)
{
MergeAttribute(context.Attributes, "data-val", "true");
var errorMessage = FormatErrorMessage(context.ModelMetadata.GetDisplayName());
MergeAttribute(context.Attributes, "data-val-fileextensions", ErrorMessageExtension);
MergeAttribute(context.Attributes, "data-val-maxfilesize", ErrorMessageSize);
}
private bool MergeAttribute(
IDictionary<string, string> attributes, string key, string value)
{
if (attributes.ContainsKey(key))
{
return false;
}
attributes.Add(key, value);
return true;
}
}
and here is the javascript code in my view:
$.validator.addMethod("FileType",
function (value, element, param) {
for (var i = 0; i < element.files.length; i++) {
var extension = getFileExtension(element.files[0].name);
if ($.inArray(extension, param.validtypes) === -1) {
return false;
}
}
return true;
});
$.validator.unobtrusive.adapters.add('FileType', ['validtypes'], function (options) {
console.log("value:");
options.rules.cannotbered = {};
options.messages["FileType"] = options.message;
});
function getFileExtension(fileName) {
if (/[.]/.exec(fileName)) {
return /[^.]+$/.exec(fileName)[0].toLowerCase();
}
return null;
}
and here is the entity class code in my project:
public class MyEntityClass
{
public int MyEntityClassId { get; set; }
[FileType("invalid format", "invalid size", "jpg,png,gif")]
public IFormFile Photo { get; set; }
}
Can anyone help me to know where the problem is?
Thanks in advance.

automatically expand related entity with OData controller

I have these classes:
public class Items
{
[Key]
public Guid Id { get; set; }
public string ItemCode { get; set; }
public decimal SalesPriceExcl { get; set; }
public decimal SalesPriceIncl { get; set; }
public virtual ICollection<ItemPrice> SalesPrices { get; set; }
public Items()
{
SalesPrices = new HashSet<App4Sales_ItemPrice>();
}
}
public class ItemPrice
{
[Key, Column(Order = 0), ForeignKey("Items")]
public Guid Id { get; set; }
public virtual Items Items { get; set; }
[Key, Column(Order=1)]
public Guid PriceList { get; set; }
public decimal PriceExcl { get; set; }
public decimal PriceIncl { get; set; }
public decimal VatPercentage { get; set; }
}
I want to query the Items and automatically get the ItemPrice collection.
I've created an OData V3 controller:
// GET: odata/Items
//[Queryable]
public IQueryable<Items> GetItems(ODataQueryOptions opts)
{
SelectExpandQueryOption expandOpts = new SelectExpandQueryOption(null, "SalesPrices", opts.Context);
Request.SetSelectExpandClause(expandOpts.SelectExpandClause);
return expandOpts.ApplyTo(db.Items.AsQueryable(), new ODataQuerySettings()) as IQueryable<Items>;
}
But I get the error:
"Cannot serialize null feed"
Yes, some Items have no ItemPrice list.
Can I get past this error, or can I do something different?
Kind regards
Jeroen
I found the underlying error is:
Unable to cast object of type
'System.Data.Entity.Infrastructure.DbQuery1[System.Web.Http.OData.Query.Expressions.SelectExpandBinder+SelectAllAndExpand1[.Models.Items]]'
to type '.Models.Items'.
I've solved it after I came across this post: http://www.jauernig-it.de/intercepting-and-post-processing-odata-queries-on-the-server/
This is my controller now:
SelectExpandQueryOption expandOpts = new SelectExpandQueryOption(null, "SalesPrices", opts.Context);
Request.SetSelectExpandClause(expandOpts.SelectExpandClause);
var result = expandOpts.ApplyTo(db.Items.AsQueryable(), new ODataQuerySettings());
var resultList = new List<Items>();
foreach (var item in result)
{
if (item is Items)
{
resultList.Add((Items)item);
}
else if (item.GetType().Name == "SelectAllAndExpand`1")
{
var entityProperty = item.GetType().GetProperty("Instance");
resultList.Add((Items)entityProperty.GetValue(item));
}
}
return resultList.AsQueryable();
Jeroen
GetItems([FromODataUri] ODataQueryOptions queryOptions)
expanding on Jeroen's post. Anytime a select or expand is involved, OData wraps the results in a SelectAll or SelectSome object; so, we need to unwrap the values rather than do an direct cast.
public static class ODataQueryOptionsExtensions
{
public static IEnumerable<T> ApplyODataOptions<T>(this IQueryable<T> query, ODataQueryOptions options) where T : class, new()
{
if (options == null)
{
return query;
}
var queryable = options.ApplyTo(query);
if (queryable is IQueryable<T> queriableEntity)
{
return queriableEntity.AsEnumerable();
}
return UnwrapAll<T>(queryable).ToList();
}
public static IEnumerable<T> UnwrapAll<T>(this IQueryable queryable) where T : class, new()
{
foreach (var item in queryable)
{
yield return Unwrap<T>(item);
}
}
public static T Unwrap<T>(object item) where T : class, new()
{
var instanceProp = item.GetType().GetProperty("Instance");
var value = (T)instanceProp.GetValue(item);
if (value != null)
{
return value;
}
value = new T();
var containerProp = item.GetType().GetProperty("Container");
var container = containerProp.GetValue(item);
if (container == null)
{
return (T)null;
}
var containerType = container.GetType();
var containerItem = container;
var allNull = true;
for (var i = 0; containerItem != null; i++)
{
var containerItemType = containerItem.GetType();
var containerItemValue = containerItemType.GetProperty("Value").GetValue(containerItem);
if (containerItemValue == null)
{
containerItem = containerType.GetProperty($"Next{i}")?.GetValue(container);
continue;
}
var containerItemName = containerItemType.GetProperty("Name").GetValue(containerItem) as string;
var expandedProp = typeof(T).GetProperty(containerItemName);
if (expandedProp.SetMethod == null)
{
containerItem = containerType.GetProperty($"Next{i}")?.GetValue(container);
continue;
}
if (containerItemValue.GetType() != typeof(string) && containerItemValue is IEnumerable containerValues)
{
var listType = typeof(List<>).MakeGenericType(expandedProp.PropertyType.GenericTypeArguments[0]);
var expandedList = (IList)Activator.CreateInstance(listType);
foreach (var expandedItem in containerValues)
{
var expandedInstanceProp = expandedItem.GetType().GetProperty("Instance");
var expandedValue = expandedInstanceProp.GetValue(expandedItem);
expandedList.Add(expandedValue);
}
expandedProp.SetValue(value, expandedList);
allNull = false;
}
else
{
var expandedInstanceProp = containerItemValue.GetType().GetProperty("Instance");
if (expandedInstanceProp == null)
{
expandedProp.SetValue(value, containerItemValue);
allNull = false;
}
else
{
var expandedValue = expandedInstanceProp.GetValue(containerItemValue);
if (expandedValue != null)
{
expandedProp.SetValue(value, expandedValue);
allNull = false;
}
else
{
var t = containerItemValue.GetType().GenericTypeArguments[0];
var wrapInfo = typeof(ODataQueryOptionsExtensions).GetMethod(nameof(Unwrap));
var wrapT = wrapInfo.MakeGenericMethod(t);
expandedValue = wrapT.Invoke(null, new[] { containerItemValue });
if (expandedValue != null)
{
expandedProp.SetValue(value, expandedValue);
allNull = false;
}
}
}
}
containerItem = containerType.GetProperty($"Next{i}")?.GetValue(container);
}
if (allNull)
{
return (T)null;
}
return value;
}
}

How hashtable is working in WCF?

I have a question.
Consider the below
IService1.cs
[ServiceContract]
public interface IService1
{
[OperationContract]
Hashtable GetHashTableCollection();
[OperationContract]
List<A> GetARecords();
}
[DataContract]
public class A
{
[DataMember]
public int MyProperty { get; set; }
[DataMember]
public Hashtable MyTable { get; set; }
}
Service1.cs
public class Service1 : IService1
{
public Hashtable GetHashTableCollection()
{
Hashtable hashtable = new Hashtable();
hashtable.Add("Area", 1000);
hashtable.Add("Perimeter", 55);
hashtable.Add("Mortgage", 540);
return hashtable;
}
public List<A> GetARecords()
{
List<A> Alist = new List<A>();
Alist.Add(new A { MyProperty = 1, MyTable = GetHashTableCollection() });
Alist.Add(new A { MyProperty = 2, MyTable = GetHashTableCollection() });
return Alist;
}
}
And the client Application is as under
private void button1_Click(object sender, EventArgs e)
{
ServiceReference1.Service1Client sc = new ServiceReference1.Service1Client();
var r1 = sc.GetHashTableCollection();
var r2 = sc.GetARecords();
}
It is working fine without any problem. The HashTable has been converted to Dictionary object.
I was under the impression that since HashTable uses IDictionary, so it should fail at compile time(happened in 3.5).
Cannot implicitly convert type 'System.Collections.Generic.Dictionary' to 'System.Collections.Hashtable'
However, it worked. How?
Am I missing any basic concept? Or DataContractSerializer is doing the work? Or something has changed in dot.net 4.0?
What it is and why it is working?
It's based on IDictionary. WCF treats IDictionary as a Dictionary<object, object> and it's exposed as such on the WSDL. You could have a custom class which implemented IDictionary, and the behavior would be the same. For example, if you run either of the projects below, and use svcutil or Add Service Reference to generate a proxy to the service, where you had an IDictionary type, you'll get a Dictionary<object, object> in the client instead.
public class StackOverflow_15471185
{
[ServiceContract]
public interface IService1
{
[OperationContract]
Hashtable GetHashTableCollection();
[OperationContract]
List<A> GetARecords();
}
[DataContract]
public class A
{
[DataMember]
public int MyProperty { get; set; }
[DataMember]
public Hashtable MyTable { get; set; }
}
public class Service : IService1
{
public Hashtable GetHashTableCollection()
{
Hashtable hashtable = new Hashtable();
hashtable.Add("Area", 1000);
hashtable.Add("Perimeter", 55);
hashtable.Add("Mortgage", 540);
return hashtable;
}
public List<A> GetARecords()
{
List<A> Alist = new List<A>();
Alist.Add(new A { MyProperty = 1, MyTable = GetHashTableCollection() });
Alist.Add(new A { MyProperty = 2, MyTable = GetHashTableCollection() });
return Alist;
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
host.AddServiceEndpoint(typeof(IService1), new BasicHttpBinding(), "");
host.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true });
host.Open();
Console.WriteLine("Host opened");
var factory = new ChannelFactory<IService1>(new BasicHttpBinding(), new EndpointAddress(baseAddress));
var proxy = factory.CreateChannel();
Console.WriteLine(proxy.GetHashTableCollection());
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}
public class StackOverflow_15471185_b
{
[ServiceContract]
public interface IService1
{
[OperationContract]
MyDic GetHashTableCollection();
[OperationContract]
List<A> GetARecords();
}
public class MyDic : IDictionary
{
public IDictionary dic = new Hashtable();
public void Add(object key, object value)
{
dic.Add(key, value);
}
public void Clear()
{
dic.Clear();
}
public bool Contains(object key)
{
return dic.Contains(key);
}
public IDictionaryEnumerator GetEnumerator()
{
return dic.GetEnumerator();
}
public bool IsFixedSize
{
get { return dic.IsFixedSize; }
}
public bool IsReadOnly
{
get { return dic.IsReadOnly; }
}
public ICollection Keys
{
get { return dic.Keys; }
}
public void Remove(object key)
{
dic.Remove(key);
}
public ICollection Values
{
get { return dic.Values; }
}
public object this[object key]
{
get
{
return dic[key];
}
set
{
dic[key] = value;
}
}
public void CopyTo(Array array, int index)
{
dic.CopyTo(array, index);
}
public int Count
{
get { return dic.Count; }
}
public bool IsSynchronized
{
get { return dic.IsSynchronized; }
}
public object SyncRoot
{
get { return dic.SyncRoot; }
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)dic).GetEnumerator();
}
}
[DataContract]
public class A
{
[DataMember]
public int MyProperty { get; set; }
[DataMember]
public MyDic MyTable { get; set; }
}
public class Service : IService1
{
public MyDic GetHashTableCollection()
{
MyDic hashtable = new MyDic();
hashtable.Add("Area", 1000);
hashtable.Add("Perimeter", 55);
hashtable.Add("Mortgage", 540);
return hashtable;
}
public List<A> GetARecords()
{
List<A> Alist = new List<A>();
Alist.Add(new A { MyProperty = 1, MyTable = GetHashTableCollection() });
Alist.Add(new A { MyProperty = 2, MyTable = GetHashTableCollection() });
return Alist;
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
host.AddServiceEndpoint(typeof(IService1), new BasicHttpBinding(), "");
host.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true });
host.Open();
Console.WriteLine("Host opened");
var factory = new ChannelFactory<IService1>(new BasicHttpBinding(), new EndpointAddress(baseAddress));
var proxy = factory.CreateChannel();
Console.WriteLine(proxy.GetHashTableCollection());
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}

NHibernate FetchMode.Lazy

I have an object which has a property on it that has then has collections which i would like to not load in a couple situations. 98% of the time i want those collections fetched but in the one instance i do not. Here is the code I have... Why does it not set the fetch mode on the properties collections?
[DataContract(Name = "ThemingJob", Namespace = "")]
[Serializable]
public class ThemingJob : ServiceJob
{
[DataMember]
public virtual Query Query { get; set; }
[DataMember]
public string Results { get; set; }
}
[DataContract(Name = "Query", Namespace = "")]
[Serializable]
public class Query : LookupEntity<Query>, DAC.US.Search.Models.IQueryEntity
{
[DataMember]
public string QueryResult { get; set; }
private IList<Asset> _Assets = new List<Asset>();
[IgnoreDataMember]
[System.Xml.Serialization.XmlIgnore]
public IList<Asset> Assets { get { return _Assets; } set { _Assets = value; } }
private IList<Theme> _Themes = new List<Theme>();
[IgnoreDataMember]
[System.Xml.Serialization.XmlIgnore]
public IList<Theme> Themes { get { return _Themes; } set { _Themes = value; } }
private IList<Affinity> _Affinity = new List<Affinity>();
[IgnoreDataMember]
[System.Xml.Serialization.XmlIgnore]
public IList<Affinity> Affinity { get { return _Affinity; } set { _Affinity = value; } }
private IList<Word> _Words = new List<Word>();
[IgnoreDataMember]
[System.Xml.Serialization.XmlIgnore]
public IList<Word> Words { get { return _Words; } set { _Words = value; } }
}
using (global::NHibernate.ISession session = NHibernateApplication.GetCurrentSession())
{
global::NHibernate.ICriteria criteria = session.CreateCriteria(typeof(ThemingJob));
global::NHibernate.ICriteria countCriteria = session.CreateCriteria(typeof(ThemingJob));
criteria.AddOrder(global::NHibernate.Criterion.Order.Desc("Id"));
var qc = criteria.CreateCriteria("Query");
qc.SetFetchMode("Assets", global::NHibernate.FetchMode.Lazy);
qc.SetFetchMode("Themes", global::NHibernate.FetchMode.Lazy);
qc.SetFetchMode("Affinity", global::NHibernate.FetchMode.Lazy);
qc.SetFetchMode("Words", global::NHibernate.FetchMode.Lazy);
pageIndex = Convert.ToInt32(pageIndex) - 1; // convert to 0 based paging index
criteria.SetMaxResults(pageSize);
criteria.SetFirstResult(pageIndex * pageSize);
countCriteria.SetProjection(global::NHibernate.Criterion.Projections.RowCount());
int totalRecords = (int)countCriteria.List()[0];
return criteria.List<ThemingJob>().ToPagedList<ThemingJob>(pageIndex, pageSize, totalRecords);
}