Ormlite: Setting model attributes in c# no longer works - ormlite-servicestack

I like to keep my data models clean (and not dependent on any Servicestack DLLs) by defining any attributes just in the database layer. However since upgrading to ver 5.0, my application fails to correctly recognise attributes set in c# using AddAttributes().
The code below shows a minimal reproducable example.
using ServiceStack;
using ServiceStack.DataAnnotations;
using ServiceStack.OrmLite;
namespace OrmliteAttributeTest
{
class Program
{
static void Main(string[] args)
{
var type = typeof(DataItem2);
type.AddAttributes(new AliasAttribute("DataItem2Table"));
var prop = type.GetProperty(nameof(DataItem2.ItemKey));
if (prop != null)
prop.AddAttributes(new PrimaryKeyAttribute());
prop = type.GetProperty(nameof(DataItem2.ItemDescription));
if (prop != null)
prop.AddAttributes(new StringLengthAttribute(100));
SqlServerDialect.Provider.GetStringConverter().UseUnicode = true;
var connectionString = #"Data Source=localhost\sqlexpress; Initial Catalog=OrmLiteTest; Integrated Security=True;";
var connectionFactory = new OrmLiteConnectionFactory(connectionString, SqlServerDialect.Provider);
using (var db = connectionFactory.OpenDbConnection())
{
db.CreateTableIfNotExists<DataItem>();
db.CreateTableIfNotExists<DataItem2>();
}
}
}
[Alias("DataItemTable")]
public class DataItem
{
[PrimaryKey]
public int ItemKey { get; set; }
[StringLength(100)]
public string ItemDescription { get; set; }
}
public class DataItem2
{
public int ItemKey { get; set; }
public string ItemDescription { get; set; }
}
}
The table for DataItem is created correctly using the attributes as specified. The table for DataItem2 fails to use any of the attibubes defined in the code.

The issue is that the static constructor of JsConfig.InitStatics() needs to be initialized once on Startup which reinitializes the static configuration (and dynamic attributes added) in ServiceStack Serializers.
This is implicitly called in ServiceStack libraries like OrmLiteConnectionFactory which because it hasn't been called before will reinitialize that ServiceStack.Text static configuration. To avoid resetting the dynamic attributes you can initialize the OrmLiteConnectionFactory before adding the attributes:
var connectionFactory = new OrmLiteConnectionFactory(connStr, SqlServerDialect.Provider);
var type = typeof(DataItem2);
type.AddAttributes(new AliasAttribute("DataItem2Table"));
var prop = type.GetProperty(nameof(DataItem2.ItemKey));
if (prop != null)
prop.AddAttributes(new PrimaryKeyAttribute());
prop = type.GetProperty(nameof(DataItem2.ItemDescription));
if (prop != null)
prop.AddAttributes(new StringLengthAttribute(100));
SqlServerDialect.Provider.GetStringConverter().UseUnicode = true;
Or if preferred you can explicitly call InitStatics() before adding any attributes, e.g:
JsConfig.InitStatics();
var type = typeof(DataItem2);
type.AddAttributes(new AliasAttribute("DataItem2Table"));
//...

Related

Is there a way to dynamically add a DbSet to EF core and to use Linq against the dynamic DbSet?

