Default model example in Swashbuckle (Swagger) - asp.net-web-api

I'm running ASP WebAPI 2 and successfully installed Swashbuckle. I am trying to figure out how one defines what the default schema values are?
For example, on the Swagger live demo site they changed the default value of pet to "doggie". They also defined the allowable values for status. (Live Demo)

I managed to get this working by following what's on this link:
https://github.com/domaindrivendev/Swashbuckle/issues/69#issuecomment-53953785
In short this is what needs to be done:
Create the classes SwaggerDefaultValue and AddDefaultValues as described in the link. Some changes that I did:
public class SwaggerDefaultValue : Attribute
{
public string Name { get; set; }
public string Value { get; set; }
public SwaggerDefaultValue(string value)
{
this.Value = value;
}
public SwaggerDefaultValue(string name, string value) : this(value)
{
this.Name = name;
}
}
public class AddDefaultValues : IOperationFilter
{
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
IDictionary<string, object> parameterValuePairs =
GetParameterValuePairs(apiDescription.ActionDescriptor);
foreach (var param in operation.parameters)
{
var parameterValuePair = parameterValuePairs.FirstOrDefault(p => p.Key.IndexOf(param.name, StringComparison.InvariantCultureIgnoreCase) >= 0);
param.#default = parameterValuePair.Value;
}
}
private IDictionary<string, object> GetParameterValuePairs(HttpActionDescriptor actionDescriptor)
{
IDictionary<string, object> parameterValuePairs = new Dictionary<string, object>();
foreach (SwaggerDefaultValue defaultValue in actionDescriptor.GetCustomAttributes<SwaggerDefaultValue>())
{
parameterValuePairs.Add(defaultValue.Name, defaultValue.Value);
}
foreach (var parameter in actionDescriptor.GetParameters())
{
if (!parameter.ParameterType.IsPrimitive)
{
foreach (PropertyInfo property in parameter.ParameterType.GetProperties())
{
var defaultValue = GetDefaultValue(property);
if (defaultValue != null)
{
parameterValuePairs.Add(property.Name, defaultValue);
}
}
}
}
return parameterValuePairs;
}
private static object GetDefaultValue(PropertyInfo property)
{
var customAttribute = property.GetCustomAttributes<SwaggerDefaultValue>().FirstOrDefault();
if (customAttribute != null)
{
return customAttribute.Value;
}
return null;
}
}
Edit your SwaggerConfig and add the AddDefaultValues class to the OperationFilters:
GlobalConfiguration.Configuration
.EnableSwagger(c => {
...
c.OperationFilter<AddDefaultValues>()
...
});
Now for the parameters I want default values I just add the following:
public IHttpActionResult Put([FromBody]Pet pet)
{
...
return Ok();
}
public class Pet {
[SwaggerDefaultValue("doggie")]
public string Name { get; set; }
[SwaggerDefaultValue("available")]
public string Status;
...
}

Well the code of vgaspar.trivix did not work completly for me, the default values did not get set for the schema. Also i got an NullPointerException. I managed to get it working as intended by editing the Apply method and manipulated the schemaRegistry like this:
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
if (operation.parameters == null)
return;
IDictionary<string, object> parameterValuePairs =
GetParameterValuePairs(apiDescription.ActionDescriptor);
foreach (var param in operation.parameters)
{
if (param.schema != null && param.schema.#ref != null)
{
string schemaName = param.schema.#ref.Split('/').LastOrDefault();
if (schemaRegistry.Definitions.ContainsKey(schemaName))
foreach (var props in schemaRegistry.Definitions[schemaName].properties)
{
if (parameterValuePairs.ContainsKey(props.Key))
props.Value.#default = parameterValuePairs[props.Key];
}
}
var parameterValuePair = parameterValuePairs.FirstOrDefault(p => p.Key.IndexOf(param.name, StringComparison.InvariantCultureIgnoreCase) >= 0);
param.#default = parameterValuePair.Value;
}
}

An example Model Schema can be defined by implementing ISchemaFilter and registering it using the following:
httpConfig
.EnableSwagger(c =>
{
c.SchemaFilter<AddSchemaExamples>()
});
An example implementation is provided here:
public class AddSchemaExamples : ISchemaFilter
{
public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type)
{
if (type == typeof(Product))
{
schema.example = new Product
{
Id = 123,
Type = ProductType.Book,
Description = "Treasure Island",
UnitPrice = 10.0M
};
}
}
}
Source: https://github.com/domaindrivendev/Swashbuckle/issues/162

