Getting Display Name from PackageID - wix

Looking through the source of the Wix Standard Bootstrapper application, it appears that each package has a DisplayName property:
pPackage->sczDisplayName
However, the BootstrapperCore dll that is used in the WiX Setup project does not have this property. Is there any way to extract this property from the bundles in managed code?

I ported the Bal code into C#, trying to make it work exactly like the C++ code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Xml;
using System.Xml.XPath;
public class BootstrapperApplicationData
{
public const string defaultFileName = "BootstrapperApplicationData.xml";
public const string xmlNamespace =
"http://schemas.microsoft.com/wix/2010/BootstrapperApplicationData";
private static DirectoryInfo defaultFolder;
public static DirectoryInfo DefaultFolder
{
get
{
if (defaultFolder == null)
{
defaultFolder = (new FileInfo(Assembly.GetExecutingAssembly().Location)).Directory;
}
return defaultFolder;
}
}
private static FileInfo defaultFile;
public static FileInfo DefaultFile
{
get
{
if (defaultFile == null)
{
defaultFile = new FileInfo(Path.Combine(DefaultFolder.FullName, defaultFileName));
}
return defaultFile;
}
}
public FileInfo DataFile { get; protected set; }
public Bundle Data { get; protected set; }
public BootstrapperApplicationData() : this(DefaultFile) { }
public BootstrapperApplicationData(FileInfo fiBootstrapperApplicationData)
{
DataFile = fiBootstrapperApplicationData;
using (FileStream fs = DataFile.OpenRead())
{
Data = ParseBundleFromStream(fs);
}
}
public static Bundle ParseBundleFromStream(Stream stream)
{
XPathDocument manifest = new XPathDocument(stream);
XPathNavigator root = manifest.CreateNavigator();
return ParseBundleFromXml(root);
}
public static Bundle ParseBundleFromXml(XPathNavigator root)
{
Bundle bundle = new Bundle();
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(root.NameTable);
namespaceManager.AddNamespace("p", xmlNamespace);
XPathNavigator bundleNode = root.SelectSingleNode("/p:BootstrapperApplicationData/p:WixBundleProperties", namespaceManager);
if (bundleNode == null)
{
throw new Exception("Failed to select bundle information");
}
bool? perMachine = GetYesNoAttribute(bundleNode, "PerMachine");
if (perMachine.HasValue)
{
bundle.PerMachine = perMachine.Value;
}
string name = GetAttribute(bundleNode, "DisplayName");
if (name != null)
{
bundle.Name = name;
}
string logVariable = GetAttribute(bundleNode, "LogPathVariable");
if (logVariable != null)
{
bundle.LogVariable = logVariable;
}
else
{
//wix would actually debug "Failed to select bundle information" and return with E_NOTFOUND, but I think it's a (harmless) bug
}
Package[] packages = ParsePackagesFromXml(root);
bundle.Packages = packages;
return bundle;
}
public static Package[] ParsePackagesFromXml(XPathNavigator root)
{
List<Package> packages = new List<Package>();
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(root.NameTable);
namespaceManager.AddNamespace("p", xmlNamespace);
XPathNodeIterator nodes = root.Select("/p:BootstrapperApplicationData/p:WixPackageProperties", namespaceManager);
foreach (XPathNavigator node in nodes)
{
Package package = new Package();
string id = GetAttribute(node, "Package");
if (id == null)
{
throw new Exception("Failed to get package identifier for package");
}
package.Id = id;
string displayName = GetAttribute(node, "DisplayName");
if (displayName != null)
{
package.DisplayName = displayName;
}
string description = GetAttribute(node, "Description");
if (description != null)
{
package.Description = description;
}
PackageType? packageType = GetPackageTypeAttribute(node, "PackageType");
if (!packageType.HasValue)
{
throw new Exception("Failed to get package type for package");
}
package.Type = packageType.Value;
bool? permanent = GetYesNoAttribute(node, "Permanent");
if (!permanent.HasValue)
{
throw new Exception("Failed to get permanent settings for package");
}
package.Permanent = permanent.Value;
bool? vital = GetYesNoAttribute(node, "Vital");
if (!vital.HasValue)
{
throw new Exception("Failed to get vital setting for package");
}
package.Vital = vital.Value;
bool? displayInternalUI = GetYesNoAttribute(node, "DisplayInternalUI");
if (!displayInternalUI.HasValue)
{
throw new Exception("Failed to get DisplayInternalUI setting for package");
}
package.DisplayInternalUI = displayInternalUI.Value;
string productCode = GetAttribute(node, "ProductCode");
if (productCode != null)
{
package.ProductCode = productCode;
}
string upgradeCode = GetAttribute(node, "UpgradeCode");
if (upgradeCode != null)
{
package.UpgradeCode = upgradeCode;
}
string version = GetAttribute(node, "Version");
if (version != null)
{
package.Version = version;
}
packages.Add(package);
}
return packages.ToArray();
}
public static string GetAttribute(XPathNavigator node, string attributeName)
{
XPathNavigator attribute = node.SelectSingleNode("#" + attributeName);
if (attribute == null)
{
return null;
}
return attribute.Value;
}
public static bool? GetYesNoAttribute(XPathNavigator node, string attributeName)
{
string attributeValue = GetAttribute(node, attributeName);
if (attributeValue == null)
{
return null;
}
return attributeValue.Equals("yes", StringComparison.InvariantCulture);
}
public static PackageType? GetPackageTypeAttribute(XPathNavigator node, string attributeName)
{
string attributeValue = GetAttribute(node, attributeName);
if (attributeValue == null)
{
return null;
}
if (attributeValue.Equals("Exe", StringComparison.InvariantCulture))
{
return PackageType.EXE;
}
else if (attributeValue.Equals("Msi", StringComparison.InvariantCulture))
{
return PackageType.MSI;
}
else if (attributeValue.Equals("Msp", StringComparison.InvariantCulture))
{
return PackageType.MSP;
}
else if (attributeValue.Equals("Msu", StringComparison.InvariantCulture))
{
return PackageType.MSU;
}
else
{
return 0;
}
}
public enum PackageType
{
EXE,
MSI,
MSP,
MSU,
}
public class Package
{
public string Id;
public string DisplayName;
public string Description;
public PackageType Type;
public bool Permanent;
public bool Vital;
public bool DisplayInternalUI;
//not available until WiX 3.9.421.0
public string ProductCode;
public string UpgradeCode;
public string Version;
}
public class Bundle
{
public bool PerMachine;
public string Name;
public string LogVariable;
public Package[] Packages;
}
}