I'm using EF Core v. 5.0 and SQLite DB and I'm trying to dynamically add a DbSet to my DbContext. I have followed and readapted this guide to EF Core: https://romiller.com/2012/03/26/dynamically-building-a-model-with-code-first/ and I realized this DbContext class:
internal class GenericAppContext : DbContext
{
public GenericAppContext()
{
//Disable the EF cache system to execute every running the OnModelCreating method.
//ATTENTION: This is a performance loss action!
this.ChangeTracker.LazyLoadingEnabled = false;
}
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
var baseDir = AppDomain.CurrentDomain.BaseDirectory;
//if "bin" is present, remove all the exceeding path starting from "bin" word
if (baseDir.Contains("bin"))
{
int index = baseDir.IndexOf("bin");
baseDir = baseDir.Substring(0, index);
}
options.UseSqlite($"Data Source={baseDir}Database\\TestSQLite.db");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
MethodInfo addMethod = typeof(ModelBuilder).GetMethods().First(e => e.Name == "Entity");
foreach (var assembly in AppDomain.CurrentDomain
.GetAssemblies()
.Where(a => a.GetName().Name != "EntityFramework"))
{
IEnumerable<Type> configTypes = assembly
.GetTypes()
.Where(t => t.BaseType != null
&& t.BaseType.IsGenericType
&& t.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>));
foreach (var type in configTypes)
{
Type entityType = type.BaseType.GetGenericArguments().Single();
object entityConfig = assembly.CreateInstance(type.FullName);
addMethod?.MakeGenericMethod(entityType)
.Invoke(modelBuilder, new object[] { });
}
}
}
}
My "Blog" and "Article" classes:
internal class Blog : EntityTypeConfiguration<Blog>
{
public int Id { get; set; }
public string Name { get; set; }
public string Category { get; set; }
}
internal class Article : EntityTypeConfiguration<Article>
{
[Key]
public int Id { get; set; }
public string Body { get; set; }
}
And here is how I initialized the DbContext, in "tables" variable I can see the "Article" table added dynamically using context.Model.GetRelationalModel().Tables.ToList()
public static string TestMethod()
{
using (var context = new GenericAppContext())
{
string result = string.Empty;
//Ensures that the database for the context exists. If it exists, no action is taken. If it does not exist then the database and all its schema are created.
context.Database.EnsureCreated();
List<ITable> tables = context.Model.GetRelationalModel().Tables.ToList();
}
}
At this point, I succesfully saw the "Article" class added dynamically, but I can't query "Article" using Linq, of course, because it doesn't exist fisically in the DbContext.
Is there a way to use Linq against a dynamic DbSet added table like "Article"?
In the end I solved using "Linq.Dynamic.Core" that allows you to use Linq and writing a lambda expression as a string, and much more, here more info about Dynamic Linq.
Here how I modified the TestMethod() showed before, and how I did the query against the "Article" table added dynamically via my "GenericContextApp" class.
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
public static string TestMethod()
{
using (var context = new GenericAppContext())
{
string result = string.Empty;
//Ensures that the database for the context exists. If it exists, no action is taken. If it does not exist then the database and all its schema are created.
context.Database.EnsureCreated();
IEnumerable<IEntityType> contextEntitiesList = context.Model.GetEntityTypes();
IQueryable<IEntityType> entitiesList = contextEntitiesList.Select(p => p).ToList().AsQueryable();
//Query against the dinamycally added "Article" table. It will return the SQL query as a string.
result = context.Query("Your.Complete.Namespace.Article").Where("Body == \"ciao\"").ToQueryString();
return result;
}
}

Entity Framework Core ignore null update

My EF models are like so:
public class Base
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class Foo : Base
{
public IEnumerable<Bar> Bars { get; set; }
}
public class Bar : Base
{
...
}
My intention was to build the API in such a way that if you specify null on an update it would discard that value however it does not appear to work that way. In my repository update code I do the following:
public override IEnumerable<Base> Update(IEnumerable<Base> items)
{
foreach (var item in items.OfType<Foo>())
{
var existingItem = _context.Foos.Find(item.Id);
if (existingItem == null)
{
throw new InvalidOperationException(
$"Can't update item of type `{typeof(Foo)}` as it doesn't exist. ");
}
var entry = _context.Entry(existingItem);
entry.CurrentValues.SetValues(item);
foreach (var property in entry.Properties)
{
var original = property.OriginalValue;
var current = property.CurrentValue;
property.IsModified = original != null && !original.Equals(current);
}
var collection = entry.Collection(nameof(Foo.Bars));
if (collection.CurrentValue == null)
collection.IsModified = false;
}
var rows = _context.SaveChanges();
return Read(items.Select(e => e.Id));
}
Since the Foo.Bars property is actually a Collection/Navigation property there's no OriginalValue property to it, only a CurrentValue and this means I can't discard the value if it is null. Also it seems that setting the collection.IsModified to false has no effect and the Foo.Bars property is set to null regardless of the IsModified state.
Looking for advice on perhaps a better way to handle this or something I'm missing. Thanks.