I know this thread is quite old, but I wanted to share my solution which creates a custom constructor just for the Swagger example schema.
In my model:
/// <summary>
/// Supply a custom constructor for Swagger where you can apply defaults to control the example schema.
/// The constructor must have one parameter of type System.Reflection.ParameterInfo[].
/// Note: Setting a property to null will prevent it from showing in the Swagger example.
/// </summary>System.Reflection.ParameterInfo[].
/// </summary>
public class SwaggerConstructor : Attribute { }
In SwaggerConfig.cs:
c.SchemaFilter<ApplySchemaVendorExtensions>();
The schema extension:
public class ApplySchemaVendorExtensions : ISchemaFilter
{
public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type)
{
ConstructorInfo constructor = type.GetConstructors().FirstOrDefault(c => c.GetCustomAttribute<SwaggerConstructor>() != null);
if (constructor != null)
{
schema.example = constructor.Invoke(new object[] { constructor.GetParameters() });
}
}
}
Usage:
[SwaggerConstructor]
public MyClass(System.Reflection.ParameterInfo[] decoy) : base()
{
MyProperty = false;
}

Stumbled across this just now, you can also set the tag in the XML documentation, in one of my models, I have this defined
/// <summary>
/// Note content
/// </summary>
/// <example>Any text for a note.</example>
public string Note { get; set; }
which ends up looking like this in the swagger documentation when selecting "Try It Now"
Hope that helps someone!

Using .NET 5 with Swashbuckle.AspNetCore 5.6.3, the only way I could get this to work efficiently is this:
public class ExampleDocFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
string ToCamelCase(string name) => char.ToLowerInvariant(name[0]) + name.Substring(1);
if (schema.Properties == null) return;
var setProperties = context.Type.GetProperties().ToList().Where(f => f.GetCustomAttribute<DefaultValueAttribute>() != null).Where(f => schema.Properties.Any(n => n.Key.Equals(ToCamelCase(f.Name)))).ToDictionary(f => f, f => f.GetCustomAttribute<DefaultValueAttribute>());
foreach (var prop in setProperties) schema.Properties[ToCamelCase(prop.Key.Name)].Example = OpenApiAnyFactory.CreateFor(schema.Properties[ToCamelCase(prop.Key.Name)], prop.Value.Value);
}
}
To use this - in your startup.cs:
services.AddSwaggerGen(swagger => {
...
swagger.SchemaFilter<ExampleDocFilter>();
});

Related

Recursively validating a complex model within a custom model binder