The BootstrapperApplicationData.xml file that is generated during the build process is placed next to your BA .dll. You can load that XML file to get lots of information about the bundle and packages in the bundle.
To load the BootstrapperApplicationData.xml in native code, use the BalManifestLoad() method in balutil.lib that is provided with the WiX toolset. You can see the code in src\ext\BalExtension\balutil\balutil.cpp. Then you can use BalInfoParseFromXml() also in balutil.lib to parse the XML file into a bunch of handy structs. You can see the code in src\ext\BalExtension\balutil\balinfo.cpp.

Related

Autocad.net Save complex Object

I'm working on a project in AutoCAD using c#, my application data is stored in complex objects
(String, double, objectId, arrays, list...) and I would like to save data for later using (serialize or saved in AutoCAD drawing) and if I re-open AutoCAD and reload my project, I can find all data in my object
Sorry for my English
So You need to use XData.
Details and sample You can find here:
https://www.keanw.com/2007/04/adding_xdata_to.html
You could serialize your class into a binary stream and then you can save it in the drawing as a bunch of binary chunks (see this topic)
But most of the time you should directly store data in Xrecords of a DBDictionary.
public abstract class RecordableObject
{
protected ObjectId dictionaryId;
protected Database database;
public string Key { get; }
protected RecordableObject(string key, Database db = null)
{
database = db ?? HostApplicationServices.WorkingDatabase;
Key = key;
using (var tr = database.TransactionManager.StartOpenCloseTransaction())
{
var NOD = (DBDictionary)tr.GetObject(database.NamedObjectsDictionaryId, OpenMode.ForRead);
DBDictionary dictionary;
if (NOD.Contains(Key))
{
dictionaryId = NOD.GetAt(Key);
}
else
{
NOD.UpgradeOpen();
dictionary = new DBDictionary();
dictionaryId = NOD.SetAt(Key, dictionary);
tr.AddNewlyCreatedDBObject(dictionary, true);
}
tr.Commit();
}
}
public abstract void SavePropertiesToDictionary();
public abstract void SetPropertiesFromDictionary();
protected void SaveData(string key, params TypedValue[] values)
{
using (var tr = database.TransactionManager.StartOpenCloseTransaction())
{
var dictionary = (DBDictionary)tr.GetObject(dictionaryId, OpenMode.ForRead);
Xrecord xrecord;
if (dictionary.Contains(key))
{
xrecord = (Xrecord)tr.GetObject(dictionary.GetAt(key), OpenMode.ForWrite);
}
else
{
xrecord = new Xrecord();
dictionary.UpgradeOpen();
dictionary.SetAt(key, xrecord);
tr.AddNewlyCreatedDBObject(xrecord, true);
}
xrecord.Data = new ResultBuffer(values);
tr.Commit();
}
}
protected T GetData<T>(string key)
{
using (var tr = database.TransactionManager.StartOpenCloseTransaction())
{
var dictionary = (DBDictionary)tr.GetObject(dictionaryId, OpenMode.ForRead);
if (dictionary.Contains(key))
{
var xrecord = (Xrecord)tr.GetObject(dictionary.GetAt(key), OpenMode.ForRead);
if (xrecord.Data != null)
return (T)xrecord.Data.AsArray()[0].Value;
}
return default;
}
}
protected T[] GetDataArray<T>(string key)
{
using(var tr = database.TransactionManager.StartOpenCloseTransaction())
{
var dictionary = (DBDictionary)tr.GetObject(dictionaryId, OpenMode.ForRead);
if (dictionary.Contains(key))
{
var xrecord = (Xrecord)tr.GetObject(dictionary.GetAt(key), OpenMode.ForRead);
if (xrecord.Data != null)
return xrecord.Data.AsArray().Select(tv => (T)tv.Value).ToArray();
}
return default;
}
}
}
Derived class example:
public class RecordableExample : RecordableObject
{
public double Size { get; set; }
public ObjectId ObjectId { get; set; }
public int[] Ints { get; set; }
public RecordableExample(string key, Database db = null) : base(key, db) { }
public override void SavePropertiesToDictionary()
{
SaveData(nameof(Size), new TypedValue((int)DxfCode.Real, Size));
SaveData(nameof(ObjectId), new TypedValue((int)DxfCode.Handle, ObjectId.Handle));
if (Ints != null)
SaveData(nameof(Ints), Ints.Select(i => new TypedValue((int)DxfCode.Int32, i)).ToArray());
}
public override void SetPropertiesFromDictionary()
{
Size = GetData<double>(nameof(Size));
Ints = GetDataArray<int>(nameof(Ints));
var handle = new Handle(Convert.ToInt64(GetData<string>(nameof(ObjectId))));
if (database.TryGetObjectId(handle, out var id))
ObjectId = id;
}
}

