Is it better to use reflection in my case? I develop library for working with vk api. My code for base class that build URI for requests:
public abstract class VkApiMethod : IVkApiMethod
{
private string _apiUri = "https://api.vk.com/method/",
_apiVersion = "5.92";
public VkApiMethod(string AccessToken)
{
this.AccessToken = AccessToken;
Fields = new string[] { };
}
public string VkApiMethodName { get; protected set; }
public string AccessToken { get; set; }
public string[] Fields { get; set; }
protected abstract string GetMethodApiParams();
public string GetRequestString()
{
return string.Format("{0}{1}?access_token={2}&fields={3}{4}&v={5}", _apiUri,
VkApiMethodName,
AccessToken,
ArrayToString(Fields),
GetMethodApiParams(),
_apiVersion);
}
}
VkApiMethod is a base class. Derived class must override GetMethodApiParams() method. GetRequestString() call GetMethodApiParams() to get params of derived class.
For example
class GetUsers : VkApiMethod
{
public GetUsers(string AccessToken)
: base(AccessToken)
{
VkApiMethodName = "users.get";
}
/// <summary>
/// Идентификаторы пользователей или их короткие имена.
/// </summary>
public string[] UserIDs { get; set; }
protected override string GetMethodApiParams()
{
return string.Format("&user_ids={0}", ArrayToString(UserIDs));
}
}
And another way without GetMethodApiParams() method using reflection:
public string GetRequestString()
{
var #params = from p in this.GetType().GetProperties()
let attr = p.GetCustomAttributes(typeof(RequestParamAttr), true)
where attr.Length == 1
select new
{
PropValue = p.GetValue(this),
AttrName = (attr.First() as RequestParamAttr).ParamName
};
var _reqUriParams = "";
foreach (var param in #params)
_reqUriParams += string.Format("&{0}={1}", param.AttrName, param.PropValue);
return string.Format("{0}{1}?access_token={2}{3}&v={4}", _apiUri,
VkApiMethodName,
AccessToken,
_reqUriParams,
_apiVersion);
}
Derived class example:
class GetUsers : VkApiMethod
{
public GetUsers(string AccessToken)
: base(AccessToken)
{
VkApiMethodName = "users.get";
}
/// <summary>
/// Идентификаторы пользователей или их короткие имена.
/// </summary>
[RequestParamAttr("user_ids")]
public string[] UserIDs { get; set; }
}
What way is better to use?
Related
Some of my actions accept models like:
public class PaymentRequest
{
public decimal Amount { get; set; }
public bool? SaveCard { get; set; }
public int? SmsCode { get; set; }
public BankCardDetails Card { get; set; }
}
public class BankCardDetails
{
public string Number { get; set; }
public string HolderName { get; set; }
public string ExpiryDate { get; set; }
public string ValidationCode { get; set; }
}
And the action method looks like:
[HttpPost]
[Route("api/v1/payment/pay")]
public Task<BankCardActionResponse> Pay([FromBody] PaymentRequest request)
{
if (request == null)
throw new HttpResponseException(HttpStatusCode.BadRequest);
return _paymentService.PayAsync(DataUserHelper.PhoneNumber, request);
}
I use Nlog. I think it's clear this is a bad idea to log all this bank data. My log config file contained the following line:
<attribute name="user-requestBody" layout="${aspnet-request-posted-body}"/>
I logged the request. I decided to refactor that and planned the following strategy. Actions that contain sensitive data into their requests I will mark with an attribute like
[RequestMethodFormatter(typeof(PaymentRequest))]
then take a look at my custom renderer:
[LayoutRenderer("http-request")]
public class NLogHttpRequestLayoutRenderer : AspNetRequestPostedBody
{
protected override void DoAppend(StringBuilder builder, LogEventInfo logEvent)
{
base.DoAppend(builder, logEvent);
var body = builder.ToString();
// Get attribute of the called action.
var type = ... // How can I get "PaymentRequest" from the [RequestMethodFormatter(typeof(PaymentRequest))]
var res = MaskHelper.GetMaskedJsonString(body, type);
// ... and so on
}
}
I think you understand the idea. I need the type from the method's RequestMethodFormatter attribute. Is it even possible to get it into the renderer? I need it because I'm going to deserialize request JSON into particular models (it's gonna be into the MaskHelper.GetMaskedJsonString), work with the models masking the data, serialize it back into JSON.
So, did I choose a wrong approach? Or it's possible to get the type from the attribute into the renderer?
After some research, I ended up with the following solution:
namespace ConsoleApp7
{
internal class Program
{
private static void Main()
{
var sourceJson = GetSourceJson();
var userInfo = JsonConvert.DeserializeObject(sourceJson, typeof(User));
Console.WriteLine("----- Serialize without Resolver-----");
Console.WriteLine(JsonConvert.SerializeObject(userInfo));
Console.WriteLine("----- Serialize with Resolver-----");
Console.WriteLine(JsonConvert.SerializeObject(userInfo, new JsonSerializerSettings
{
ContractResolver = new MaskPropertyResolver()
}));
}
private static string GetSourceJson()
{
var guid = Guid.Parse("3e92f0c4-55dc-474b-ae21-8b3dac1a0942");
return JsonConvert.SerializeObject(new User
{
UserId = guid,
Age = 19,
Name = "John",
BirthDate = new DateTime(1990, 5, 12),
Hobbies = new[]
{
new Hobby
{
Name = "Football",
Rating = 5,
DurationYears = 3,
},
new Hobby
{
Name = "Basketball",
Rating = 7,
DurationYears = 4,
}
}
});
}
}
public class User
{
[MaskGuidValue]
public Guid UserId { get; set; }
[MaskStringValue("***")] public string Name { get; set; }
public int Age { get; set; }
[MaskDateTimeValue]
public DateTime BirthDate { get; set; }
public Hobby[] Hobbies { get; set; }
}
public class Hobby
{
[MaskStringValue("----")]
public string Name { get; set; }
[MaskIntValue(replacement: 11111)]
public int Rating { get; set; }
public int DurationYears { get; set; }
}
public class MaskPropertyResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var props = base.CreateProperties(type, memberSerialization);
var allowedPropertyTypes = new Type[]
{
typeof(Guid),
typeof(DateTime),
typeof(string),
typeof(int),
};
foreach (var prop in props.Where(p => allowedPropertyTypes.Contains(p.PropertyType)))
{
if (prop.UnderlyingName == null)
continue;
var propertyInfo = type.GetProperty(prop.UnderlyingName);
var attribute =
propertyInfo?.GetCustomAttributes().FirstOrDefault(x => x is IMaskAttribute) as IMaskAttribute;
if (attribute == null)
{
continue;
}
if (attribute.Type != propertyInfo.PropertyType)
{
// Log this case, cause somebody used wrong attribute
continue;
}
prop.ValueProvider = new MaskValueProvider(propertyInfo, attribute.Replacement, attribute.Type);
}
return props;
}
private class MaskValueProvider : IValueProvider
{
private readonly PropertyInfo _targetProperty;
private readonly object _replacement;
private readonly Type _type;
public MaskValueProvider(PropertyInfo targetProperty, object replacement, Type type)
{
_targetProperty = targetProperty;
_replacement = replacement;
_type = type;
}
public object GetValue(object target)
{
return _replacement;
}
public void SetValue(object target, object value)
{
_targetProperty.SetValue(target, value);
}
}
}
[AttributeUsage(AttributeTargets.Property)]
public class MaskStringValueAttribute : Attribute, IMaskAttribute
{
public Type Type => typeof(string);
public object Replacement { get; }
public MaskStringValueAttribute(string replacement)
{
Replacement = replacement;
}
}
[AttributeUsage(AttributeTargets.Property)]
public class MaskIntValueAttribute : Attribute, IMaskAttribute
{
public object Replacement { get; }
public Type Type => typeof(int);
public MaskIntValueAttribute(int replacement)
{
Replacement = replacement;
}
}
[AttributeUsage(AttributeTargets.Property)]
public class MaskGuidValueAttribute : Attribute, IMaskAttribute
{
public Type Type => typeof(Guid);
public object Replacement => Guid.Empty;
}
[AttributeUsage(AttributeTargets.Property)]
public class MaskDateTimeValueAttribute : Attribute, IMaskAttribute
{
public Type Type => typeof(DateTime);
public object Replacement => new DateTime(1970, 1, 1);
}
public interface IMaskAttribute
{
Type Type { get; }
object Replacement { get; }
}
}
I hope somebody will find it helpful.
You can try nuget package https://www.nuget.org/packages/Slin.Masking and https://www.nuget.org/packages/Slin.Masking.NLog.
It can easily be integrated with DotNet projects with slight changes, and you can define your rules for it. But the document needs some improvement.
As a suggestion, you can use two files:
masking.json (can be a generic one, that shared across all projects)
masking.custom.json (can be used with particular rules for specific projects)
I have an application with a general search request which has an identification number that could be anything like product number or customer number along with additional search criteria. I want to display all the results. I am writing a middleware to call the search api end points.
public class GeneralRequest
{
public string IdentificationNumber { get; set; }
public string Location { get; set; }
public bool IsActive { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime Start { get; set; }
public DateTime Stop { get; set; }
}
public class AdditionalSearch
{
public RangeSearch Range { get; set; }
public string Location { get; set; }
public bool IsActive { get; set; }
}
public class RangeSearch
{
public DateTime Start { get; set; }
public DateTime Stop { get; set; }
}
public class GetProductRequest : AdditionalSearch, ISearchRequest
{
public string ProductId { get; set; }
}
public class GetCustomerRequst : AdditionalSearch, ISearchRequest
{
public string CustomerNumber { get; set; }
}
public class GetManufacturerRequest : AdditionalSearch, ISearchRequest
{
public string ManufacturerNumber { get; set; }
}
// this is a dummy interface to make all the requests general
public interface ISearchRequest
{
}
This is the searchprocessor where I am creating the correct request based on the identification number pattern. But I am failing to assign the AdditonalSearch to the request that I get after invoking the func. Of course it is an interface that has nothing in it. How can I achieve this by not repeating(I mean I don't want to repeat the initialization logic in the dictionary)
Please suggest me what is the best practice here.
public class SearchProcessor
{
private readonly Dictionary<Regex, Func<GeneralRequest, ISearchRequest>> _pattern;
private readonly IAppClient _appClient;
public SearchProcessor(IAppClient appClient)
{
_appClient = appClient;
_pattern = new Dictionary<Regex, Func<GeneralRequest, ISearchRequest>>
{
{new Regex("/\b([0-9]|10)\b /"), p=> new GetProductRequest(){ProductId = p.IdentificationNumber} },
{new Regex("^\\d{}1,9}$"), p=> new GetCustomerRequst(){CustomerNumber = p.IdentificationNumber} },
{new Regex("^\\d{}1,11}$"), p=> new GetManufacturerRequest(){ManufacturerNumber = p.IdentificationNumber} }
};
}
public List<SearchResult> GetAllSearchResults(GeneralRequest request)
{
var requests = _pattern.Where(r => r.Key.IsMatch(request.IdentificationNumber)).Select(v => v.Value);
var responses = new List<SearchResult>();
foreach (var req in requests)
{
var appRequest = req.Invoke(request);
appRequest.AdditionalSearch = new AdditionalSearch // this is where I am not able to assign the additional seach from the general request
{
Range = new RangeSearch { Start = request.Start, Stop = request.Stop}
IsActive = request.IsActive,
Location = request.Location
};
//This calls another api to get the response.
responses.Add(_appclient.FindResult(appRequest));
}
return responses;
}
}
---UPDATE--
Here is the appclient that calls the external api..
sample request for the getproduct route is
public class AppClient : IAppClient
{
private readonly string _baseurl;
private readonly string _getProductRoute;
private readonly string _getCustomerRoute;
private readonly string _getManufacturerRoute;
public AppClient()
{
_getProductRoute = $"{_baseurl}/api/get-product";
_getCustomerRoute = $"{_baseurl}/api/get-customer";
_getManufacturerRoute = $"{_baseurl}/api/get-Manufacturer";
}
public SearchResult FindResult(ISearchRequest searchRequest)
{
var routes = new Dictionary<Type, string>
{
{typeof(GetProductRequest), _getProductRoute },
{typeof(GetCustomerRequst), _getCustomerRoute },
{typeof(GetManufacturerRequest), _getManufacturerRoute }
};
// Here it is going to be http implementation to call above routes.
return new SearchResult();
}
}
The request for get product route is
{
"ProductId":"",
"RangeSearch":{
"Start":"",
"Stop":""
},
"Location":"",
"IsActive":true
}
For get-customer request is
{
"CustomerNumber":"",
"RangeSearch":{
"Start":"",
"Stop":""
},
"Location":"",
"IsActive":true
}
You can simplify your work by doing this :
public class SearchRequest
{
public string IdentificationNumber { get; set; }
public string Location { get; set; }
public bool IsActive { get; set; }
public SearchRequestRange Range { get; set; }
public SearchRequest(string identificationNumber)
{
IdentificationNumber = identificationNumber;
}
}
public class SearchRequestRange
{
public DateTime Start { get; set; }
public DateTime Stop { get; set; }
}
now the process you are doing is not needed, you only need to adjust your ApiClient to something like this :
public class AppClient : IAppClient
{
private static readonly IReadOnlyDictionary<string, string> _endPointsSearchPatterns = new Dictionary<string, string>
{
{"get-product", "/\b([0-9]|10)\b /"},
{"get-customer", "^\\d{}1,9}$"},
{"get-Manufacturer", "^\\d{}1,11}$"}
};
private readonly string _baseUrl;
public AppClient(string baseUrl)
{
if(string.IsNotNullOrWhiteSpace(baseUrl))
throw new ArgumentNullException(nameof(baseUrl));
_baseurl = baseUrl;
}
public IEnumerable<SearchResult> FindResult(SearchRequest searchRequest)
{
var endPoints = _endPointsSearchPatterns.Where(x=> Regex.IsMatch(request.IdentificationNumber , x.Value))?.ToList();
if(endPoints?.Count == 0)
{
yield break;
}
var responses = new List<SearchResult>();
foreach(var endpoint in endPoints)
{
ISearchBy search = null;
switch(endPoint.Key)
{
case "get-product":
action = new ProductApiClient(this);
break;
case "get-customer":
action = new ProductApiClient(this);
break;
case "get-Manufacturer":
action = new ProductApiClient(this);
break;
}
yield return action.SearchBy(searchRequest);
}
return searchResult;
}
}
Regarding GetProductRequest, GetCustomerRequst, and GetManufacturerRequest these should be refactored, and instead you can create a class for each entity like this :
public interface ISearchBy
{
SearchResult ISearchBy(SearchRequest request);
}
public class ProductApiClient : ISearchBy
{
private readonly IAppClient _appClient;
public ProductApiClient(IAppClient appClient)
{
_appClient = appClient;
}
public SearchResult SearchBy(SearchRequest request)
{
// do stuff
}
// other related endpoints
}
public class CustomerApiClient : ISearchBy
{
private readonly IAppClient _appClient;
public CustomerApiClient(IAppClient appClient)
{
_appClient = appClient;
}
public SearchResult SearchBy(SearchRequest request)
{
// do stuff
}
// other related endpoints
}
public class ManufacturerApiClient : ISearchBy
{
private readonly IAppClient _appClient;
public ManufacturerApiClient(IAppClient appClient)
{
_appClient = appClient;
}
public SearchResult SearchBy(SearchRequest request)
{
// do stuff
}
// other related endpoints
}
I have a POCO like this:
public class Process
{
public Process() { }
[DataMember(Name = "lang_code")]
public string LCode { get; set; }
[DataMember(Name = "data_currency")]
public string Currency { get; set; }
[DataMember(Name = "country_code")]
public string CCode { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
Now when I serialize my POCO I get json back like this which has field name:
{"LCode":"en-US","Currency":"USD","CCode":"IN"}
Is there any way to get it the way DataMember fields are after serializing POCO. Something like below:
{"lang_code":"en-US","data_currency":"USD","country_code":"IN"}
Below is the code we have:
ProcessStr = ExtractHeader(headers, PROCESS_HEADER);
Console.WriteLine(ProcessStr);
if (!string.IsNullOrWhiteSpace(ProcessStr))
{
Process = DeserializeJson<Process>(ProcessStr);
if (Process != null && !string.IsNullOrWhiteSpace(Process.Gold))
{
Process.Gold = HttpUtility.HtmlEncode(Process.Gold);
}
ProcessStr = Process.ToString();
Console.WriteLine(ProcessStr);
}
private T DeserializeJson<T>(string str) where T : new()
{
try
{
return Utf8Json.JsonSerializer.Deserialize<T>(str);
}
catch (Exception e)
{
return new T();
}
}
It looks like you are using two different packages, Newtonsoft.Json to serialize and Utf8Json to deserialize. They use different annotations. You can get it to work, but it might be simpler to choose one or the other.
Newtonsoft.Json uses the JsonProperty attribute whereas Utf8Json uses the DataMember one.
using System;
using System.Diagnostics;
using System.Runtime.Serialization;
using Newtonsoft.Json;
using Utf8Json;
namespace JSONPropertyTest
{
public class Process
{
public Process() { }
[JsonProperty("lang_code")]
[DataMember(Name = "lang_code")]
public string LCode { get; set; }
[JsonProperty("data_currency")]
[DataMember(Name = "data_currency")]
public string Currency { get; set; }
[JsonProperty("country_code")]
[DataMember(Name = "country_code")]
public string CCode { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
class Program
{
static private T DeserializeJson<T>(string str) where T : new()
{
try
{
return Utf8Json.JsonSerializer.Deserialize<T>(str);
}
catch (Exception e)
{
return new T();
}
}
static void Main(string[] args)
{
var test = new Process { LCode = "en-US",Currency = "USD", CCode = "IN" };
var json = test.ToString();
Console.WriteLine($"serialized={test}");
var deserialized = DeserializeJson<Process>(json);
Debug.Assert(test.CCode == deserialized.CCode);
Debug.Assert(test.LCode == deserialized.LCode);
Debug.Assert(test.Currency == deserialized.Currency);
Console.WriteLine($"deserialized={deserialized}");
}
}
}
To just use Utf8Json you need to update your ToString method, which is the only one in the code you've shown that relies on Newtonsoft.Json. That would look like this:
public class Process
{
public Process() { }
[DataMember(Name = "lang_code")]
public string LCode { get; set; }
[DataMember(Name = "data_currency")]
public string Currency { get; set; }
[DataMember(Name = "country_code")]
public string CCode { get; set; }
public override string ToString()
{
return Utf8Json.JsonSerializer.ToJsonString(this);
}
}
I have multiple classes of the parent class "command_functions"
example
class Empty_Command : command_functions
each of the command classes override a value
public override string command_display_name { get { return "Empty"; } }
is there anyway to search types of command_functions looking for where command_display_name is set to a matching string and return that.
so I could use it like so
command_functions find = FindCommand("Empty");
if(find != null)
{
new find();
}
Using Generics this can be done. From what I can tell is you have a set of classes that inherit from a class Empty_Command (I am assuming abstract) and you would like to find which specific class to execute based on command name.
I have created the following example which assumes that all the inherited types are in the same assembly. Its no problem if they are across multiple assemblies just your load is different.
public abstract class Empty_Command
{
/// <summary>
/// Find command
/// </summary>
/// <param name="commandName">the command name</param>
/// <returns></returns>
public static Empty_Command FindCommand(string commandName)
{
//get all the types that are inherited from the Empty_Command class and are not abstract (skips empty commad)
var types = Assembly.GetExecutingAssembly().GetTypes().Where(x => typeof(Empty_Command).IsAssignableFrom(x) && !x.IsAbstract);
//enuerate all types
foreach (var type in types)
{
//create an instance of empty command from the type
var item = Activator.CreateInstance(type) as Empty_Command;
if (item == null)
continue;
//test the display name
if(item.command_display_name.Equals(commandName))
return item;
}
return null;
}
public abstract string command_display_name { get; }
}
I commented a bit of the code to help out. But here is my full test stub.
using System;
using System.Linq;
using System.Reflection;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var cmd = Empty_Command.FindCommand("command_2");
if (cmd != null)
Console.WriteLine(cmd.command_display_name);
Console.ReadKey();
}
}
public abstract class Empty_Command
{
/// <summary>
/// Find command
/// </summary>
/// <param name="commandName">the command name</param>
/// <returns></returns>
public static Empty_Command FindCommand(string commandName)
{
//get all the types that are inherited from the Empty_Command class and are not abstract (skips empty commad)
var types = Assembly.GetExecutingAssembly().GetTypes().Where(x => typeof(Empty_Command).IsAssignableFrom(x) && !x.IsAbstract);
//enuerate all types
foreach (var type in types)
{
//create an instance of empty command from the type
var item = Activator.CreateInstance(type) as Empty_Command;
if (item == null)
continue;
//test the display name
if(item.command_display_name.Equals(commandName))
return item;
}
return null;
}
public abstract string command_display_name { get; }
}
public class Command1 : Empty_Command
{
public override string command_display_name
{
get { return "command_1"; }
}
}
public class Command2 : Empty_Command
{
public override string command_display_name
{
get { return "command_2"; }
}
}
public class Command3 : Empty_Command
{
public override string command_display_name
{
get { return "command_3"; }
}
}
}
Hope this helps...
To get the value of a non-static property you need to instantiate the type, therefore the first condition is that you can instantiate each type you wish to examine.
Otherwise it is a matter of using reflection to get a list of types, filtering by base type, instantiating and calling the property get method.
You are looking for a Factory design pattern. (http://en.wikipedia.org/wiki/Factory_method_pattern)
In this Factory you can create a function like
command_functions findCommand(string commandText)
{
if (commandText == "empty") return new Empty_Command();
if (...) etc
}
You can use following pattern to do that
interface IGroup2
{
string SearchOption { get; set; }
List<Projects> GetProjects();
}
public class GroupHead : IGroup2
{
public string SearchOption { get; set; }
public GroupHead()
{
SearchOption = "GroupHead";
}
public List<Projects> GetProjects()
{
//Code here
return null;
}
}
public class ProjectIncharge : IGroup2
{
public string SearchOption { get; set; }
public ProjectIncharge()
{
SearchOption = "ProjectIncharge";
}
public List<Projects> GetProjects()
{
//Code here
return null;
}
}
public class ProjectManager : IGroup2
{
public string SearchOption { get; set; }
public ProjectManager()
{
SearchOption = "ProjectManager";
}
public List<Projects> GetProjects()
{
//Code here
return null;
}
}
public class Test
{
private static List<IGroup2> searchRuleList = new List<IGroup2>()
{
new GroupHead(),
new ProjectIncharge(),
new ProjectManager(),
};
public static void Main(string[] args)
{
IGroup2 searchOptionRule = searchRuleList.Find(delegate(IGroup2 searchRule) { return searchRule.SearchOption.Equals(args[0]); });
}
}
I have an issue with serialization. I understand that methods can not be serialized for good reason, so I created a factory class to convert my existing class into a more manageable class.
This is the original class:
using Assets.Components;
using Assets.Data;
using IO.Components;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
namespace Assets
{
[Serializable]
public class Asset
{
#region Fields
Metadata _metadata;
string _fileName;
string _companyId;
#endregion
#region Properties
[Required]
public string DisplayName { get; set; }
public string Description { get; set; }
public string Tags { get; set; }
public int Id { get; set; }
public int CategoryId { get; set; }
public AssetType Type { get; set; }
public int LanguageId { get; set; }
public int StatusId { get; set; }
public DateTime DateCreated { get; set; }
public long DateCreatedMilliseconds { get { return DateCreated.ToJavaScriptMilliseconds(); } }
public int Views { get; set; }
public int Downloads { get; set; }
public string ThumbNail { get; set; }
public string Filename
{
set { _fileName = value; }
}
[Required]
public string CompanyId
{
set { _companyId = value; }
}
public string GetBaseDirectory
{
get { return "/Public/Uploads/" + this._companyId + "/0"; }
}
public double Rating
{
get
{
List<int> Score = new List<int>();
foreach (IRating oRating in this.Ratings())
{
Score.Add(oRating.Score);
}
return (Score.Count > 0) ? Score.Average() : 0;
}
}
public Metadata Metadata
{
get
{
if (_metadata == null)
{
_metadata = new Metadata(this.Id);
if (_metadata.AssetId == 0)
{
try
{
if (GetFilename() != null)
{
string path = System.IO.Path.Combine(HttpContext.Current.Server.MapPath(this.GetBaseDirectory), GetFilename());
if (!System.IO.File.Exists(path))
_metadata = new Metadata();
else
{
_metadata = MetadataExtractor.Create(path, this.Id);
_metadata.save();
}
}
else
{
_metadata = new Metadata();
}
}
catch
{
_metadata = new Metadata();
}
}
}
return _metadata;
}
}
public bool IsConverted { get; set; }
public string UserId { get; set; }
public DateTime DateModified { get; set; }
public long DateModifiedMilliseconds { get { return DateCreated.ToJavaScriptMilliseconds(); } }
public string Culture { get; set; }
public string Language { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public int CategoryCount { get; set; }
public int AssetCount { get; set; }
public bool IgnoreRights { get; set; }
#endregion
#region Contructors
/// <summary>
/// Default constructor
/// </summary>
public Asset()
{
}
/// <summary>
/// Get's the asset from the database, but set's the status to the profiles Requires Approval state.
/// </summary>
/// <param name="Id">Asset Id</param>
/// <param name="IsViewing">Boolean to update the reports table</param>
/// <param name="IsDownloading">Boolean to update the reports table</param>
public Asset(int Id, string UserId, string CompanyId, bool IsViewing, bool IsDownloading)
{
try
{
Asset oAsset = AssetData.GetAsset(Id, IsViewing, IsDownloading, UserId, CompanyId);
// Assign the values to this class
this.Id = oAsset.Id;
this.DisplayName = oAsset.DisplayName;
this.IsConverted = oAsset.IsConverted;
this.StatusId = oAsset.StatusId;
this.Type = oAsset.Type;
this.UserId = oAsset.UserId;
this.UserName = oAsset.UserName;
this.CompanyId = oAsset.GetCompanyId();
this.Description = oAsset.Description;
this.Tags = oAsset.Tags;
this.LanguageId = oAsset.LanguageId;
this.Culture = oAsset.Culture;
this.Language = oAsset.Language;
if (oAsset.ThumbNail != null) this.ThumbNail = oAsset.ThumbNail;
this.Filename = oAsset.GetFilename();
if (oAsset.Views != 0) this.Views = oAsset.Views;
if (oAsset.Downloads != 0) this.Downloads = oAsset.Downloads;
}
catch (Exception ex)
{
Stars.BLL.Error.Handling.LogError("Skipstone", "Asset", "Asset", ex.Message, ex.ToString()); // Record our error
}
}
/// <summary>
/// Used for executing some of the public methods
/// </summary>
/// <param name="Id">Id of the asset to retrieve</param>
/// <param name="CompanyId">The CompanyId of the company for the User</param>
public Asset(int Id, string CompanyId)
{
this.Id = Id;
this.CompanyId = CompanyId;
}
#endregion
#region Public methods
public string GetCompanyId()
{
return _companyId;
}
public string GetFilename()
{
return _fileName;
}
public string GetThumbnail()
{
return this.GetBaseDirectory + "/" + this.ThumbNail;
}
public string GetSmallThumbnail()
{
return this.GetBaseDirectory + "/sml_" + this.ThumbNail;
}
public Collection<IRating> Ratings()
{
Collection<IRating> oRatings = new Collection<IRating>();
try
{
oRatings = RatingData.get(this.Id);
}
catch
{
// record our error
}
return oRatings;
}
public Collection<IComment> Comments()
{
Collection<IComment> oComments = new Collection<IComment>();
try
{
oComments = CommentData.getAssetComments(this.Id);
}
catch (Exception ex)
{
// record our error
}
return oComments;
}
public void SaveMetadata()
{
}
public Collection<GenericType> Categories()
{
return MiscellaneousManager.AssetCategories(this.Id, GetCompanyId());
}
public void Save()
{
if (this.Id > 0)
{
AssetData.update(this);
}
else
{
Asset oAsset = AssetData.create(this);
this.Id = oAsset.Id;
this.DisplayName = oAsset.DisplayName;
this.Type = oAsset.Type;
this.UserId = oAsset.UserId;
this.CompanyId = oAsset.GetCompanyId();
this.Description = oAsset.Description;
this.Tags = oAsset.Tags;
this.LanguageId = oAsset.LanguageId;
this.Culture = oAsset.Culture;
this.Language = oAsset.Language;
if (oAsset.ThumbNail != null) this.ThumbNail = oAsset.ThumbNail;
this.Filename = oAsset.GetFilename();
if (oAsset.Views != 0) this.Views = oAsset.Views;
if (oAsset.Downloads != 0) this.Downloads = oAsset.Downloads;
}
}
public void delete()
{
AssetData.delete(this.Id);
AssetManager.RemoveFromCache(this);
}
#endregion
}
}
and this is my factory method:
private static SerialisedAsset AssetFactory(Assets.Asset Object)
{
SerialisedAsset FactoryObject = new SerialisedAsset()
{
Id = Object.Id,
Name = Object.DisplayName,
UserId = Object.UserId,
UserName = Object.UserName,
CompanyId = Object.GetCompanyId(),
Description = Object.Description,
Tags = Object.Tags,
DateCreated = Object.DateCreated,
Path = Object.GetBaseDirectory,
FileName = Object.GetFilename(),
ThumbnailName = Object.ThumbNail
};
return FactoryObject;
}
which is part of my audittrailmanager class:
using Assets;
using Core;
using Reports.Objects;
using System;
using System.IO;
using System.Xml.Serialization;
namespace Reports.Components
{
public static class AuditTrailManager
{
#region Public methods
public static Audit AuditTrailFactory(Profile Profile, Object Object, Event Event)
{
Audit Audit = new Audit(SerializeObject(Object))
{
UserId = Profile.UserId,
UserName = Profile.UserName,
CompanyId = Profile.CompanyId,
ObjectName = GetObjectNameFromType(Object.GetType().ToString()),
Event = Event
};
return Audit;
}
#endregion
#region Private methods
private static string GetObjectNameFromType(string Type)
{
switch (Type)
{
case "Assets.Asset": return "Asset";
case "Core.SiteSetting": return "CompanySettings";
}
return "";
}
private static string SerializeObject(Object Object)
{
string ObjectType = Object.GetType().ToString();
switch (ObjectType)
{
case "Assets.Asset": return Serialize(AssetFactory((Asset)Object));
}
return ""; // If we fail
}
private static string Serialize(Object Object)
{
XmlSerializer ser = new XmlSerializer(Object.GetType());
using (StringWriter Xml = new StringWriter())
{
ser.Serialize(Xml, Object);
return (Xml.ToString());
}
}
private static SerialisedAsset AssetFactory(Assets.Asset Object)
{
SerialisedAsset FactoryObject = new SerialisedAsset()
{
Id = Object.Id,
Name = Object.DisplayName,
UserId = Object.UserId,
UserName = Object.UserName,
CompanyId = Object.GetCompanyId(),
Description = Object.Description,
Tags = Object.Tags,
DateCreated = Object.DateCreated,
Path = Object.GetBaseDirectory,
FileName = Object.GetFilename(),
ThumbnailName = Object.ThumbNail
};
return FactoryObject;
}
#endregion
}
}
What I am trying to do is create an audit trail which records the object I am working on (in this case an asset) and I am serializing the class and inserting it into the database for use in reporting, etc.
My question is; is this the way to do it. Is there a better way?