Below is the code for a simple JsonModelBinder I created for an ASP.NET Core Mvc app. Is there a simple way to recursively validate the model, its properties and the properties of its properties and so on?
JsonModelBinder
public class JsonModelBinder : IModelBinder
{
static readonly JsonSerializerSettings settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
};
public Task BindModelAsync(ModelBindingContext bindingContext)
{
try
{
var json = bindingContext.ValueProvider.GetValue(bindingContext.ModelName).Values;
if (json.Count > 0)
{
var model = JsonConvert.DeserializeObject(json, bindingContext.ModelType, settings);
// TODO: Validate complex model
bindingContext.Result = ModelBindingResult.Success(model);
}
else
{
bindingContext.Result = ModelBindingResult.Success(null);
}
}
catch (JsonException ex)
{
bindingContext.ModelState.AddModelError(bindingContext.ModelName, ex.Message);
bindingContext.Result = ModelBindingResult.Failed();
}
return Task.CompletedTask;
}
}
Example model
public class Foo {
[Required]
public string Name { get; set; }
public List<Bar> Bars { get; set; }
public Baz Baz { get; set; }
}
public class Bar {
[Required]
public string Name { get; set; }
}
public class Baz {
[Required]
public string Name { get; set; }
}
Controller action
public async Task<IActionResult> Edit(Guid id, [Required, ModelBinder(typeof(JsonModelBinder))] Foo foo) {
if (ModelState.IsValid) {
// Do stuff
}
else {
return View(foo);
}
}
I created the following recursive data annotations validator, and used it like below. Based on some feedback I received, it appears that there is nothing like this built into .NET Core at this time.
Recursive data annotations validator:
public static class RecursiveValidator
{
/// <summary>
/// Recursively validates <paramref name="instance"/>.
/// </summary>
/// <param name="instance"></param>
/// <param name="prefix"></param>
/// <param name="validationContext"></param>
/// <param name="results"></param>
/// <param name="validateAllProperties"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"><paramref name="instance"/> is null</exception>
public static bool TryValidateObject(object instance, IServiceProvider serviceProvider, IDictionary<object, object> items, ICollection<ValidationResult> results, bool validateAllProperties, string prefix)
{
if (instance is null)
{
throw new ArgumentNullException(nameof(instance));
}
var tempResults = new List<ValidationResult>();
ValidationContext validationContext = new ValidationContext(instance, serviceProvider, items);
var isValid = Validator.TryValidateObject(instance, validationContext, tempResults, validateAllProperties: validateAllProperties);
foreach (var item in tempResults)
{
IEnumerable<string> memberNames = item.MemberNames.Select(name => (!string.IsNullOrEmpty(prefix) ? prefix + "." : "") + name);
results.Add(new ValidationResult(item.ErrorMessage, memberNames));
}
foreach (var prop in instance.GetType().GetProperties())
{
if (prop.GetSetMethod() == null)
{
continue;
}
else if (prop.PropertyType.IsClass && prop.PropertyType != typeof(string))
{
var value = prop.GetValue(instance);
if (value == null)
{
continue;
}
else if (value is IEnumerable<object> list)
{
var memberPrefix = (!string.IsNullOrEmpty(prefix) ? prefix + "." : "") + prop.Name;
int i = 0;
foreach (var item in list)
{
if (!TryValidateObject(item, serviceProvider, items, results, validateAllProperties: validateAllProperties, prefix: $"{memberPrefix}[{i}]"))
{
isValid = false;
}
i++;
}
}
else
{
var memberPrefix = (!string.IsNullOrEmpty(prefix) ? prefix + "." : "") + prop.Name;
if (!TryValidateObject(value, serviceProvider, items, results, validateAllProperties: validateAllProperties, prefix: memberPrefix))
{
isValid = false;
}
}
}
}
return isValid;
}
}
Example of using it to add model state errors to the bindingContext:
var validationResults = new List<ValidationResult>();
if (!RecursiveValidator.TryValidateObject(model, bindingContext.HttpContext.RequestServices, null, validationResults, validateAllProperties: true, prefix: bindingContext.ModelName))
{
foreach (var result in validationResults)
{
foreach (var member in result.MemberNames)
{
bindingContext.ModelState.AddModelError(member, result.ErrorMessage);
}
}
}

Serialize Dictionary<long, VALUE> to BSON documents

I want to serialize Dictionary<long, VALUE> to following JSON in MongoDB.
{
"213" : {},
"63624" : {},
...
}
I don't want other DictionaryRepresentation except DictionaryRepresentation.Document.Document
I am using MongoDB C# Driver (v2.0.1.27), and it is not smart to convert the long type key into string, which causes an exception.
You can do this with the existing serializers but it requires a small amount of configuration.
Assume the following class:
public class C
{
public int Id { get; set; }
public Dictionary<long, long> D { get; set; }
}
You can configure a custom serializer for the D property (the Dictionary) that uses a key serializer that serializes longs to strings. The code would look like this:
BsonClassMap.RegisterClassMap<C>(cm =>
{
cm.AutoMap();
var customDictionarySerializer = new DictionaryInterfaceImplementerSerializer<Dictionary<long, long>>(
dictionaryRepresentation: DictionaryRepresentation.Document,
keySerializer: new Int64Serializer(BsonType.String),
valueSerializer: BsonSerializer.SerializerRegistry.GetSerializer<long>());
cm.GetMemberMap(c => c.D).SetSerializer(customDictionarySerializer);
});
The key idea here is that even though the keys and values are both longs, we are using different serializers for the keys and the values.
If we then run a quick test:
var document = new C { Id = 1, D = new Dictionary<long, long> { { 2, 3 } } };
var json = document.ToJson();
Console.WriteLine(json);
We see that the Dictionary keys are now being serialized as strings:
{ "_id" : 1, "D" : { "2" : NumberLong(3) } }
Also I worked out another solution, hope it helps other people
public class LongDictionarySerializer<K> : DictionarySerializerBase<Dictionary<long, K>>
{
public LongDictionarySerializer() : base(DictionaryRepresentation.Document)
{
}
protected override Dictionary<long, K> CreateInstance()
{
return new Dictionary<long, K>();
}
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, Dictionary<long, K> value)
{
if (value != null)
{
Dictionary<string, K> dic = value.ToDictionary(d => d.Key.ToString(), d => d.Value);
BsonSerializer.Serialize<Dictionary<string, K>>(context.Writer, dic);
}
else
BsonSerializer.Serialize<object>(context.Writer, null);
}
public override Dictionary<long, K> Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
{
Dictionary<string, K> dic = BsonSerializer.Deserialize<Dictionary<string, K>>(context.Reader);
if (dic == null)
return null;
Dictionary<long, K> ret = new Dictionary<long, K>();
foreach( var pair in dic )
{
long key;
if (!long.TryParse(pair.Key, out key))
continue;
ret[key] = pair.Value;
}
return ret;
}
}
Then on the field
[BsonElement(Fields.Markets)]
[BsonSerializer(typeof(LongDictionarySerializer<XXX>))]
public Dictionary<long, XXX> Markets { get; set; }