How to keep user logged in after browser is closed

Every time I close the browser I need to log in again into this app. It is developed in .NET Core 2.0. I'm trying to let it logged in, like every other regular site.
I checked this post that may be useful, but since the code is quite different from this application I decided to create this post.
This is my security code:
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.Text;
namespace Petito.Common
{
public interface IActivityContext
{
string ActivityID { get; }
IPrincipal User { get; }
}
[JsonObject(MemberSerialization = MemberSerialization.Fields)]
public class ActivityIdentity : IIdentity
{
private string _name = null;
[JsonIgnore()]
private bool _isAuthenticated = false;
[JsonIgnore()]
private string _authenticationType = "";
public ActivityIdentity()
{
}
public ActivityIdentity(string name) : this(name, false, "")
{
}
internal ActivityIdentity(string name, bool isAuthenticated, string authenticationType)
{
this._name = name;
this._isAuthenticated = isAuthenticated;
this._authenticationType = authenticationType;
}
public string Name { get => _name; }
public bool IsAuthenticated { get => _isAuthenticated; }
public string AuthenticationType { get => _authenticationType; }
public static ActivityIdentity Unathenticated => new ActivityIdentity();
}
[JsonObject(MemberSerialization = MemberSerialization.Fields)]
public class ActivityPrincipal : IPrincipal
{
private ActivityIdentity _activityIdentity = null;
private string[] _roles = null;
public ActivityPrincipal() : this(ActivityIdentity.Unathenticated, null)
{
}
public ActivityPrincipal(ActivityIdentity activityIdentity, params string[] roles)
{
_activityIdentity = activityIdentity;
_roles = roles;
}
public ActivityIdentity Identity => _activityIdentity;
IIdentity IPrincipal.Identity => _activityIdentity;
public bool IsInRole(string role)
{
if (_roles != null && _roles.Length > 0)
{
return _roles.Contains(role);
}
return false;
}
}
[JsonObject(MemberSerialization = MemberSerialization.Fields)]
public class ActivityContext : IDisposable, IActivityContext
{
private string _activityID = Guid.NewGuid().ToString();
private DateTime _startDate = DateTime.UtcNow;
private DateTime? _endDate = null;
private ActivityPrincipal _activityPrincipal = null;
public ActivityContext() : this(null)
{
}
public ActivityContext(IPrincipal principal)
{
_activityPrincipal = Convert(principal);
}
private ActivityPrincipal Convert(IPrincipal principal)
{
if (principal == null)
{
return new ActivityPrincipal();
}
var activityPrincipal = principal as ActivityPrincipal;
if (activityPrincipal != null)
{
return activityPrincipal;
}
var claimsPrincipal = principal as ClaimsPrincipal;
if (claimsPrincipal != null)
{
var roles = claimsPrincipal.Claims.Select(x => x.Value);
var p = new ActivityPrincipal(
new ActivityIdentity(claimsPrincipal.Identity.Name, claimsPrincipal.Identity.IsAuthenticated, claimsPrincipal.Identity.AuthenticationType)
, roles.ToArray()
);
return p;
}
throw new NotSupportedException($"Converting {principal.GetType()} not supported");
}
public void Dispose()
{
if (!_endDate.HasValue)
{
_endDate = DateTime.UtcNow;
}
}
public string ActivityID { get => _activityID; }
public DateTime StartDate { get => _startDate; }
public DateTime? EndDate { get => _endDate; }
public IPrincipal User
{
get
{
return _activityPrincipal;
}
}
}
}
Of course, I'll still try to figure out what's wrong with the code. Please if you need another part of the code other from that I posted let me know.
Thanks!