CamelCaseNamingStrategy vs CamelCasePropertyNamesContractResolver

With JSON.Net, I see 2 different ways of saying that I want my properties to be serialized in camelCase:
CamelCaseNamingStrategy
CamelCasePropertyNamesContractResolver
According to this code snippet, both options give the same result since the assertion does not fail:
public class Bar
{
public int SomeValue { get; set; }
}
public class Foo
{
public Bar Bar { get; set; } = new Bar();
public string AnotherValue { get; set; }
}
[Fact]
public void TestBothOptions()
{
var x = new Foo();
x.AnotherValue = "test";
x.Bar.SomeValue = 12;
var serializerSettingsWithNamingStrategy = new JsonSerializerSettings();
serializerSettingsWithNamingStrategy.ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy(),
};
var serializerSettingsWithContractResolver = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
};
var one = JsonConvert.SerializeObject(x, serializerSettingsWithNamingStrategy);
var two = JsonConvert.SerializeObject(x, serializerSettingsWithContractResolver);
Assert.Equal(one, two); // {"bar":{"someValue":12},"anotherValue":"test"}
}
So, does anybody know the difference between the two options?
CamelCaseNamingStrategy is newer and more flexible to use, since you can specify, via attributes, different naming strategies to use for different classes or properties in your model without using a resolver, e.g.:
[JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))]
public class MyClass
{
public string CamelCaseProperty1 { get; set; }
public string CamelCaseProperty2 { get; set; }
[JsonProperty(NamingStrategyType = typeof(DefaultNamingStrategy)]
public string DefaultCaseProperty { get; set; }
}
You can also create your own custom naming strategy class if you need to.
CamelCasePropertyNamesContractResolver still survives for backward compatibility. After CamelCaseNamingStrategy was introduced, the source code for that resolver was changed to use the strategy. You can see this clearly in this excerpt from the source code:
public class CamelCasePropertyNamesContractResolver : DefaultContractResolver
{
...
public CamelCasePropertyNamesContractResolver()
{
NamingStrategy = new CamelCaseNamingStrategy
{
ProcessDictionaryKeys = true,
OverrideSpecifiedNames = true
};
}
...
}
Note there are subtle differences between the CamelCasePropertyNamesContractResolver and the DefaultContractResolver in terms of how they cache information, which may or may not be of concern to you. See Does Json.NET cache types' serialization information? for more information about that.

How do I test response data using nUnit?

Django has a very handy test client/dummy web browser that one can use in test cases to verify the correctness of HTTP responses (e.g., status codes, context/model data). It does not require you to have the web server running, as it deals directly with the framework to simulate the calls.
I'd really love an nUnit (or similar) equivalent that we can slip right into our test suites. We're working in MVC3 and 4, and want to check things like successful 301 redirects, that model validation is correct, and that ViewModel data is correct in the views.
What's the best solution for this?
ViewModel Data should be easy to check with the following:
public T GetViewModelFromResult<T>(ActionResult result) where T : class
{
Assert.IsInstanceOf<ViewResult>(result);
var model = ((ViewResult)result).Model;
Assert.IsInstanceOf<T>(model);
return model as T;
}
[Test]
public void TheModelHasTheOrder()
{
var controller = new MyController();
var result = controller.MyActionMethod();
var model = GetViewModelFromResult<MyModel>();
Assert.That(model, Is.SameAs(???));
}
As for the model validation, if you are using the out of the box .net property attributes like [Required] etc, you can be pretty sure they will work fine, and won't need testing.
To explicitly test the [Required] etc attributes on your object you will have extract the built in .net validation into another class. Then use that class in your controllers to validate your objects, instead of the Model.IsValid property on your controller.
The model validator class:
public class ModelValidator : IModelValidator
{
public bool IsValid(object entity)
{
return Validate(entity, new List<ValidationResult>());
}
public IEnumerable<ValidationResult> Validate(object entity)
{
var validationResults = new List<ValidationResult>();
Validate(entity, validationResults);
return validationResults;
}
private static bool Validate(object entity, ICollection<ValidationResult> validationResults)
{
if (entity != null)
{
var validationContext = new ValidationContext(entity, null, null);
return Validator.TryValidateObject(entity, validationContext, validationResults);
}
return false;
}
}
This could be verifiable in unit tests with the following:
public class MySampleEntity
{
[Required]
public string X { get; set; }
[Required]
public int Y { get; set; }
}
[TestFixture]
public class ModelValidatorTests
{
[Test]
public void GivenThePropertiesArePopulatedTheModelIsValid()
{
// arrange
var _validator = new ModelValidator();
var _entity = new MySampleEntity { X = "ABC", Y = 50 };
// act
var _result = _validator.IsValid(_entity);
// assert
Assert.That(_result, Is.True);
}
}

Unit Testing ASP.NET DataAnnotations validation

I am using DataAnnotations for my model validation i.e.
[Required(ErrorMessage="Please enter a name")]
public string Name { get; set; }
In my controller, I am checking the value of ModelState. This is correctly returning false for invalid model data posted from my view.
However, when executing the unit test of my controller action, ModelState always returns true:
[TestMethod]
public void Submitting_Empty_Shipping_Details_Displays_Default_View_With_Error()
{
// Arrange
CartController controller = new CartController(null, null);
Cart cart = new Cart();
cart.AddItem(new Product(), 1);
// Act
var result = controller.CheckOut(cart, new ShippingDetails() { Name = "" });
// Assert
Assert.IsTrue(string.IsNullOrEmpty(result.ViewName));
Assert.IsFalse(result.ViewData.ModelState.IsValid);
}
Do I need to do anything extra to set up the model validation in my tests?
I posted this in my blog post:
using System.ComponentModel.DataAnnotations;
// model class
public class Fiz
{
[Required]
public string Name { get; set; }
[Required]
[RegularExpression(".+#..+")]
public string Email { get; set; }
}
// in test class
[TestMethod]
public void EmailRequired()
{
var fiz = new Fiz
{
Name = "asdf",
Email = null
};
Assert.IsTrue(ValidateModel(fiz).Any(
v => v.MemberNames.Contains("Email") &&
v.ErrorMessage.Contains("required")));
}
private IList<ValidationResult> ValidateModel(object model)
{
var validationResults = new List<ValidationResult>();
var ctx = new ValidationContext(model, null, null);
Validator.TryValidateObject(model, ctx, validationResults, true);
return validationResults;
}
Validation will be performed by the ModelBinder. In the example, you construct the ShippingDetails yourself, which will skip the ModelBinder and thus, validation entirely. Note the difference between input validation and model validation. Input validation is to make sure the user provided some data, given he had the chance to do so. If you provide a form without the associated field, the associated validator won't be invoked.
There have been changes in MVC2 on model validation vs. input validation, so the exact behaviour depends on the version you are using. See http://bradwilson.typepad.com/blog/2010/01/input-validation-vs-model-validation-in-aspnet-mvc.html for details on this regarding both MVC and MVC 2.
[EDIT] I guess the cleanest solution to this is to call UpdateModel on the Controller manually when testing by providing a custom mock ValueProvider. That should fire validation and set the ModelState correctly.
I was going through http://bradwilson.typepad.com/blog/2009/04/dataannotations-and-aspnet-mvc.html, in this post I didn't like the idea of putting the validation tests in controller test and somewhat manual checking in each test that if the validation attribute exists or not. So, below is the helper method and it's usage which I implemented, it works for both EDM (which has metadata attributes, because of the reason we can not apply attributes on auto generated EDM classes) and POCO objects which have ValidationAttributes applied to their properties.
The helper method does not parse into hierarchical objects, but validation can be tested on flat individual objects(Type-level)
class TestsHelper
{
internal static void ValidateObject<T>(T obj)
{
var type = typeof(T);
var meta = type.GetCustomAttributes(false).OfType<MetadataTypeAttribute>().FirstOrDefault();
if (meta != null)
{
type = meta.MetadataClassType;
}
var propertyInfo = type.GetProperties();
foreach (var info in propertyInfo)
{
var attributes = info.GetCustomAttributes(false).OfType<ValidationAttribute>();
foreach (var attribute in attributes)
{
var objPropInfo = obj.GetType().GetProperty(info.Name);
attribute.Validate(objPropInfo.GetValue(obj, null), info.Name);
}
}
}
}
/// <summary>
/// Link EDM class with meta data class
/// </summary>
[MetadataType(typeof(ServiceMetadata))]
public partial class Service
{
}
/// <summary>
/// Meta data class to hold validation attributes for each property
/// </summary>
public class ServiceMetadata
{
/// <summary>
/// Name
/// </summary>
[Required]
[StringLength(1000)]
public object Name { get; set; }
/// <summary>
/// Description
/// </summary>
[Required]
[StringLength(2000)]
public object Description { get; set; }
}
[TestFixture]
public class ServiceModelTests
{
[Test]
[ExpectedException(typeof(ValidationException), ExpectedMessage = "The Name field is required.")]
public void Name_Not_Present()
{
var serv = new Service{Name ="", Description="Test"};
TestsHelper.ValidateObject(serv);
}
[Test]
[ExpectedException(typeof(ValidationException), ExpectedMessage = "The Description field is required.")]
public void Description_Not_Present()
{
var serv = new Service { Name = "Test", Description = string.Empty};
TestsHelper.ValidateObject(serv);
}
}
this is another post http://johan.driessen.se/archive/2009/11/18/testing-dataannotation-based-validation-in-asp.net-mvc.aspx which talks about validating in .Net 4, but i think i am going to stick to my helper method which is valid in both 3.5 and 4
I like to test the data attributes on my models and view models outside the context of the controller. I've done this by writing my own version of TryUpdateModel that doesn't need a controller and can be used to populate a ModelState dictionary.
Here is my TryUpdateModel method (mostly taken from the .NET MVC Controller source code):
private static ModelStateDictionary TryUpdateModel<TModel>(TModel model,
IValueProvider valueProvider) where TModel : class
{
var modelState = new ModelStateDictionary();
var controllerContext = new ControllerContext();
var binder = ModelBinders.Binders.GetBinder(typeof(TModel));
var bindingContext = new ModelBindingContext()
{
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(
() => model, typeof(TModel)),
ModelState = modelState,
ValueProvider = valueProvider
};
binder.BindModel(controllerContext, bindingContext);
return modelState;
}
This can then be easily used in a unit test like this:
// Arrange
var viewModel = new AddressViewModel();
var addressValues = new FormCollection
{
{"CustomerName", "Richard"}
};
// Act
var modelState = TryUpdateModel(viewModel, addressValues);
// Assert
Assert.False(modelState.IsValid);
I had an issue where TestsHelper worked most of the time but not for validation methods defined by the IValidatableObject interface. The CompareAttribute also gave me some problems. That is why the try/catch is in there. The following code seems to validate all cases:
public static void ValidateUsingReflection<T>(T obj, Controller controller)
{
ValidationContext validationContext = new ValidationContext(obj, null, null);
Type type = typeof(T);
MetadataTypeAttribute meta = type.GetCustomAttributes(false).OfType<MetadataTypeAttribute>().FirstOrDefault();
if (meta != null)
{
type = meta.MetadataClassType;
}
PropertyInfo[] propertyInfo = type.GetProperties();
foreach (PropertyInfo info in propertyInfo)
{
IEnumerable<ValidationAttribute> attributes = info.GetCustomAttributes(false).OfType<ValidationAttribute>();
foreach (ValidationAttribute attribute in attributes)
{
PropertyInfo objPropInfo = obj.GetType().GetProperty(info.Name);
try
{
validationContext.DisplayName = info.Name;
attribute.Validate(objPropInfo.GetValue(obj, null), validationContext);
}
catch (Exception ex)
{
controller.ModelState.AddModelError(info.Name, ex.Message);
}
}
}
IValidatableObject valObj = obj as IValidatableObject;
if (null != valObj)
{
IEnumerable<ValidationResult> results = valObj.Validate(validationContext);
foreach (ValidationResult result in results)
{
string key = result.MemberNames.FirstOrDefault() ?? string.Empty;
controller.ModelState.AddModelError(key, result.ErrorMessage);
}
}
}

Resources