Access to a property with Interface cast

ActionBase, ActionA, ActionB and ActionC are Entities (from a database). ActionA, ActionB and ActionC are derived type of ActionBase.
ActionB and ActionC implements ISpecialAction with a SpecialProperty.
ex :
public interface ISpecialAction
{
Guid SpecialProperty { get; }
}
public partial class ActionBase
{
public objectX OnePropertyBase { get; set; }
}
public partial class ActionA : ActionBase
{
public objectY OnePropertyA { get; set; }
}
public partial class ActionB:ActionBase,ISpecialAction
{
public objectZ OnePropertyB { get; set; }
public Guid SpecialProperty
{
get
{
return OnePropertyB.ID;
}
}
}
public partial class ActionC : ActionBase ,ISpecialAction
{
public objectW OnePropertyC { get; set; }
public Guid SpecialProperty
{
get
{
return OnePropertyC.ID;
}
}
}
My problem is that SpecialProperty is build from other Properties of the objects (ActionB or ActionC) and when the cast (to ISpecialAction) is done, OtherProperty and OtherProperty2 are null.
I tried :
GetActionBase().ToList().Where(x=>x is ISpecialAction && ((dynamic) x).SpecialProperty== p_SpecialProperty);
GetActionBase().ToList().Where(x=>x is ISpecialAction && ((ISpecialAction) x).SpecialProperty== p_SpecialProperty);
GetActionBase().ToList().OfType<ISpecialAction>().Where(x => x.SpecialProperty== p_SpecialProperty).Cast<ActionBase>();
return GetActionOnGoing().ToList().OfType<ICityAction>().Cast<ActionBase>().Where(x => ((dynamic)x).CityId == p_CityId);
remark : OfType<> doesn't works with an Interface in Linq to entities but is ok in Linq to object
How do I access my property interface without knowing the type of the object?
I might missed something but this is Ok with the code you provided :
public class objectX
{
}
public class objectY
{
}
public class objectZ
{
public Guid ID { get { return Guid.NewGuid();} }
}
public class objectW
{
public Guid ID { get { return new Guid(); } }
}
class Program
{
private static Guid p_SpecialProperty;
static void Main(string[] args)
{
var result = GetActionBase().ToList().Where(x => x is ISpecialAction && ((dynamic)x).SpecialProperty == p_SpecialProperty).FirstOrDefault();
var result1 = GetActionBase().ToList().Where(x => x is ISpecialAction && ((ISpecialAction)x).SpecialProperty == p_SpecialProperty).FirstOrDefault();
var result2 = GetActionBase().ToList().OfType<ISpecialAction>().Where(x => x.SpecialProperty == p_SpecialProperty).Cast<ActionBase>().FirstOrDefault();
}
private static IEnumerable<ActionBase> GetActionBase()
{
return new List<ActionBase> {new ActionA{OnePropertyA= new objectY()}, new ActionB{OnePropertyB=new objectZ()},new ActionC{OnePropertyC=new objectW()} };
}
}
Not sure if I exactly understand your question, but could you try using an intermediate interface, such as:
public interface ISpecialActionB : ISpecialAction
{
objectZ OnePropertyB { get; set; }
}
public class ActionB : ActionBase, ISpecialActionB
{
//same stuff
}
and casting to that instead.
var b = new ActionB{OnePropertyB = new Whatever()};
var bAsSpecial = b as ISpecialActionB;
var whatever = b.OnePropertyB; // should not be null
It' ok.
Your example run very well without problem so I searched in a other way : AutoMapper.
l_List.Actions = Mapper.Map<List<ActionBase>, Action[]>(l_ActionManagement.GetActionBySpecialId(l_Special.ID).ToList());
The problem was not interfaces or Linq queries but it was that automapper need an empty constructor and in this constructor, I need to initialize OnePropertyB and OnePropertyC to compute SpecialProperty.
Thanks