Setting up examples in Swagger

I am using Swashbuckle.AspNetCore.Swagger (1.0.0) and Swashbuckle.AspNetCore.SwaggerGen (1.0.0). I am trying to add default examples to my API following Default model example in Swashbuckle (Swagger). I created a new class file and added,
public class SwaggerDefaultValue : Attribute
{
public string ParameterName { get; set; }
public string Value { get; set; }
public SwaggerDefaultValue(string parameterName, string value)
{
this.ParameterName = parameterName;
this.Value = value;
}
}
public class AddDefaultValues : IOperationFilter
{
public void Apply(Operation operation, DataTypeRegistry dataTypeRegistry, ApiDescription apiDescription)
{
foreach (var param in operation.Parameters)
{
var actionParam = apiDescription.ActionDescriptor.GetParameters().First(p => p.ParameterName == param.Name);
if (actionParam != null)
{
var customAttribute = actionParam.ActionDescriptor.GetCustomAttributes<SwaggerDefaultValue>().FirstOrDefault();
if (customAttribute != null)
{
param.DefaultValue = customAttribute.Value;
}
}
}
}
}
but I get this error - AddDefaultValues does not implement interface member IOperationFilter.Apply(Operation, OperationFilterContext)
That link you are following is not for the Swashbuckle.AspNetCore version
Look in the correct project for the proper examples:
https://github.com/domaindrivendev/Swashbuckle.AspNetCore/search?q=IOperationFilter&unscoped_q=IOperationFilter

Generate share point menu based on Mapped path and retain the data through entire session