Translating expression tree from a type to another type with complex mappings

inspired by this answer I'm trying to map a property on a model class to an expression based on the actual entity.
These are the two classes involved:
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Id { get; set; }
public DateTime? BirthDate { get; set; }
public int CustomerTypeId { get; set; }
}
public class CustomerModel
{
...
public bool HasEvenId { get; set; }
}
An example of a possible expression I'd like to convert is:
Expression<Func<CustomerModel, bool>> from = model => model.HasEvenId;
Expression<Func<Customer, bool>> to = entity => ((entity.Id % 2) == 0);
The problem is that I have to expose an OData endpoint via ASP.NET WebAPI but I need to make some operations on the entities before I can them, hence the need of a model class and the need to translate the expression based on the model that I could receive as an OData query in an expression based on the entity, that I would use to query EF4.
This is where I got so far:
private static readonly Dictionary<Expression, Expression> Mappings = GetMappings();
private static Dictionary<Expression, Expression> GetMappings()
{
var mappings = new Dictionary<Expression, Expression>();
var mapping = GetMappingFor((CustomerModel model) => model.HasEvenId, (Customer customer) => (customer.Id%2) == 0);
mappings.Add(mapping.Item1, mapping.Item2);
return mappings;
}
private static Tuple<Expression, Expression> GetMappingFor<TFrom, TTo, TValue>(Expression<Func<TFrom, TValue>> fromExpression, Expression<Func<TTo, TValue>> toExpression)
{
MemberExpression fromMemberExpression = (MemberExpression) fromExpression.Body;
return Tuple.Create<Expression, Expression>(fromMemberExpression, toExpression);
}
public static Expression<Func<TTo, bool>> Translate<TFrom, TTo>(Expression<Func<TFrom, bool>> expression, Dictionary<Expression, Expression> mappings = null)
{
if (expression == null)
return null;
string parameterName = expression.Parameters[0].Name;
parameterName = string.IsNullOrWhiteSpace(parameterName) ? "p" : parameterName;
var param = Expression.Parameter(typeof(TTo), parameterName);
var subst = new Dictionary<Expression, Expression> { { expression.Parameters[0], param } };
ParameterChangeVisitor parameterChange = new ParameterChangeVisitor(parameterName);
if (mappings != null)
foreach (var mapp in mappings)
subst.Add(mapp.Key, parameterChange.Visit(mapp.Value));
var visitor = new TypeChangeVisitor(typeof(TFrom), typeof(TTo), subst);
return Expression.Lambda<Func<TTo, bool>>(visitor.Visit(expression.Body), param);
}
public IQueryable<CustomerModel> Get()
{
var filterExtractor = new ODataFilterExtractor<CustomerModel>();
Expression<Func<CustomerModel, bool>> expression = filterExtractor.Extract(Request);
Expression<Func<Customer, bool>> translatedExpression = Translate<CustomerModel, Customer>(expression, Mappings);
IQueryable<Customer> query = _context.Customers;
if (translatedExpression != null)
query = query.Where(translatedExpression);
var finalQuery = from item in query.AsEnumerable()
select new CustomerModel()
{
FirstName = item.FirstName,
LastName = item.LastName,
Id = item.Id,
BirthDate = item.BirthDate,
CustomerTypeId = item.CustomerTypeId,
HasEvenId = (item.Id % 2 ) == 0
};
return finalQuery.AsQueryable();
}
where:
ODataFilterExtractor is a class that extract the $filter expression from the RequestMessage we receive;
ParameterChangeVisitor just changes all the ParameterExpression to a new one having the provided string as parameter name;
In addition, I changed the VisitMember method of the answer linked above in this way:
protected override Expression VisitMember(MemberExpression node)
{
// if we see x.Name on the old type, substitute for new type
if (node.Member.DeclaringType == _from)
{
MemberInfo toMember = _to.GetMember(node.Member.Name, node.Member.MemberType, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).SingleOrDefault();
if (toMember != null)
{
return Expression.MakeMemberAccess(Visit(node.Expression), toMember);
}
else
{
if (_substitutions.Select(kvp => kvp.Key).OfType<MemberExpression>().Any(me => me.Member.Equals(node.Member)))
{
MemberExpression key = _substitutions.Select(kvp => kvp.Key).OfType<MemberExpression>().Single(me => me.Member.Equals(node.Member));
Expression value = _substitutions[key];
// What to return here?
return Expression.Invoke(value);
}
}
}
return base.VisitMember(node);
}
Thanks for you help.
I took the liberty of modifying your code just a hair but this does the trick,
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Id { get; set; }
public DateTime? BirthDate { get; set; }
public int CustomerTypeId { get; set; }
}
public class CustomerModel
{
public string FullName { get; set; }
public bool HasEvenId { get; set; }
}
sealed class AToBConverter<TA, TB> : ExpressionVisitor
{
private readonly Dictionary<ParameterExpression, ParameterExpression> _parameters = new Dictionary<ParameterExpression, ParameterExpression>();
private readonly Dictionary<MemberInfo, LambdaExpression> _mappings;
protected override Expression VisitParameter(ParameterExpression node)
{
if (node.Type == typeof(TA))
{
ParameterExpression parameter;
if (!this._parameters.TryGetValue(node, out parameter))
{
this._parameters.Add(node, parameter = Expression.Parameter(typeof(TB), node.Name));
}
return parameter;
}
return node;
}
protected override Expression VisitMember(MemberExpression node)
{
if (node.Expression == null || node.Expression.Type != typeof(TA))
{
return base.VisitMember(node);
}
Expression expression = this.Visit(node.Expression);
if (expression.Type != typeof(TB))
{
throw new Exception("Whoops");
}
LambdaExpression lambdaExpression;
if (this._mappings.TryGetValue(node.Member, out lambdaExpression))
{
return new SimpleExpressionReplacer(lambdaExpression.Parameters.Single(), expression).Visit(lambdaExpression.Body);
}
return Expression.Property(
expression,
node.Member.Name
);
}
protected override Expression VisitLambda<T>(Expression<T> node)
{
return Expression.Lambda(
this.Visit(node.Body),
node.Parameters.Select(this.Visit).Cast<ParameterExpression>()
);
}
public AToBConverter(Dictionary<MemberInfo, LambdaExpression> mappings)
{
this._mappings = mappings;
}
}
sealed class SimpleExpressionReplacer : ExpressionVisitor
{
private readonly Expression _replacement;
private readonly Expression _toFind;
public override Expression Visit(Expression node)
{
return node == this._toFind ? this._replacement : base.Visit(node);
}
public SimpleExpressionReplacer(Expression toFind, Expression replacement)
{
this._toFind = toFind;
this._replacement = replacement;
}
}
class Program
{
private static Dictionary<MemberInfo, LambdaExpression> GetMappings()
{
var mappings = new Dictionary<MemberInfo, LambdaExpression>();
var mapping = GetMappingFor(model => model.HasEvenId, customer => (customer.Id % 2) == 0);
mappings.Add(mapping.Item1, mapping.Item2);
mapping = GetMappingFor(model => model.FullName, customer => customer.FirstName + " " + customer.LastName);
mappings.Add(mapping.Item1, mapping.Item2);
return mappings;
}
private static Tuple<MemberInfo, LambdaExpression> GetMappingFor<TValue>(Expression<Func<CustomerModel, TValue>> fromExpression, Expression<Func<Customer, TValue>> toExpression)
{
return Tuple.Create(((MemberExpression)fromExpression.Body).Member, (LambdaExpression)toExpression);
}
static void Main()
{
Expression<Func<CustomerModel, bool>> source = model => model.HasEvenId && model.FullName == "John Smith";
Expression<Func<Customer, bool>> desiredResult = model => (model.Id % 2) == 0 && (model.FirstName + " " + model.LastName) == "John Smith";
Expression output = new AToBConverter<CustomerModel, Customer>(GetMappings()).Visit(source);
Console.WriteLine("The two expressions do {0}match.", desiredResult.ToString() == output.ToString() ? null : "not ");
Console.ReadLine();
}
}
Another solution would be to use AutoMapper to map complex types and modify the resulting expression query with an ExpressionTransformer before it gets executed. I will try to explain with a complete sample:
Model classes
Some POCO classes just for holding data.
public enum CostUnitType
{
None = 0,
StockUnit = 1,
MachineUnit = 2,
MaintenanceUnit = 3
}
public class CostUnit
{
public string CostUnitId { get; set; }
public string WorkplaceId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public CostUnitType Type { get; set; }
public override string ToString()
{
return CostUnitId + " " + Name + " " + Type;
}
}
public class StockUnit
{
public string Id { get; set; }
public string Name { get; set; }
}
public class MachineUnit
{
public string Id { get; set; }
public string Name { get; set; }
}
public class MaintenanceUnit
{
public string Id { get; set; }
public string Name { get; set; }
}
The ExpressionTransformer class
Most of the time, using the Mapper static class is fine, but sometimes you need to map the same types with different configurations, so you need to explicitly use an IMappingEngine like this:
var configuration = new ConfigurationStore(new TypeMapFactory(), MapperRegistry.Mappers);
var engine = new MappingEngine(configuration);
The way to create a MappingEngine is not obvious at all. I had to dig in the source code to find how it was done.
public static class ExpressionTransformer
{
private static readonly MappingEngine Mapper;
/// <summary>
/// Initializes the <see cref="ExpressionTransformer"/> class.
/// Creates an instance of AutoMapper. Initializes mappings.
/// </summary>
static ExpressionTransformer()
{
ConfigurationStore configurationStore = new ConfigurationStore(new TypeMapFactory(), MapperRegistry.Mappers);
// Create mapping
// Maps Id from StockUnit to CostUnitId from CostUnit
configurationStore.CreateMap<StockUnit, CostUnit>()
.ForMember(m => m.CostUnitId, opt => opt.MapFrom(src => src.Id));
// Maps Id from MachineUnit to CostUnitId from CostUnit
configurationStore.CreateMap<MachineUnit, CostUnit>()
.ForMember(m => m.CostUnitId, opt => opt.MapFrom(src => src.Id));
// Maps Id from MaintenanceUnit to CostUnitId from CostUnit
configurationStore.CreateMap<MaintenanceUnit, CostUnit>()
.ForMember(m => m.CostUnitId, opt => opt.MapFrom(src => src.Id));
// Create instance of AutoMapper
Mapper = new MappingEngine(configurationStore);
}
public static Expression<Func<TDestination, bool>> Tranform<TSource, TDestination>(Expression<Func<TSource, bool>> sourceExpression)
{
// Resolve mappings by AutoMapper for given types.
var map = Mapper.ConfigurationProvider.FindTypeMapFor(typeof(TSource), typeof(TDestination));
if (map == null)
{
throw new AutoMapperMappingException(string.Format("No Mapping found for {0} --> {1}.", typeof(TSource).Name, typeof(TDestination).Name));
}
// Transform from TSource to TDestination with specified mappings
var visitor = new ParameterTypeVisitor<TSource, TDestination>(sourceExpression, map.GetPropertyMaps());
var expression = visitor.Transform();
return expression;
}
private class ParameterTypeVisitor<TSource, TDestination> : ExpressionVisitor
{
private readonly Dictionary<string, ParameterExpression> _parameters;
private readonly Expression<Func<TSource, bool>> _expression;
private readonly IEnumerable<PropertyMap> _maps;
public ParameterTypeVisitor(Expression<Func<TSource, bool>> expression, IEnumerable<PropertyMap> maps)
{
_parameters = expression.Parameters
.ToDictionary(p => p.Name, p => Expression.Parameter(typeof(TDestination), p.Name));
_expression = expression;
_maps = maps;
}
public Expression<Func<TDestination, bool>> Transform()
{
return (Expression<Func<TDestination, bool>>) Visit(_expression);
}
protected override Expression VisitMember(MemberExpression node)
{
if (node.Member.DeclaringType == typeof(TSource))
{
var memberName = node.Member.Name;
var member = _maps.FirstOrDefault(p => typeof(TSource) == node.Expression.Type
&& p.SourceMember.Name == memberName);
if (member != null)
{
// Return Property from TDestination
var expression = Visit(node.Expression);
return Expression.MakeMemberAccess(expression, member.DestinationProperty.MemberInfo);
}
}
return base.VisitMember(node);
}
protected override Expression VisitParameter(ParameterExpression node)
{
var parameter = _parameters[node.Name];
return parameter;
}
protected override Expression VisitLambda<T>(Expression<T> node)
{
var expression = Visit(node.Body);
return Expression.Lambda(expression, _parameters.Select(x => x.Value));
}
}
}
Usage
To Convert an expression we just need to call Transform Method of ExpressionTransformer class
Expression<Func<StockUnit, bool>> stockQuery = unit => unit.Id == "0815" && unit.Name == "ABC";
// Call Transform<TSource, TDestination> method.
Expression<Func<CostUnit, bool>> costUnitQuery = ExpressionTransformer.Tranform<StockUnit, CostUnit>(stockQuery);
Result