Get all the site collection inside the web application and sub sites inside the site collection and create menu base don navigation url.
http://test.com/
http://:test.com/Development
http://:test.com/Development/Project 1
http://:test.com/Development/Project 2
http://:test.com/Development/Project 3
http://:test.com/testing
http://:test.com/testing/Project 1
http://:test.com/testing/Project 2
http://:test.com/testing/Project 3
My menu should be
Development -> Project1
Project2
Project3
Testing-> Project1
Project2
Project3
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using System.Collections;
using System.Xml;
using System.Data;
namespace MasterPageWithCodeBehind.MasterPageModule
{
public class _starter : MasterPage
{
protected Menu CustomMenu;
private static IDictionary<string, XmlDocument> _Tracker = new Dictionary<string, XmlDocument> { };
protected static List<MappedPath> lstNavigationPath;
protected void Page_Load(object sender, EventArgs e)
{
XmlDataSource xmlDS = new XmlDataSource();
xmlDS.ID = "xSource1";
XmlDocument xmlData = GetMenuData(SPContext.Current.Web.CurrentUser.LoginName);
xmlDS.Data = xmlData.InnerXml;
xmlDS.XPath = "Home/Menu";
try
{
CustomMenu.DataSource = xmlDS;
CustomMenu.DataBind();
}
catch (Exception Ex)
{
}
}
public static XmlDocument GetMenuData(string loginUser)
{
//value to return
//Menu item = null;
XmlDocument item = null;
//lock collection to prevent changes during operation
lock (_Tracker)
{
//if value not found, create and add
if (!_Tracker.TryGetValue(loginUser, out item))
{
using (SPSite site = new SPSite(SPContext.Current.Site.Url))
{
SPWebApplication webapp = site.WebApplication;
item = GenerateMenu(webapp);
}
//calculate next key
string newIdent = loginUser;
//add item
_Tracker.Add(newIdent, item);
}
else
{
item = _Tracker[loginUser];
}
}
return item;
}
public static XmlDocument GenerateMenu(SPWebApplication webApp)
{
lstNavigationPath = new List<MappedPath>();
string loginUserName = SPContext.Current.Web.CurrentUser.LoginName;
SPSecurity.RunWithElevatedPrivileges(delegate()
{
foreach (SPSite site in webApp.Sites)
{
using (SPWeb oSPWeb = site.OpenWeb())
{
foreach (SPWeb web in site.AllWebs)
{
if (web.DoesUserHavePermissions(loginUserName, SPBasePermissions.Open))
{
lstNavigationPath.Add(new MappedPath()
{
Title = web.Title,
URL = web.Url,
navigationpath = web.ServerRelativeUrl
});
}
}
}
}
});
XmlDocument xDoc = new XmlDocument();
XmlNode docNode = xDoc.CreateXmlDeclaration("1.0", "UTF-8", null);
xDoc.AppendChild(docNode);
// Home Node
XmlNode homeNode = xDoc.CreateElement("Home");
xDoc.AppendChild(homeNode);
foreach (MappedPath obj in lstNavigationPath)
{
string navigationPath = obj.navigationpath.TrimStart('/');
XmlNode menuNode = null;
XmlNode parentNode = null;
if (navigationPath.Split('/').Count() == 1)
{
menuNode = xDoc.CreateElement("Menu");
}
else
{
string parentNodeString = "/" + navigationPath.Substring(0, navigationPath.LastIndexOf('/'));
parentNode = xDoc.SelectSingleNode(string.Format("Home/Menu[#navigationpath=\"{0}\"]", parentNodeString));
if (parentNode == null)
{
parentNode = xDoc.SelectSingleNode(string.Format("descendant::SubMenu[#navigationpath=\"{0}\"]", parentNodeString));
}
menuNode = xDoc.CreateElement(parentNode == null ? "Menu" : "SubMenu");
}
XmlAttribute textAttribute = xDoc.CreateAttribute("text");
textAttribute.Value = obj.Title;
menuNode.Attributes.Append(textAttribute);
XmlAttribute urlAttribute = xDoc.CreateAttribute("url");
urlAttribute.Value = obj.URL;
menuNode.Attributes.Append(urlAttribute);
XmlAttribute navigationpathAttribute = xDoc.CreateAttribute("navigationpath");
navigationpathAttribute.Value = obj.navigationpath;
menuNode.Attributes.Append(navigationpathAttribute);
if (parentNode != null)
{
parentNode.AppendChild(menuNode);
}
else
{
homeNode.AppendChild(menuNode);
}
}
return xDoc;
}
}
public class MappedPath
{
public string Title { get; set; }
public string URL { get; set; }
public string navigationpath { get; set; }
}
}

Custom model binder with inheritance using Web API and RavenDB