data mode : read write with c# local database in wp7

I created a local db with helper app project. and deployed it from isolate storage to installation folder,i added to project directory with content build action by add existing item. my problem is that i want to insert data, but i don't know how to move the db file to isolate storage to insert and data must add to my .sdf file that is locate in my project directory also.
Souphia,
While learning to use WP, I wrote a simple application that tracked tasks.
One version of that app stored all task data in Sql on the phone.
You can read the post and download all the code for the app here:
http://www.ritzcovan.com/2012/02/building-a-simple-windows-phone-app-part-3/
But, here is some of the code from that project:
First we have the model class decorated with the appropriate attributes:
[Table]
public class Task : INotifyPropertyChanged, INotifyPropertyChanging
{
[Column(IsDbGenerated = false, IsPrimaryKey = true, CanBeNull = false)]
public string Id
{
get { return _id; }
set
{
NotifyPropertyChanging("Id");
_id = value;
NotifyPropertyChanging("Id");
}
}
[Column]
public string Name
{
get { return _name; }
set
{
NotifyPropertyChanging("Name");
_name = value;
NotifyPropertyChanged("Name");
}
}
[Column]
public string Category
{
get { return _category; }
set
{
NotifyPropertyChanging("Category");
_category = value;
NotifyPropertyChanged("Category");
}
}
[Column]
public DateTime? DueDate
{
get { return _dueDate; }
set
{
NotifyPropertyChanging("DueDate");
_dueDate = value;
NotifyPropertyChanged("DueDate");
}
}
[Column]
public DateTime? CreateDate
{
get { return _createDate; }
set
{
NotifyPropertyChanging("CreateDate");
_createDate = value;
NotifyPropertyChanged("CreateDate");
}
}
[Column]
public bool IsComplete
{
get { return _isComplete; }
set
{
NotifyPropertyChanging("IsComplete");
_isComplete = value;
NotifyPropertyChanged("IsComplete");
}
}
[Column(IsVersion = true)] private Binary _version;
private string _id;
private bool _isComplete;
private DateTime? _createDate;
private DateTime? _dueDate;
private string _name;
private string _category;
public event PropertyChangedEventHandler PropertyChanged;
public event PropertyChangingEventHandler PropertyChanging;
public void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
public void NotifyPropertyChanging(string property)
{
if (PropertyChanging != null)
PropertyChanging(this, new PropertyChangingEventArgs(property));
}
}
In the constructor in app.xaml.cs, I have the following:
TaskMasterDataContext = new TaskMasterDataContext();
if (!TaskMasterDataContext.DatabaseExists())
{
TaskMasterDataContext.CreateDatabase();
DatabaseHelper.SetupDatabase(TaskMasterDataContext);
}
and here is the TaskMasterDataContext.cs code
public class TaskMasterDataContext : DataContext
{
public TaskMasterDataContext() : base("Data Source=isostore:/TaskMasterData.sdf")
{
}
public Table<Task> Tasks;
}
public static class DatabaseHelper
{
public static void SetupDatabase(TaskMasterDataContext dataContext)
{
string category = string.Empty;
var tasks = new List<Task>();
for (int i = 0; i < 20; i++)
{
tasks.Add(new Task()
{
Id = System.Guid.NewGuid().ToString(),
Category = GetCategoryString(i),
CreateDate = DateTime.Now,
DueDate = DateTime.Now.AddDays(new Random().Next(1, 30)),
IsComplete = false,
Name = String.Format("{0} Task # {1}", GetCategoryString(i), i)
});
}
dataContext.Tasks.InsertAllOnSubmit(tasks);
dataContext.SubmitChanges();
}
private static string GetCategoryString(int i)
{
if (i%2 == 0)
return "home";
if (i%3 == 0)
return "personal";
return "work";
}
}
The DatabaseHelper class is just there to populate the DB with some test data after its created.
I hope this helps.

Resources