I'm developing a simple web app where I need to bind all types implementing and interface of a specific type. My interface has one single property like this
public interface IContent {
string Id { get;set; }
}
a common class using this interface would look like this
public class Article : IContent {
public string Id { get;set; }
public string Heading { get;set; }
}
to be clean here the article class is just one of many different classes implementing IContent so therefor I need a generic way of storing and updating these types.
So in my controller I have the put method like this
public void Put(string id, [System.Web.Http.ModelBinding.ModelBinder(typeof(ContentModelBinder))] IContent value)
{
// Store the updated object in ravendb
}
and the ContentBinder
public class ContentModelBinder : System.Web.Http.ModelBinding.IModelBinder {
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext) {
actionContext.ControllerContext.Request.Content.ReadAsAsync<Article>().ContinueWith(task =>
{
Article model = task.Result;
bindingContext.Model = model;
});
return true;
}
}
The code above does not work because it does not seem to get hold of the Heading property even though if I use the default model binder it binds the Heading correctly.
So, in the BindModel method I guess I need to load the correct object from ravendb based on the Id and then update the complex object using some kind of default model binder or so? This is where I need some help.
Marcus, following is an example which would work fine for both Json and Xml formatter.
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Runtime.Serialization;
using System.Web.Http;
using System.Web.Http.SelfHost;
namespace Service
{
class Service
{
private static HttpSelfHostServer server = null;
private static string baseAddress = string.Format("http://{0}:9095/", Environment.MachineName);
static void Main(string[] args)
{
HttpSelfHostConfiguration config = new HttpSelfHostConfiguration(baseAddress);
config.Routes.MapHttpRoute("Default", "api/{controller}/{id}", new { id = RouteParameter.Optional });
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
config.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Objects;
try
{
server = new HttpSelfHostServer(config);
server.OpenAsync().Wait();
Console.WriteLine("Service listenting at: {0} ...", baseAddress);
TestWithHttpClient("application/xml");
TestWithHttpClient("application/json");
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine("Exception Details:\n{0}", ex.ToString());
}
finally
{
if (server != null)
{
server.CloseAsync().Wait();
}
}
}
private static void TestWithHttpClient(string mediaType)
{
HttpClient client = new HttpClient();
MediaTypeFormatter formatter = null;
// NOTE: following any settings on the following formatters should match
// to the settings that the service's formatters have.
if (mediaType == "application/xml")
{
formatter = new XmlMediaTypeFormatter();
}
else if (mediaType == "application/json")
{
JsonMediaTypeFormatter jsonFormatter = new JsonMediaTypeFormatter();
jsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Objects;
formatter = jsonFormatter;
}
HttpRequestMessage request = new HttpRequestMessage();
request.RequestUri = new Uri(baseAddress + "api/students");
request.Method = HttpMethod.Get;
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(mediaType));
HttpResponseMessage response = client.SendAsync(request).Result;
Student std = response.Content.ReadAsAsync<Student>().Result;
Console.WriteLine("GET data in '{0}' format", mediaType);
if (StudentsController.CONSTANT_STUDENT.Equals(std))
{
Console.WriteLine("both are equal");
}
client = new HttpClient();
request = new HttpRequestMessage();
request.RequestUri = new Uri(baseAddress + "api/students");
request.Method = HttpMethod.Post;
request.Content = new ObjectContent<Person>(StudentsController.CONSTANT_STUDENT, formatter);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(mediaType));
Student std1 = client.SendAsync(request).Result.Content.ReadAsAsync<Student>().Result;
Console.WriteLine("POST and receive data in '{0}' format", mediaType);
if (StudentsController.CONSTANT_STUDENT.Equals(std1))
{
Console.WriteLine("both are equal");
}
}
}
public class StudentsController : ApiController
{
public static readonly Student CONSTANT_STUDENT = new Student() { Id = 1, Name = "John", EnrolledCourses = new List<string>() { "maths", "physics" } };
public Person Get()
{
return CONSTANT_STUDENT;
}
// NOTE: specifying FromBody here is not required. By default complextypes are bound
// by formatters which read the body
public Person Post([FromBody] Person person)
{
if (!ModelState.IsValid)
{
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, this.ModelState));
}
return person;
}
}
[DataContract]
[KnownType(typeof(Student))]
public abstract class Person : IEquatable<Person>
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
// this is ignored
public DateTime DateOfBirth { get; set; }
public bool Equals(Person other)
{
if (other == null)
return false;
if (ReferenceEquals(this, other))
return true;
if (this.Id != other.Id)
return false;
if (this.Name != other.Name)
return false;
return true;
}
}
[DataContract]
public class Student : Person, IEquatable<Student>
{
[DataMember]
public List<string> EnrolledCourses { get; set; }
public bool Equals(Student other)
{
if (!base.Equals(other))
{
return false;
}
if (this.EnrolledCourses == null && other.EnrolledCourses == null)
{
return true;
}
if ((this.EnrolledCourses == null && other.EnrolledCourses != null) ||
(this.EnrolledCourses != null && other.EnrolledCourses == null))
return false;
if (this.EnrolledCourses.Count != other.EnrolledCourses.Count)
return false;
for (int i = 0; i < this.EnrolledCourses.Count; i++)
{
if (this.EnrolledCourses[i] != other.EnrolledCourses[i])
return false;
}
return true;
}
}
}
I used #kiran-challa solution and added TypeNameHandling on Json media type formatter's SerializerSettings.